Commit 2cef84bdc836de9041b5c68dcba0f0e59f666277
1 parent
15245310
* 【修复】自定义 DefaultStreamMessageListenerContainerX 实现,解决 Redisson Stream 读取不到数据返回 `null` 导致 NPE 问题
Showing
3 changed files
with
67 additions
and
2 deletions
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
| ... | ... | @@ -18,6 +18,7 @@ import org.springframework.data.redis.core.RedisTemplate; |
| 18 | 18 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 19 | 19 | import org.springframework.data.redis.listener.ChannelTopic; |
| 20 | 20 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 21 | +import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX; | |
| 21 | 22 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 22 | 23 | |
| 23 | 24 | import java.util.List; |
| ... | ... | @@ -81,11 +82,12 @@ public class YudaoMQAutoConfiguration { |
| 81 | 82 | .build(); |
| 82 | 83 | // 创建 container 对象 |
| 83 | 84 | StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = |
| 84 | - StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); | |
| 85 | +// StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); | |
| 86 | + DefaultStreamMessageListenerContainerX.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); | |
| 85 | 87 | |
| 86 | 88 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 87 | 89 | String consumerName = buildConsumerName(); |
| 88 | - listeners.forEach(listener -> { | |
| 90 | + listeners.parallelStream().forEach(listener -> { | |
| 89 | 91 | // 创建 listener 对应的消费者分组 |
| 90 | 92 | try { |
| 91 | 93 | redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainerX.java
0 → 100644
| 1 | +package org.springframework.data.redis.stream; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 4 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 5 | +import org.springframework.data.redis.connection.stream.ByteRecord; | |
| 6 | +import org.springframework.data.redis.connection.stream.ReadOffset; | |
| 7 | +import org.springframework.data.redis.connection.stream.Record; | |
| 8 | +import org.springframework.util.Assert; | |
| 9 | + | |
| 10 | +import java.util.Collections; | |
| 11 | +import java.util.List; | |
| 12 | +import java.util.function.Function; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 拓展 DefaultStreamMessageListenerContainer 实现,解决 Spring Data Redis + Redisson 结合使用时,Redisson 在 Stream 获得不到数据时,返回 null 而不是空 List,导致 NPE 异常。 | |
| 16 | + * 对应 issue:https://github.com/spring-projects/spring-data-redis/issues/2147 和 https://github.com/redisson/redisson/issues/4006 | |
| 17 | + * 目前看下来 Spring Data Redis 不肯加 null 判断,Redisson 暂时也没改返回 null 到空 List 的打算,所以暂时只能自己改,哽咽! | |
| 18 | + * | |
| 19 | + * @author 芋道源码 | |
| 20 | + */ | |
| 21 | +public class DefaultStreamMessageListenerContainerX<K, V extends Record<K, ?>> extends DefaultStreamMessageListenerContainer<K, V> { | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 参考 {@link StreamMessageListenerContainer#create(RedisConnectionFactory, StreamMessageListenerContainerOptions)} 的实现 | |
| 25 | + */ | |
| 26 | + public static <K, V extends Record<K, ?>> StreamMessageListenerContainer<K, V> create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions<K, V> options) { | |
| 27 | + Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null!"); | |
| 28 | + Assert.notNull(options, "StreamMessageListenerContainerOptions must not be null!"); | |
| 29 | + return new DefaultStreamMessageListenerContainerX<>(connectionFactory, options); | |
| 30 | + } | |
| 31 | + | |
| 32 | + public DefaultStreamMessageListenerContainerX(RedisConnectionFactory connectionFactory, StreamMessageListenerContainerOptions<K, V> containerOptions) { | |
| 33 | + super(connectionFactory, containerOptions); | |
| 34 | + } | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 参考 {@link DefaultStreamMessageListenerContainer#register(StreamReadRequest, StreamListener)} 的实现 | |
| 38 | + */ | |
| 39 | + @Override | |
| 40 | + public Subscription register(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | |
| 41 | + return this.doRegisterX(getReadTaskX(streamRequest, listener)); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @SuppressWarnings("unchecked") | |
| 45 | + private StreamPollTask<K, V> getReadTaskX(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | |
| 46 | + StreamPollTask<K, V> task = ReflectUtil.invoke(this, "getReadTask", streamRequest, listener); | |
| 47 | + // 修改 readFunction 方法 | |
| 48 | + Function<ReadOffset, List<ByteRecord>> readFunction = (Function<ReadOffset, List<ByteRecord>>) ReflectUtil.getFieldValue(task, "readFunction"); | |
| 49 | + ReflectUtil.setFieldValue(task, "readFunction", (Function<ReadOffset, List<ByteRecord>>) readOffset -> { | |
| 50 | + List<ByteRecord> records = readFunction.apply(readOffset); | |
| 51 | + //【重点】保证 records 不是空,避免 NPE 的问题!!! | |
| 52 | + return records != null ? records : Collections.emptyList(); | |
| 53 | + }); | |
| 54 | + return task; | |
| 55 | + } | |
| 56 | + | |
| 57 | + private Subscription doRegisterX(Task task) { | |
| 58 | + return ReflectUtil.invoke(this, "doRegister", task); | |
| 59 | + } | |
| 60 | + | |
| 61 | +} | |
| 62 | + | ... | ... |
更新日志.md
| ... | ... | @@ -37,6 +37,7 @@ TODO |
| 37 | 37 | * 【修复】Knife4j 接口文档 404 的问题,原因是 `spring.mvc.static-path-pattern` 配置项,影响了基础路径 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/f966fae0606178ec5ffa47dd487f7984867da557) |
| 38 | 38 | * 【修复】修复文件访问地址错误 [#68](https://github.com/YunaiV/ruoyi-vue-pro/pull/68) |
| 39 | 39 | * 【修复】工作流程发起以及审批异常,由 `@NotEmpty` 校验、和 Long 类型异常导致 [#73](https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/73) |
| 40 | +* 【修复】自定义 DefaultStreamMessageListenerContainerX 实现,解决 Redisson Stream 读取不到数据返回 `null` 导致 NPE 问题 [commit]() | |
| 40 | 41 | |
| 41 | 42 | ### 🔨 Dependency Upgrades |
| 42 | 43 | ... | ... |