Commit d6cc9e23a306f0d0ecb13810de7317aee1b4b010

Authored by YunaiV
1 parent 9c76fd4b

完成基础的 stream 封装

src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
... ... @@ -83,12 +83,15 @@ public class RedisConfig {
83 83 redisTemplate.getRequiredConnectionFactory(), containerOptions);
84 84  
85 85 // 第二步,注册监听器,消费对应的 Stream 主题
86   - String consumerName = buildConsumerName();
  86 +// String consumerName = buildConsumerName();
  87 + String consumerName = "110";
87 88 listeners.forEach(listener -> {
88 89 // 创建 listener 对应的消费者分组
89 90 try {
90 91 redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup());
91 92 } catch (Exception ignore) {}
  93 + // 设置 listener 对应的 redisTemplate
  94 + listener.setRedisTemplate(redisTemplate);
92 95 // 创建 Consumer 对象
93 96 Consumer consumer = Consumer.from(listener.getGroup(), consumerName);
94 97 // 设置 Consumer 消费进度,以最小消费进度为准
... ...
src/main/java/cn/iocoder/dashboard/framework/redis/core/pubsub/AbstractChannelMessageListener.java
1 1 package cn.iocoder.dashboard.framework.redis.core.pubsub;
2 2  
3   -import cn.hutool.core.util.ArrayUtil;
  3 +import cn.hutool.core.util.TypeUtil;
4 4 import cn.iocoder.dashboard.util.json.JsonUtils;
5 5 import lombok.SneakyThrows;
6 6 import org.springframework.data.redis.connection.Message;
7 7 import org.springframework.data.redis.connection.MessageListener;
8   -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
9 8  
10 9 import java.lang.reflect.Type;
11 10  
... ... @@ -62,21 +61,11 @@ public abstract class AbstractChannelMessageListener<T extends ChannelMessage> i
62 61 */
63 62 @SuppressWarnings("unchecked")
64 63 private Class<T> getMessageClass() {
65   - Class<?> targetClass = getClass();
66   - while (targetClass.getSuperclass() != null) {
67   - // 如果不是 AbstractMessageListener 父类,继续向上查找
68   - if (targetClass.getSuperclass() != AbstractChannelMessageListener.class) {
69   - targetClass = targetClass.getSuperclass();
70   - continue;
71   - }
72   - // 如果是 AbstractMessageListener 父类,则解析泛型
73   - Type[] types = ((ParameterizedTypeImpl) targetClass.getGenericSuperclass()).getActualTypeArguments();
74   - if (ArrayUtil.isEmpty(types)) {
75   - throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName()));
76   - }
77   - return (Class<T>) types[0];
  64 + Type type = TypeUtil.getTypeArgument(getClass(), 0);
  65 + if (type == null) {
  66 + throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName()));
78 67 }
79   - throw new IllegalStateException(String.format("类型(%s) 找不到 AbstractMessageListener 父类", getClass().getName()));
  68 + return (Class<T>) type;
80 69 }
81 70  
82 71 }
... ...
src/main/java/cn/iocoder/dashboard/framework/redis/core/stream/AbstractStreamMessageListener.java
1 1 package cn.iocoder.dashboard.framework.redis.core.stream;
2 2  
3   -import cn.hutool.core.util.ArrayUtil;
  3 +import cn.hutool.core.util.TypeUtil;
  4 +import cn.iocoder.dashboard.util.json.JsonUtils;
4 5 import lombok.Getter;
  6 +import lombok.Setter;
5 7 import lombok.SneakyThrows;
6 8 import org.springframework.beans.factory.annotation.Value;
7 9 import org.springframework.data.redis.connection.stream.ObjectRecord;
  10 +import org.springframework.data.redis.core.RedisTemplate;
8 11 import org.springframework.data.redis.stream.StreamListener;
9   -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
10 12  
11 13 import java.lang.reflect.Type;
12 14  
... ... @@ -33,9 +35,14 @@ public abstract class AbstractStreamMessageListener&lt;T extends StreamMessage&gt;
33 35 /**
34 36 * Redis 消费者分组,默认使用 spring.application.name 名字
35 37 */
36   - @Value("spring.application.name")
  38 + @Value("${spring.application.name}")
37 39 @Getter
38 40 private String group;
  41 + /**
  42 + *
  43 + */
  44 + @Setter
  45 + private RedisTemplate<String, ?> redisTemplate;
39 46  
40 47 @SneakyThrows
41 48 protected AbstractStreamMessageListener() {
... ... @@ -45,10 +52,16 @@ public abstract class AbstractStreamMessageListener&lt;T extends StreamMessage&gt;
45 52  
46 53 @Override
47 54 public void onMessage(ObjectRecord<String, String> message) {
48   - System.out.println(message);
49   - if (true) {
50   -// throw new IllegalStateException("测试下");
51   - }
  55 + // 消费消息
  56 + 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
52 65 }
53 66  
54 67 /**
... ... @@ -64,23 +77,12 @@ public abstract class AbstractStreamMessageListener&lt;T extends StreamMessage&gt;
64 77 * @return 消息类型
65 78 */
66 79 @SuppressWarnings("unchecked")
67   - // TODO 芋艿:稍后重构
68 80 private Class<T> getMessageClass() {
69   - Class<?> targetClass = getClass();
70   - while (targetClass.getSuperclass() != null) {
71   - // 如果不是 AbstractMessageListener 父类,继续向上查找
72   - if (targetClass.getSuperclass() != AbstractStreamMessageListener.class) {
73   - targetClass = targetClass.getSuperclass();
74   - continue;
75   - }
76   - // 如果是 AbstractMessageListener 父类,则解析泛型
77   - Type[] types = ((ParameterizedTypeImpl) targetClass.getGenericSuperclass()).getActualTypeArguments();
78   - if (ArrayUtil.isEmpty(types)) {
79   - throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName()));
80   - }
81   - return (Class<T>) types[0];
  81 + Type type = TypeUtil.getTypeArgument(getClass(), 0);
  82 + if (type == null) {
  83 + throw new IllegalStateException(String.format("类型(%s) 需要设置消息类型", getClass().getName()));
82 84 }
83   - throw new IllegalStateException(String.format("类型(%s) 找不到 AbstractStreamMessageListener 父类", getClass().getName()));
  85 + return (Class<T>) type;
84 86 }
85 87  
86 88 }
... ...
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/mail/SysMailSendConsumer.java
... ... @@ -2,14 +2,16 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.mail;
2 2  
3 3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener;
4 4 import cn.iocoder.dashboard.modules.system.mq.message.mail.SysMailSendMessage;
  5 +import lombok.extern.slf4j.Slf4j;
5 6 import org.springframework.stereotype.Component;
6 7  
7 8 @Component
  9 +@Slf4j
8 10 public class SysMailSendConsumer extends AbstractStreamMessageListener<SysMailSendMessage> {
9 11  
10 12 @Override
11 13 public void onMessage(SysMailSendMessage message) {
12   -
  14 + log.info("[onMessage][消息内容({})]", message);
13 15 }
14 16  
15 17 }
... ...
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SysSmsSendConsumer.java
... ... @@ -2,14 +2,16 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
2 2  
3 3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener;
4 4 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
  5 +import lombok.extern.slf4j.Slf4j;
5 6 import org.springframework.stereotype.Component;
6 7  
7 8 @Component
  9 +@Slf4j
8 10 public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> {
9 11  
10 12 @Override
11 13 public void onMessage(SysSmsSendMessage message) {
12   - System.out.println(message);
  14 + log.info("[onMessage][消息内容({})]", message);
13 15 }
14 16  
15 17 }
... ...