SpringBoot2.0高级案例(07) :整合Redis集群 ,实现消息队列场景

1本文源码 2GitHub地址:知了一笑 3https://github.com/cicadasmile/middle-ware-parent

一、Redis集群简介

1、RedisCluster概念

Redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求,当一个服务宕机可以快速的切换到另外一个服务。redis cluster主要是针对海量数据+高并发+高可用的场景。

二、与SpringBoot2.0整合

1、核心依赖

1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-redis</artifactId> 4 <version>${spring-boot.version}</version> 5</dependency> 6<dependency> 7 <groupId>redis.clients</groupId> 8 <artifactId>jedis</artifactId> 9 <version>${redis-client.version}</version> 10</dependency>

2、核心配置

1spring: 2 # Redis 集群 3 redis: 4 sentinel: 5 # sentinel 配置 6 master: mymaster 7 nodes: 192.168.0.127:26379 8 maxTotal: 60 9 minIdle: 10 10 maxWaitMillis: 10000 11 testWhileIdle: true 12 testOnBorrow: true 13 testOnReturn: false 14 timeBetweenEvictionRunsMillis: 10000

3、参数渲染类

1@ConfigurationProperties(prefix = "spring.redis.sentinel") 2public class RedisParam { 3 private String nodes ; 4 private String master ; 5 private Integer maxTotal ; 6 private Integer minIdle ; 7 private Integer maxWaitMillis ; 8 private Integer timeBetweenEvictionRunsMillis ; 9 private boolean testWhileIdle ; 10 private boolean testOnBorrow ; 11 private boolean testOnReturn ; 12 // 省略GET和SET方法 13}

4、集群配置文件

1@Configuration 2@EnableConfigurationProperties(RedisParam.class) 3public class RedisPool { 4 @Resource 5 private RedisParam redisParam ; 6 @Bean("jedisSentinelPool") 7 public JedisSentinelPool getRedisPool (){ 8 Set<String> sentinels = new HashSet<>(); 9 sentinels.addAll(Arrays.asList(redisParam.getNodes().split(","))); 10 GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 11 poolConfig.setMaxTotal(redisParam.getMaxTotal()); 12 poolConfig.setMinIdle(redisParam.getMinIdle()); 13 poolConfig.setMaxWaitMillis(redisParam.getMaxWaitMillis()); 14 poolConfig.setTestWhileIdle(redisParam.isTestWhileIdle()); 15 poolConfig.setTestOnBorrow(redisParam.isTestOnBorrow()); 16 poolConfig.setTestOnReturn(redisParam.isTestOnReturn()); 17 poolConfig.setTimeBetweenEvictionRunsMillis(redisParam.getTimeBetweenEvictionRunsMillis()); 18 JedisSentinelPool redisPool = new JedisSentinelPool(redisParam.getMaster(), sentinels, poolConfig); 19 return redisPool; 20 } 21 @Bean 22 SpringUtil springUtil() { 23 return new SpringUtil(); 24 } 25 @Bean 26 RedisListener redisListener() { 27 return new RedisListener(); 28 } 29}

5、配置Redis模板类

1@Configuration 2public class RedisConfig { 3 @Bean 4 public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { 5 StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(); 6 stringRedisTemplate.setConnectionFactory(factory); 7 return stringRedisTemplate; 8 } 9}

三、模拟队列场景案例

生产者消费者模式:客户端监听消息队列,消息达到,消费者马上消费,如果消息队列里面没有消息,那么消费者就继续监听。基于Redis的LPUSH(BLPUSH)把消息入队,用 RPOP(BRPOP)获取消息的模式。

1、加锁解锁工具

1@Component 2public class RedisLock { 3 private static String keyPrefix = "RedisLock:"; 4 @Resource 5 private JedisSentinelPool jedisSentinelPool; 6 public boolean addLock(String key, long expire) { 7 Jedis jedis = null; 8 try { 9 jedis = jedisSentinelPool.getResource(); 10 /* 11 * nxxx的值只能取NX或者XX,如果取NX,则只有当key不存在是才进行set,如果取XX,则只有当key已经存在时才进行set 12 * expx的值只能取EX或者PX,代表数据过期时间的单位,EX代表秒,PX代表毫秒。 13 */ 14 String value = jedis.set(keyPrefix + key, "1", "nx", "ex", expire); 15 return value != null; 16 } catch (Exception e){ 17 e.printStackTrace(); 18 }finally { 19 if (jedis != null) jedis.close(); 20 } 21 return false; 22 } 23 public void removeLock(String key) { 24 Jedis jedis = null; 25 try { 26 jedis = jedisSentinelPool.getResource(); 27 jedis.del(keyPrefix + key); 28 } finally { 29 if (jedis != null) jedis.close(); 30 } 31 } 32}

2、消息消费

1)封装接口

