推荐一款基于Redis的高可用延迟队列

  • 2020 年 2 月 18 日
  • 筆記

前言

之前给大家推荐的几款延迟任务处理队列,对于一些要求比较高的场景是无法运用于生产环境的,一旦机器宕机或者应用重启会导致队列消息丢失,从而造成无法挽回的损失。今天给大家分享一款高可用延迟队列 Redisson

简介

Redisson 在基于 NIONetty 框架上,充分利用了 Redis 键值数据库提供的一系列优势,在 Java 实用工具包中常用接口的基础上,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

代码案例

框架支持多种 redis 集成部署方式,包括单节点,主从模式,集群模式,哨兵模式等等。

项目 pom.xml 引入:

<dependency>      <groupId>org.redisson</groupId>      <artifactId>redisson</artifactId>      <version>3.12.1</version>  </dependency>

定义红包信息实体类,必须实现序列并定义空构造方法:

/**   * 红包信息   */  public class RedPacketMessage implements Serializable {        /**       * 红包 ID       */      private  long redPacketId;        /**       * 创建时间戳       */      private  long timestamp;        public RedPacketMessage() {        }        public RedPacketMessage(long redPacketId) {          this.redPacketId = redPacketId;          this.timestamp = System.currentTimeMillis();      }        public long getRedPacketId() {          return redPacketId;      }        public long getTimestamp() {          return timestamp;      }    }

代码案例:

/**   * 红包过期失效 高可用延迟队列   */  public class RedPacketDelayQueue {        private static final Logger LOGGER = LoggerFactory.getLogger(RedPacketDelayQueue.class);        public static void main(String[] args) throws Exception {          Config config = new Config();          config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456").setDatabase(2);          RedissonClient redissonClient = Redisson.create(config);          /**           * 红包目标队列           */          RBlockingQueue<RedPacketMessage> blockingRedPacketQueue                  = redissonClient.getBlockingQueue("redPacketDelayQueue");          /**           * 定时任务将到期的元素转移到目标队列           */          RDelayedQueue<RedPacketMessage> delayedRedPacketQueue                  = redissonClient.getDelayedQueue(blockingRedPacketQueue);            /**           * 延时信息入队列           */          delayedRedPacketQueue.offer(new RedPacketMessage(20200113), 3, TimeUnit.SECONDS);          delayedRedPacketQueue.offer(new RedPacketMessage(20200114), 5, TimeUnit.SECONDS);          delayedRedPacketQueue.offer(new RedPacketMessage(20200115), 10, TimeUnit.SECONDS);          while (true){              /**               * 取出失效红包               */              RedPacketMessage redPacket = blockingRedPacketQueue.take();              LOGGER.info("红包ID:{}过期失效",redPacket.getRedPacketId());              /**               * 处理相关业务逻辑:记录相关信息并退还剩余红包金额               */          }      }  }

高可用

主从部署方式:

/**   * 主从部署方式   */  Config config = new Config();  config.useMasterSlaveServers()          //设置redis主节点          .setMasterAddress("redis://192.168.1.120:6379")          //设置redis从节点          .addSlaveAddress("redis://192.168.1.130:6379", "redis://192.168.1.140:6379");  RedissonClient redisson = Redisson.create(config);

集群部署方式:

/**   * 集群部署方式   * cluster方式至少6个节点   * 3主3从,3主做sharding,3从用来保证主宕机后可以高可用   */  Config config = new Config();  config.useClusterServers()          .setScanInterval(2000)//集群状态扫描间隔时间,单位是毫秒          .addNodeAddress("redis://192.168.1.120:6379")          .addNodeAddress("redis://192.168.1.130:6379")          .addNodeAddress("redis://192.168.1.140:6379")          .addNodeAddress("redis://192.168.1.150:6379")          .addNodeAddress("redis://192.168.1.160:6379")          .addNodeAddress("redis://192.168.1.170:6379");  RedissonClient redissonClient = Redisson.create(config);

哨兵部署方式:

/**   * 哨兵部署方式   * sentinel是采用 Paxos拜占庭协议,一般sentinel至少3个节点   */  Config config = new Config();  config.useSentinelServers()          .setMasterName("my-sentinel-name")          .addSentinelAddress("redis://192.168.1.120:6379")          .addSentinelAddress("redis://192.168.1.130:6379")          .addSentinelAddress("redis://192.168.1.140:6379");  RedissonClient redisson = Redisson.create(config);

云托管部署方式:

/**   * 云托管部署方式   * 这种方式主要解决redis提供商为云服务的提供商的redis连接   * 比如亚马逊云、微软云   */  config.useReplicatedServers()          //主节点变化扫描间隔时间          .setScanInterval(2000)          .addNodeAddress("redis://192.168.1.120:6379")          .addNodeAddress("redis://192.168.1.130:6379")          .addNodeAddress("redis://192.168.1.140:6379");  RedissonClient redisson = Redisson.create(config);

小结

无论是JDK内置的延迟队列还是基于时间轮算法的队列,都无法保证生产系统的高可用性,而Redisson很好的解决了这个问题。

源码

https://gitee.com/52itstyle/spring-boot-seckill