Commit a2315826370546744354f3505d0f376d35dc0a34

Authored by YunaiV
1 parent d29b0beb

增强 mq starter 组件,支持 interceptor 拦截器机制

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&lt;T extends AbstractChannelMe @@ -25,6 +30,11 @@ public abstract class AbstractChannelMessageListener&lt;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&lt;T extends AbstractChannelMe @@ -44,7 +54,13 @@ public abstract class AbstractChannelMessageListener&lt;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&lt;T extends AbstractChannelMe @@ -68,4 +84,20 @@ public abstract class AbstractChannelMessageListener&lt;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&lt;T extends AbstractStreamMess @@ -39,10 +42,10 @@ public abstract class AbstractStreamMessageListener&lt;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&lt;T extends AbstractStreamMess @@ -54,14 +57,20 @@ public abstract class AbstractStreamMessageListener&lt;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&lt;T extends AbstractStreamMess @@ -85,4 +94,20 @@ public abstract class AbstractStreamMessageListener&lt;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 }