Commit a2315826370546744354f3505d0f376d35dc0a34
1 parent
d29b0beb
增强 mq starter 组件,支持 interceptor 拦截器机制
Showing
6 changed files
with
161 additions
and
26 deletions
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.mq.config; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.mq.config; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.system.SystemUtil; | 3 | import cn.hutool.system.SystemUtil; |
| 4 | import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | 4 | import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; |
| 5 | +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 5 | import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; | 6 | import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; |
| 6 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | 7 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; |
| 7 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; | 8 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
| @@ -9,7 +10,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -9,7 +10,6 @@ import lombok.extern.slf4j.Slf4j; | ||
| 9 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; | 10 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
| 10 | import org.springframework.context.annotation.Bean; | 11 | import org.springframework.context.annotation.Bean; |
| 11 | import org.springframework.context.annotation.Configuration; | 12 | import org.springframework.context.annotation.Configuration; |
| 12 | -import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| 13 | import org.springframework.data.redis.connection.stream.Consumer; | 13 | import org.springframework.data.redis.connection.stream.Consumer; |
| 14 | import org.springframework.data.redis.connection.stream.ObjectRecord; | 14 | import org.springframework.data.redis.connection.stream.ObjectRecord; |
| 15 | import org.springframework.data.redis.connection.stream.ReadOffset; | 15 | import org.springframework.data.redis.connection.stream.ReadOffset; |
| @@ -33,8 +33,12 @@ import java.util.List; | @@ -33,8 +33,12 @@ import java.util.List; | ||
| 33 | public class YudaoMQAutoConfiguration { | 33 | public class YudaoMQAutoConfiguration { |
| 34 | 34 | ||
| 35 | @Bean | 35 | @Bean |
| 36 | - public RedisMQTemplate redisMQTemplate(StringRedisTemplate stringRedisTemplate) { | ||
| 37 | - return new RedisMQTemplate(stringRedisTemplate); | 36 | + public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, |
| 37 | + List<RedisMessageInterceptor> interceptors) { | ||
| 38 | + RedisMQTemplate redisMQTemplate = new RedisMQTemplate(redisTemplate); | ||
| 39 | + // 添加拦截器 | ||
| 40 | + interceptors.forEach(redisMQTemplate::addInterceptor); | ||
| 41 | + return redisMQTemplate; | ||
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | // ========== 消费者相关 ========== | 44 | // ========== 消费者相关 ========== |
| @@ -44,13 +48,14 @@ public class YudaoMQAutoConfiguration { | @@ -44,13 +48,14 @@ public class YudaoMQAutoConfiguration { | ||
| 44 | */ | 48 | */ |
| 45 | @Bean | 49 | @Bean |
| 46 | public RedisMessageListenerContainer redisMessageListenerContainer( | 50 | public RedisMessageListenerContainer redisMessageListenerContainer( |
| 47 | - RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> listeners) { | 51 | + RedisMQTemplate redisMQTemplate, List<AbstractChannelMessageListener<?>> listeners) { |
| 48 | // 创建 RedisMessageListenerContainer 对象 | 52 | // 创建 RedisMessageListenerContainer 对象 |
| 49 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | 53 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 50 | // 设置 RedisConnection 工厂。 | 54 | // 设置 RedisConnection 工厂。 |
| 51 | - container.setConnectionFactory(factory); | 55 | + container.setConnectionFactory(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory()); |
| 52 | // 添加监听器 | 56 | // 添加监听器 |
| 53 | listeners.forEach(listener -> { | 57 | listeners.forEach(listener -> { |
| 58 | + listener.setRedisMQTemplate(redisMQTemplate); | ||
| 54 | container.addMessageListener(listener, new ChannelTopic(listener.getChannel())); | 59 | container.addMessageListener(listener, new ChannelTopic(listener.getChannel())); |
| 55 | log.info("[redisMessageListenerContainer][注册 Channel({}) 对应的监听器({})]", | 60 | log.info("[redisMessageListenerContainer][注册 Channel({}) 对应的监听器({})]", |
| 56 | listener.getChannel(), listener.getClass().getName()); | 61 | listener.getChannel(), listener.getClass().getName()); |
| @@ -65,7 +70,8 @@ public class YudaoMQAutoConfiguration { | @@ -65,7 +70,8 @@ public class YudaoMQAutoConfiguration { | ||
| 65 | */ | 70 | */ |
| 66 | @Bean(initMethod = "start", destroyMethod = "stop") | 71 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 67 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( | 72 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( |
| 68 | - RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) { | 73 | + RedisMQTemplate redisMQTemplate, List<AbstractStreamMessageListener<?>> listeners) { |
| 74 | + RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); | ||
| 69 | // 第一步,创建 StreamMessageListenerContainer 容器 | 75 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| 70 | // 创建 options 配置 | 76 | // 创建 options 配置 |
| 71 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = | 77 | StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions = |
| @@ -74,8 +80,8 @@ public class YudaoMQAutoConfiguration { | @@ -74,8 +80,8 @@ public class YudaoMQAutoConfiguration { | ||
| 74 | .targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化 | 80 | .targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化 |
| 75 | .build(); | 81 | .build(); |
| 76 | // 创建 container 对象 | 82 | // 创建 container 对象 |
| 77 | - StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = StreamMessageListenerContainer.create( | ||
| 78 | - redisTemplate.getRequiredConnectionFactory(), containerOptions); | 83 | + StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = |
| 84 | + StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); | ||
| 79 | 85 | ||
| 80 | // 第二步,注册监听器,消费对应的 Stream 主题 | 86 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 81 | String consumerName = buildConsumerName(); | 87 | String consumerName = buildConsumerName(); |
| @@ -85,7 +91,7 @@ public class YudaoMQAutoConfiguration { | @@ -85,7 +91,7 @@ public class YudaoMQAutoConfiguration { | ||
| 85 | redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); | 91 | redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); |
| 86 | } catch (Exception ignore) {} | 92 | } catch (Exception ignore) {} |
| 87 | // 设置 listener 对应的 redisTemplate | 93 | // 设置 listener 对应的 redisTemplate |
| 88 | - listener.setRedisTemplate(redisTemplate); | 94 | + listener.setRedisMQTemplate(redisMQTemplate); |
| 89 | // 创建 Consumer 对象 | 95 | // 创建 Consumer 对象 |
| 90 | Consumer consumer = Consumer.from(listener.getGroup(), consumerName); | 96 | Consumer consumer = Consumer.from(listener.getGroup(), consumerName); |
| 91 | // 设置 Consumer 消费进度,以最小消费进度为准 | 97 | // 设置 Consumer 消费进度,以最小消费进度为准 |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java
| 1 | package cn.iocoder.yudao.framework.mq.core; | 1 | package cn.iocoder.yudao.framework.mq.core; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 5 | +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 4 | import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; | 6 | import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; |
| 5 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | 7 | import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; |
| 6 | import lombok.AllArgsConstructor; | 8 | import lombok.AllArgsConstructor; |
| 9 | +import lombok.Getter; | ||
| 7 | import org.springframework.data.redis.connection.stream.RecordId; | 10 | import org.springframework.data.redis.connection.stream.RecordId; |
| 8 | import org.springframework.data.redis.connection.stream.StreamRecords; | 11 | import org.springframework.data.redis.connection.stream.StreamRecords; |
| 9 | import org.springframework.data.redis.core.RedisTemplate; | 12 | import org.springframework.data.redis.core.RedisTemplate; |
| 10 | 13 | ||
| 14 | +import java.util.ArrayList; | ||
| 15 | +import java.util.List; | ||
| 16 | + | ||
| 11 | /** | 17 | /** |
| 12 | * Redis MQ 操作模板类 | 18 | * Redis MQ 操作模板类 |
| 13 | * | 19 | * |
| @@ -16,7 +22,13 @@ import org.springframework.data.redis.core.RedisTemplate; | @@ -16,7 +22,13 @@ import org.springframework.data.redis.core.RedisTemplate; | ||
| 16 | @AllArgsConstructor | 22 | @AllArgsConstructor |
| 17 | public class RedisMQTemplate { | 23 | public class RedisMQTemplate { |
| 18 | 24 | ||
| 25 | + @Getter | ||
| 19 | private final RedisTemplate<String, ?> redisTemplate; | 26 | private final RedisTemplate<String, ?> redisTemplate; |
| 27 | + /** | ||
| 28 | + * 拦截器数组 | ||
| 29 | + */ | ||
| 30 | + @Getter | ||
| 31 | + private final List<RedisMessageInterceptor> interceptors = new ArrayList<>(); | ||
| 20 | 32 | ||
| 21 | /** | 33 | /** |
| 22 | * 发送 Redis 消息,基于 Redis pub/sub 实现 | 34 | * 发送 Redis 消息,基于 Redis pub/sub 实现 |
| @@ -24,7 +36,13 @@ public class RedisMQTemplate { | @@ -24,7 +36,13 @@ public class RedisMQTemplate { | ||
| 24 | * @param message 消息 | 36 | * @param message 消息 |
| 25 | */ | 37 | */ |
| 26 | public <T extends AbstractChannelMessage> void send(T message) { | 38 | public <T extends AbstractChannelMessage> void send(T message) { |
| 27 | - redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message)); | 39 | + try { |
| 40 | + sendMessageBefore(message); | ||
| 41 | + // 发送消息 | ||
| 42 | + redisTemplate.convertAndSend(message.getChannel(), JsonUtils.toJsonString(message)); | ||
| 43 | + } finally { | ||
| 44 | + sendMessageAfter(message); | ||
| 45 | + } | ||
| 28 | } | 46 | } |
| 29 | 47 | ||
| 30 | /** | 48 | /** |
| @@ -34,9 +52,36 @@ public class RedisMQTemplate { | @@ -34,9 +52,36 @@ public class RedisMQTemplate { | ||
| 34 | * @return 消息记录的编号对象 | 52 | * @return 消息记录的编号对象 |
| 35 | */ | 53 | */ |
| 36 | public <T extends AbstractStreamMessage> RecordId send(T message) { | 54 | public <T extends AbstractStreamMessage> RecordId send(T message) { |
| 37 | - return redisTemplate.opsForStream().add(StreamRecords.newRecord() | ||
| 38 | - .ofObject(JsonUtils.toJsonString(message)) // 设置内容 | ||
| 39 | - .withStreamKey(message.getStreamKey())); // 设置 stream key | 55 | + try { |
| 56 | + sendMessageBefore(message); | ||
| 57 | + // 发送消息 | ||
| 58 | + return redisTemplate.opsForStream().add(StreamRecords.newRecord() | ||
| 59 | + .ofObject(JsonUtils.toJsonString(message)) // 设置内容 | ||
| 60 | + .withStreamKey(message.getStreamKey())); // 设置 stream key | ||
| 61 | + } finally { | ||
| 62 | + sendMessageAfter(message); | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 添加拦截器 | ||
| 68 | + * | ||
| 69 | + * @param interceptor 拦截器 | ||
| 70 | + */ | ||
| 71 | + public void addInterceptor(RedisMessageInterceptor interceptor) { | ||
| 72 | + interceptors.add(interceptor); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + private void sendMessageBefore(AbstractRedisMessage message) { | ||
| 76 | + // 正序 | ||
| 77 | + interceptors.forEach(interceptor -> interceptor.sendMessageBefore(message)); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + private void sendMessageAfter(AbstractRedisMessage message) { | ||
| 81 | + // 倒序 | ||
| 82 | + for (int i = interceptors.size() - 1; i >= 0; i--) { | ||
| 83 | + interceptors.get(i).sendMessageAfter(message); | ||
| 84 | + } | ||
| 40 | } | 85 | } |
| 41 | 86 | ||
| 42 | } | 87 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mq.core.interceptor; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * {@link AbstractRedisMessage} 消息拦截器 | ||
| 7 | + * 通过拦截器,作为插件机制,实现拓展。 | ||
| 8 | + * 例如说,多租户场景下的 MQ 消息处理 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +public interface RedisMessageInterceptor { | ||
| 13 | + | ||
| 14 | + default void sendMessageBefore(AbstractRedisMessage message) { | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + default void sendMessageAfter(AbstractRedisMessage message) { | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + default void consumeMessageBefore(AbstractRedisMessage message) { | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + default void consumeMessageAfter(AbstractRedisMessage message) { | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | +} |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java
| @@ -2,11 +2,16 @@ package cn.iocoder.yudao.framework.mq.core.pubsub; | @@ -2,11 +2,16 @@ package cn.iocoder.yudao.framework.mq.core.pubsub; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 6 | +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 7 | +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 8 | +import lombok.Setter; | ||
| 5 | import lombok.SneakyThrows; | 9 | import lombok.SneakyThrows; |
| 6 | import org.springframework.data.redis.connection.Message; | 10 | import org.springframework.data.redis.connection.Message; |
| 7 | import org.springframework.data.redis.connection.MessageListener; | 11 | import org.springframework.data.redis.connection.MessageListener; |
| 8 | 12 | ||
| 9 | import java.lang.reflect.Type; | 13 | import java.lang.reflect.Type; |
| 14 | +import java.util.List; | ||
| 10 | 15 | ||
| 11 | /** | 16 | /** |
| 12 | * Redis Pub/Sub 监听器抽象类,用于实现广播消费 | 17 | * Redis Pub/Sub 监听器抽象类,用于实现广播消费 |
| @@ -25,6 +30,11 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | @@ -25,6 +30,11 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | ||
| 25 | * Redis Channel | 30 | * Redis Channel |
| 26 | */ | 31 | */ |
| 27 | private final String channel; | 32 | private final String channel; |
| 33 | + /** | ||
| 34 | + * RedisMQTemplate | ||
| 35 | + */ | ||
| 36 | + @Setter | ||
| 37 | + private RedisMQTemplate redisMQTemplate; | ||
| 28 | 38 | ||
| 29 | @SneakyThrows | 39 | @SneakyThrows |
| 30 | protected AbstractChannelMessageListener() { | 40 | protected AbstractChannelMessageListener() { |
| @@ -44,7 +54,13 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | @@ -44,7 +54,13 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | ||
| 44 | @Override | 54 | @Override |
| 45 | public final void onMessage(Message message, byte[] bytes) { | 55 | public final void onMessage(Message message, byte[] bytes) { |
| 46 | T messageObj = JsonUtils.parseObject(message.getBody(), messageType); | 56 | T messageObj = JsonUtils.parseObject(message.getBody(), messageType); |
| 47 | - this.onMessage(messageObj); | 57 | + try { |
| 58 | + consumeMessageBefore(messageObj); | ||
| 59 | + // 消费消息 | ||
| 60 | + this.onMessage(messageObj); | ||
| 61 | + } finally { | ||
| 62 | + consumeMessageAfter(messageObj); | ||
| 63 | + } | ||
| 48 | } | 64 | } |
| 49 | 65 | ||
| 50 | /** | 66 | /** |
| @@ -68,4 +84,20 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | @@ -68,4 +84,20 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | ||
| 68 | return (Class<T>) type; | 84 | return (Class<T>) type; |
| 69 | } | 85 | } |
| 70 | 86 | ||
| 87 | + private void consumeMessageBefore(AbstractRedisMessage message) { | ||
| 88 | + assert redisMQTemplate != null; | ||
| 89 | + List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); | ||
| 90 | + // 正序 | ||
| 91 | + interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message)); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + private void consumeMessageAfter(AbstractRedisMessage message) { | ||
| 95 | + assert redisMQTemplate != null; | ||
| 96 | + List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); | ||
| 97 | + // 倒序 | ||
| 98 | + for (int i = interceptors.size() - 1; i >= 0; i--) { | ||
| 99 | + interceptors.get(i).consumeMessageAfter(message); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + | ||
| 71 | } | 103 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java
| 1 | package cn.iocoder.yudao.framework.mq.core.stream; | 1 | package cn.iocoder.yudao.framework.mq.core.stream; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 3 | import com.fasterxml.jackson.annotation.JsonIgnore; | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 4 | 5 | ||
| 5 | /** | 6 | /** |
| @@ -7,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | @@ -7,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| 7 | * | 8 | * |
| 8 | * @author 芋道源码 | 9 | * @author 芋道源码 |
| 9 | */ | 10 | */ |
| 10 | -public abstract class AbstractStreamMessage { | 11 | +public abstract class AbstractStreamMessage extends AbstractRedisMessage { |
| 11 | 12 | ||
| 12 | /** | 13 | /** |
| 13 | * 获得 Redis Stream Key | 14 | * 获得 Redis Stream Key |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessageListener.java
| @@ -2,15 +2,18 @@ package cn.iocoder.yudao.framework.mq.core.stream; | @@ -2,15 +2,18 @@ package cn.iocoder.yudao.framework.mq.core.stream; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 6 | +import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 7 | +import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 5 | import lombok.Getter; | 8 | import lombok.Getter; |
| 6 | import lombok.Setter; | 9 | import lombok.Setter; |
| 7 | import lombok.SneakyThrows; | 10 | import lombok.SneakyThrows; |
| 8 | import org.springframework.beans.factory.annotation.Value; | 11 | import org.springframework.beans.factory.annotation.Value; |
| 9 | import org.springframework.data.redis.connection.stream.ObjectRecord; | 12 | import org.springframework.data.redis.connection.stream.ObjectRecord; |
| 10 | -import org.springframework.data.redis.core.RedisTemplate; | ||
| 11 | import org.springframework.data.redis.stream.StreamListener; | 13 | import org.springframework.data.redis.stream.StreamListener; |
| 12 | 14 | ||
| 13 | import java.lang.reflect.Type; | 15 | import java.lang.reflect.Type; |
| 16 | +import java.util.List; | ||
| 14 | 17 | ||
| 15 | /** | 18 | /** |
| 16 | * Redis Stream 监听器抽象类,用于实现集群消费 | 19 | * Redis Stream 监听器抽象类,用于实现集群消费 |
| @@ -39,10 +42,10 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | @@ -39,10 +42,10 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | ||
| 39 | @Getter | 42 | @Getter |
| 40 | private String group; | 43 | private String group; |
| 41 | /** | 44 | /** |
| 42 | - * RedisTemplate | 45 | + * RedisMQTemplate |
| 43 | */ | 46 | */ |
| 44 | @Setter | 47 | @Setter |
| 45 | - private RedisTemplate<String, ?> redisTemplate; | 48 | + private RedisMQTemplate redisMQTemplate; |
| 46 | 49 | ||
| 47 | @SneakyThrows | 50 | @SneakyThrows |
| 48 | protected AbstractStreamMessageListener() { | 51 | protected AbstractStreamMessageListener() { |
| @@ -54,14 +57,20 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | @@ -54,14 +57,20 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | ||
| 54 | public void onMessage(ObjectRecord<String, String> message) { | 57 | public void onMessage(ObjectRecord<String, String> message) { |
| 55 | // 消费消息 | 58 | // 消费消息 |
| 56 | T messageObj = JsonUtils.parseObject(message.getValue(), messageType); | 59 | T messageObj = JsonUtils.parseObject(message.getValue(), messageType); |
| 57 | - this.onMessage(messageObj); | ||
| 58 | - // ack 消息消费完成 | ||
| 59 | - redisTemplate.opsForStream().acknowledge(group, message); | ||
| 60 | - // TODO 芋艿:需要额外考虑以下几个点: | ||
| 61 | - // 1. 处理异常的情况 | ||
| 62 | - // 2. 发送日志;以及事务的结合 | ||
| 63 | - // 3. 消费日志;以及通用的幂等性 | ||
| 64 | - // 4. 消费失败的重试,https://zhuanlan.zhihu.com/p/60501638 | 60 | + try { |
| 61 | + consumeMessageBefore(messageObj); | ||
| 62 | + // 消费消息 | ||
| 63 | + this.onMessage(messageObj); | ||
| 64 | + // ack 消息消费完成 | ||
| 65 | + redisMQTemplate.getRedisTemplate().opsForStream().acknowledge(group, message); | ||
| 66 | + // TODO 芋艿:需要额外考虑以下几个点: | ||
| 67 | + // 1. 处理异常的情况 | ||
| 68 | + // 2. 发送日志;以及事务的结合 | ||
| 69 | + // 3. 消费日志;以及通用的幂等性 | ||
| 70 | + // 4. 消费失败的重试,https://zhuanlan.zhihu.com/p/60501638 | ||
| 71 | + } finally { | ||
| 72 | + consumeMessageAfter(messageObj); | ||
| 73 | + } | ||
| 65 | } | 74 | } |
| 66 | 75 | ||
| 67 | /** | 76 | /** |
| @@ -85,4 +94,20 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | @@ -85,4 +94,20 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | ||
| 85 | return (Class<T>) type; | 94 | return (Class<T>) type; |
| 86 | } | 95 | } |
| 87 | 96 | ||
| 97 | + private void consumeMessageBefore(AbstractRedisMessage message) { | ||
| 98 | + assert redisMQTemplate != null; | ||
| 99 | + List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); | ||
| 100 | + // 正序 | ||
| 101 | + interceptors.forEach(interceptor -> interceptor.consumeMessageBefore(message)); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + private void consumeMessageAfter(AbstractRedisMessage message) { | ||
| 105 | + assert redisMQTemplate != null; | ||
| 106 | + List<RedisMessageInterceptor> interceptors = redisMQTemplate.getInterceptors(); | ||
| 107 | + // 倒序 | ||
| 108 | + for (int i = interceptors.size() - 1; i >= 0; i--) { | ||
| 109 | + interceptors.get(i).consumeMessageAfter(message); | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + | ||
| 88 | } | 113 | } |