Commit 604266d33c6b29a829282294b02d076e8fbe1b17

Authored by gaibu
1 parent 0e6b4e46

fix: 解决 redis mq 消息丢失问题

yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.mq.job;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
5 5 import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
  6 +import lombok.AllArgsConstructor;
6 7 import lombok.extern.slf4j.Slf4j;
7 8 import org.redisson.api.RLock;
8 9 import org.redisson.api.RedissonClient;
... ... @@ -22,7 +23,9 @@ import java.util.Map;
22 23 * 这个任务用于处理,crash 之后的消费者未消费完的消息
23 24 */
24 25 @Slf4j
  26 +@AllArgsConstructor
25 27 public class RedisPendingMessageResendJob {
  28 +
26 29 private static final String LOCK_KEY = "redis:pending:msg:lock";
27 30  
28 31 private final List<AbstractStreamMessageListener<?>> listeners;
... ... @@ -30,13 +33,6 @@ public class RedisPendingMessageResendJob {
30 33 private final String groupName;
31 34 private final RedissonClient redissonClient;
32 35  
33   - public RedisPendingMessageResendJob(List<AbstractStreamMessageListener<?>> listeners, RedisMQTemplate redisTemplate, String groupName, RedissonClient redissonClient) {
34   - this.listeners = listeners;
35   - this.redisTemplate = redisTemplate;
36   - this.groupName = groupName;
37   - this.redissonClient = redissonClient;
38   - }
39   -
40 36 /**
41 37 * 一分钟执行一次,这里选择每分钟的35秒执行,是为了避免整点任务过多的问题
42 38 */
... ... @@ -57,28 +53,26 @@ public class RedisPendingMessageResendJob {
57 53  
58 54 private void execute() {
59 55 StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();
60   -
61 56 listeners.forEach(listener -> {
62 57 PendingMessagesSummary pendingMessagesSummary = ops.pending(listener.getStreamKey(), groupName);
63 58 // 每个消费者的 pending 队列消息数量
64 59 Map<String, Long> pendingMessagesPerConsumer = pendingMessagesSummary.getPendingMessagesPerConsumer();
65   - pendingMessagesPerConsumer.entrySet().forEach(entry -> {
66   - String consumerName = entry.getKey();
67   - Long pendingMessageCount = entry.getValue();
  60 + pendingMessagesPerConsumer.forEach((consumerName, pendingMessageCount) -> {
68 61 log.info("[processPendingMessage][消费者({}) 消息数量({})]", consumerName, pendingMessageCount);
69 62  
70 63 // 从消费者的 pending 队列中读取消息
71 64 List<MapRecord<String, Object, Object>> records = ops.read(Consumer.from(groupName, consumerName), StreamOffset.create(listener.getStreamKey(), ReadOffset.from("0")));
72   - if (CollUtil.isNotEmpty(records)) {
73   - for (MapRecord<String, Object, Object> record : records) {
74   - // 重新投递消息
75   - redisTemplate.getRedisTemplate().opsForStream().add(StreamRecords.newRecord()
76   - .ofObject(record.getValue()) // 设置内容
77   - .withStreamKey(listener.getStreamKey()));
  65 + if (CollUtil.isEmpty(records)) {
  66 + return;
  67 + }
  68 + for (MapRecord<String, Object, Object> record : records) {
  69 + // 重新投递消息
  70 + redisTemplate.getRedisTemplate().opsForStream().add(StreamRecords.newRecord()
  71 + .ofObject(record.getValue()) // 设置内容
  72 + .withStreamKey(listener.getStreamKey()));
78 73  
79   - // ack 消息消费完成
80   - redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, record);
81   - }
  74 + // ack 消息消费完成
  75 + redisTemplate.getRedisTemplate().opsForStream().acknowledge(groupName, record);
82 76 }
83 77 });
84 78 });
... ...