Commit a6c92816f0fb5801c27f6dc74cc0ac9542622b86
1 parent
0f31ce7e
fix: 解决 redis mq 消息丢失问题
Showing
1 changed file
with
20 additions
and
1 deletions
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/scheduler/PendingMessageScheduler.java
| @@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.mq.scheduler; | @@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.mq.scheduler; | ||
| 3 | import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | 3 | import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; |
| 4 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | 4 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; |
| 5 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 6 | +import org.redisson.api.RLock; | ||
| 7 | +import org.redisson.api.RedissonClient; | ||
| 6 | import org.springframework.beans.factory.annotation.Autowired; | 8 | import org.springframework.beans.factory.annotation.Autowired; |
| 7 | import org.springframework.beans.factory.annotation.Value; | 9 | import org.springframework.beans.factory.annotation.Value; |
| 8 | import org.springframework.data.redis.connection.stream.Consumer; | 10 | import org.springframework.data.redis.connection.stream.Consumer; |
| @@ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.Scheduled; | @@ -17,6 +19,7 @@ import org.springframework.scheduling.annotation.Scheduled; | ||
| 17 | 19 | ||
| 18 | import java.util.List; | 20 | import java.util.List; |
| 19 | import java.util.Map; | 21 | import java.util.Map; |
| 22 | +import java.util.concurrent.TimeUnit; | ||
| 20 | 23 | ||
| 21 | /** | 24 | /** |
| 22 | * 这个定时器用于处理,crash 之后的消费者未消费完的消息 | 25 | * 这个定时器用于处理,crash 之后的消费者未消费完的消息 |
| @@ -24,19 +27,35 @@ import java.util.Map; | @@ -24,19 +27,35 @@ import java.util.Map; | ||
| 24 | @Slf4j | 27 | @Slf4j |
| 25 | @EnableScheduling | 28 | @EnableScheduling |
| 26 | public class PendingMessageScheduler { | 29 | public class PendingMessageScheduler { |
| 27 | - | 30 | + private static final String LOCK_KEY = "redis:pending:msg:lock"; |
| 28 | @Autowired | 31 | @Autowired |
| 29 | private List<AbstractStreamMessageListener<?>> listeners; | 32 | private List<AbstractStreamMessageListener<?>> listeners; |
| 30 | @Autowired | 33 | @Autowired |
| 31 | private RedisMQTemplate redisTemplate; | 34 | private RedisMQTemplate redisTemplate; |
| 32 | @Value("${spring.application.name}") | 35 | @Value("${spring.application.name}") |
| 33 | private String groupName; | 36 | private String groupName; |
| 37 | + @Autowired | ||
| 38 | + private RedissonClient redissonClient; | ||
| 34 | 39 | ||
| 35 | /** | 40 | /** |
| 36 | * 一分钟执行一次 | 41 | * 一分钟执行一次 |
| 37 | */ | 42 | */ |
| 38 | @Scheduled(fixedRate = 60 * 1000) | 43 | @Scheduled(fixedRate = 60 * 1000) |
| 39 | public void processPendingMessage() { | 44 | public void processPendingMessage() { |
| 45 | + final RLock lock = redissonClient.getLock(LOCK_KEY); | ||
| 46 | + try { | ||
| 47 | + // 尝试加锁,最多等待 30 秒,上锁以后 60 秒自动解锁 | ||
| 48 | + boolean lockFlag = lock.tryLock(30, 60, TimeUnit.SECONDS); | ||
| 49 | + if (lockFlag) { | ||
| 50 | + execute(); | ||
| 51 | + } | ||
| 52 | + } catch (InterruptedException e) { | ||
| 53 | + log.error("获取锁失败", e); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + private void execute() { | ||
| 40 | StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream(); | 59 | StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream(); |
| 41 | 60 | ||
| 42 | for (AbstractStreamMessageListener<?> listener : listeners) { | 61 | for (AbstractStreamMessageListener<?> listener : listeners) { |