1public interface RedisHandler { 2 /** 3 * 队列名称 4 */ 5 String queueName(); 6 7 /** 8 * 队列消息内容 9 */ 10 String consume (String msgBody); 11}

2)接口实现

1@Component 2public class LogAListen implements RedisHandler { 3 private static final Logger LOG = LoggerFactory.getLogger(LogAListen.class) ; 4 @Resource 5 private RedisLock redisLock; 6 @Override 7 public String queueName() { 8 return "LogA-key"; 9 } 10 @Override 11 public String consume(String msgBody) { 12 // 加锁,防止消息重复投递 13 String lockKey = "lock-order-uuid-A"; 14 boolean lock = false; 15 try { 16 lock = redisLock.addLock(lockKey, 60); 17 if (!lock) { 18 return "success"; 19 } 20 LOG.info("LogA-key == >>" + msgBody); 21 } catch (Exception e){ 22 e.printStackTrace(); 23 } finally { 24 if (lock) { 25 redisLock.removeLock(lockKey); 26 } 27 } 28 return "success"; 29 } 30}

3、消息监听器

1public class RedisListener implements InitializingBean { 2 /** 3 * Redis 集群 4 */ 5 @Resource 6 private JedisSentinelPool jedisSentinelPool; 7 private List<RedisHandler> handlers = null; 8 private ExecutorService product = null; 9 private ExecutorService consumer = null; 10 /** 11 * 初始化配置 12 */ 13 @Override 14 public void afterPropertiesSet() { 15 handlers = SpringUtil.getBeans(RedisHandler.class) ; 16 product = new ThreadPoolExecutor(10,15,60 * 3, 17 TimeUnit.SECONDS,new SynchronousQueue<>()); 18 consumer = new ThreadPoolExecutor(10,15,60 * 3, 19 TimeUnit.SECONDS,new SynchronousQueue<>()); 20 for (RedisHandler redisHandler : handlers){ 21 product.execute(() -> { 22 redisTask(redisHandler); 23 }); 24 } 25 } 26 /** 27 * 队列监听 28 */ 29 public void redisTask (RedisHandler redisHandler){ 30 Jedis jedis = null ; 31 while (true){ 32 try { 33 jedis = jedisSentinelPool.getResource() ; 34 List<String> msgBodyList = jedis.brpop(0, redisHandler.queueName()); 35 if (msgBodyList != null && msgBodyList.size()>0){ 36 consumer.execute(() -> { 37 redisHandler.consume(msgBodyList.get(1)) ; 38 }); 39 } 40 } catch (Exception e){ 41 e.printStackTrace(); 42 } finally { 43 if (jedis != null) jedis.close(); 44 } 45 } 46 } 47}

4、消息生产者

1@Service 2public class RedisServiceImpl implements RedisService { 3 @Resource 4 private JedisSentinelPool jedisSentinelPool; 5 @Override 6 public void saveQueue(String queueKey, String msgBody) { 7 Jedis jedis = null; 8 try { 9 jedis = jedisSentinelPool.getResource(); 10 jedis.lpush(queueKey,msgBody) ; 11 } catch (Exception e){ 12 e.printStackTrace(); 13 } finally { 14 if (jedis != null) jedis.close(); 15 } 16 } 17}

5、场景测试接口

1@RestController 2public class RedisController { 3 @Resource 4 private RedisService redisService ; 5 /** 6 * 队列推消息 7 */ 8 @RequestMapping("/saveQueue") 9 public String saveQueue (){ 10 MsgBody msgBody = new MsgBody() ; 11 msgBody.setName("LogAModel"); 12 msgBody.setDesc("描述"); 13 msgBody.setCreateTime(new Date()); 14 redisService.saveQueue("LogA-key", JSONObject.toJSONString(msgBody)); 15 return "success" ; 16 } 17}

四、源代码地址

1GitHub地址:知了一笑 2https://github.com/cicadasmile/middle-ware-parent 3码云地址:知了一笑 4https://gitee.com/cicadasmile/middle-ware-parent

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid