redis消息发布订阅

1.redis自带的发布订阅模式

缺点:

首先这些消息并没有持久化机制,属于即发即弃模式。也就是说它们不能像ActiveMQ中的消息那样保证持久化消息订阅者不会错过任何消息,无论这些消息订阅者是否随时在线。最重要的是在应用开发中如何保证双发都在线的长连接状态?因为Redis的监听其实是打开了一个长连接操作的。任何网络波动都会断开的。

也是由于即发即弃的消息模式,所以Redis也没有必要专门对使用订阅/发布功能的客户端连接进行识别,用来明确该客户端连接的ID是否在之前已经连接过Redis服务了。ActiveMQ中保持持续通知的功能的前提,就是能够识别客户端连接ID的历史连接情况,以便确定哪些订阅消息这个客户端还没有处理。

分布式消息系统中,如何避免消息重复?

造成消息重复的根本原因是:网络不可靠。只要通过网络交换数据,就无法避免这个问题。所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

a. 消费端处理消息的业务逻辑保持幂等性;

b. 保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现。

通过幂等性,不管来多少条重复消息,可以实现处理的结果都一样。再利用一张日志表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么就可以不再处理这条消息,避免消息的重复处理。

/**
*发布
*/
@Service("publishToRedisService")
public class PublishToRedisServiceImpl implements PublishToRedisService {

   private static final Logger LOGGER = LoggerFactory.getLogger(PublishToRedisServiceImpl.class);

   @Autowired
   private RedisTemplate<Serializable, Serializable> redisTemplate;

   @Override
   public void publish(String topic, JSONObject message) {
       LOGGER.info("PublishToRedisServiceImpl.publish topic={}, message={}",topic,JSONObject.toJSONString(message));
       RedisTemplate<Serializable, Serializable> redisTemplate = this.redisTemplate;
       String json = JSONObject.toJSONString(message);
       try{
           redisTemplate.convertAndSend(topic, json);

           redisTemplate.
       }catch (Exception e){
           LOGGER.error("redisTemplate.convertAndSend error",e);
       }

   }
}
/**
*接收
*/
public class SubscribeService implements MessageListener {

  private static final Logger logger = LoggerFactory.getLogger(SubscribeService.class);
  @Autowired
  private RedisTemplate<String, String> redisTemplate;


  @Override
  public void onMessage(final Message message, final byte[] pattern ) {
     logger.info("=====================>>>>>>>>>>>>>>>>>>>>>>");
     logger.info("Message received: " + message.toString());
  }
}

2.队列的方法

用redis的list数据结构 实现先进先出队列

//如果队列为空,阻塞等待数据
List<String> list = jedis.blpop(0,topic);

启动队列

/**
 * 程序启动后执行该方法
 * 
 */
@Component
public class PollQueueInit   implements CommandLineRunner {

	@Autowired
	private PollQueueService pollQueueService;

	@Override
	public void run(String... args) {
			new Thread(()->
					pollQueueService.pollQueue()
			).start();
	}
}
Spring Cloud中遇到Redis消息发布/订阅模式下的重复消费问题,通常是因为消费者应用重启、网络分区或者其他并发场景下导致的消息持久化和重新处理。为了解决这个问题,可以采取以下几个策略: 1. **消息ID确认**:每次接收到消息后,消费者需要检查消息的唯一标识(如UUID),如果发现已经消费过的消息再次到达,则忽略它。 ```java @Autowired private RedisTemplate<String, String> redisTemplate; String messageId = ...; // 消息的唯一ID ListenableFuture<String> result = redisTemplate.opsForValue().listen(consumer -> { if (consumer.receive(messageId)) { // 如果消息ID已存在,则忽略 return Mono.error(new DuplicateMessageException("Message already processed")); } // 正常处理消息... }); ``` 2. **消息过期时间**:设置Redis消息的TTL(Time To Live)为一个合理的值,当消息超时未被消费则自动删除,避免无限循环消费。 ```java redisTemplate.opsForValue().set(messageKey, message, 60, TimeUnit.SECONDS); // 设置1分钟过期时间 ``` 3. **集群模式**:如果使用的是Redis Cluster,确保消息只在一个实例上发布,并配置客户端连接池以保持一致性,减少由于节点故障导致的数据复制引起的重复消费。 4. **幂等操作**:对于一些幂等的操作,即使多次处理也无妨,确保业务逻辑能够处理这种情况。 5. **监控与日志**:对消费者的健康状态进行监控,如果有异常情况记录日志,以便排查问题并优化。 **相关问题--:** 1. Spring Cloud中如何配置Redis Cluster以防止消息重复消费? 2. 如何通过Redis事务机制避免重复消费? 3. 使用Spring Cloud Stream时,如何保证消费者组的一致性?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值