Commit d6cc9e23a306f0d0ecb13810de7317aee1b4b010
1 parent
9c76fd4b
完成基础的 stream 封装
Showing
5 changed files
with
39 additions
and
41 deletions
src/main/java/cn/iocoder/dashboard/framework/redis/config/RedisConfig.java
| @@ -83,12 +83,15 @@ public class RedisConfig { | @@ -83,12 +83,15 @@ public class RedisConfig { | ||
| 83 | redisTemplate.getRequiredConnectionFactory(), containerOptions); | 83 | redisTemplate.getRequiredConnectionFactory(), containerOptions); |
| 84 | 84 | ||
| 85 | // 第二步,注册监听器,消费对应的 Stream 主题 | 85 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 86 | - String consumerName = buildConsumerName(); | 86 | +// String consumerName = buildConsumerName(); |
| 87 | + String consumerName = "110"; | ||
| 87 | listeners.forEach(listener -> { | 88 | listeners.forEach(listener -> { |
| 88 | // 创建 listener 对应的消费者分组 | 89 | // 创建 listener 对应的消费者分组 |
| 89 | try { | 90 | try { |
| 90 | redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); | 91 | redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); |
| 91 | } catch (Exception ignore) {} | 92 | } catch (Exception ignore) {} |
| 93 | + // 设置 listener 对应的 redisTemplate | ||
| 94 | + listener.setRedisTemplate(redisTemplate); | ||
| 92 | // 创建 Consumer 对象 | 95 | // 创建 Consumer 对象 |
| 93 | Consumer consumer = Consumer.from(listener.getGroup(), consumerName); | 96 | Consumer consumer = Consumer.from(listener.getGroup(), consumerName); |
| 94 | // 设置 Consumer 消费进度,以最小消费进度为准 | 97 | // 设置 Consumer 消费进度,以最小消费进度为准 |
src/main/java/cn/iocoder/dashboard/framework/redis/core/pubsub/AbstractChannelMessageListener.java
| 1 | package cn.iocoder.dashboard.framework.redis.core.pubsub; | 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 | import cn.iocoder.dashboard.util.json.JsonUtils; | 4 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 5 | import lombok.SneakyThrows; | 5 | import lombok.SneakyThrows; |
| 6 | import org.springframework.data.redis.connection.Message; | 6 | import org.springframework.data.redis.connection.Message; |
| 7 | import org.springframework.data.redis.connection.MessageListener; | 7 | import org.springframework.data.redis.connection.MessageListener; |
| 8 | -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; | ||
| 9 | 8 | ||
| 10 | import java.lang.reflect.Type; | 9 | import java.lang.reflect.Type; |
| 11 | 10 | ||
| @@ -62,21 +61,11 @@ public abstract class AbstractChannelMessageListener<T extends ChannelMessage> i | @@ -62,21 +61,11 @@ public abstract class AbstractChannelMessageListener<T extends ChannelMessage> i | ||
| 62 | */ | 61 | */ |
| 63 | @SuppressWarnings("unchecked") | 62 | @SuppressWarnings("unchecked") |
| 64 | private Class<T> getMessageClass() { | 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 | package cn.iocoder.dashboard.framework.redis.core.stream; | 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 | import lombok.Getter; | 5 | import lombok.Getter; |
| 6 | +import lombok.Setter; | ||
| 5 | import lombok.SneakyThrows; | 7 | import lombok.SneakyThrows; |
| 6 | import org.springframework.beans.factory.annotation.Value; | 8 | import org.springframework.beans.factory.annotation.Value; |
| 7 | import org.springframework.data.redis.connection.stream.ObjectRecord; | 9 | import org.springframework.data.redis.connection.stream.ObjectRecord; |
| 10 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 8 | import org.springframework.data.redis.stream.StreamListener; | 11 | import org.springframework.data.redis.stream.StreamListener; |
| 9 | -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; | ||
| 10 | 12 | ||
| 11 | import java.lang.reflect.Type; | 13 | import java.lang.reflect.Type; |
| 12 | 14 | ||
| @@ -33,9 +35,14 @@ public abstract class AbstractStreamMessageListener<T extends StreamMessage> | @@ -33,9 +35,14 @@ public abstract class AbstractStreamMessageListener<T extends StreamMessage> | ||
| 33 | /** | 35 | /** |
| 34 | * Redis 消费者分组,默认使用 spring.application.name 名字 | 36 | * Redis 消费者分组,默认使用 spring.application.name 名字 |
| 35 | */ | 37 | */ |
| 36 | - @Value("spring.application.name") | 38 | + @Value("${spring.application.name}") |
| 37 | @Getter | 39 | @Getter |
| 38 | private String group; | 40 | private String group; |
| 41 | + /** | ||
| 42 | + * | ||
| 43 | + */ | ||
| 44 | + @Setter | ||
| 45 | + private RedisTemplate<String, ?> redisTemplate; | ||
| 39 | 46 | ||
| 40 | @SneakyThrows | 47 | @SneakyThrows |
| 41 | protected AbstractStreamMessageListener() { | 48 | protected AbstractStreamMessageListener() { |
| @@ -45,10 +52,16 @@ public abstract class AbstractStreamMessageListener<T extends StreamMessage> | @@ -45,10 +52,16 @@ public abstract class AbstractStreamMessageListener<T extends StreamMessage> | ||
| 45 | 52 | ||
| 46 | @Override | 53 | @Override |
| 47 | public void onMessage(ObjectRecord<String, String> message) { | 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<T extends StreamMessage> | @@ -64,23 +77,12 @@ public abstract class AbstractStreamMessageListener<T extends StreamMessage> | ||
| 64 | * @return 消息类型 | 77 | * @return 消息类型 |
| 65 | */ | 78 | */ |
| 66 | @SuppressWarnings("unchecked") | 79 | @SuppressWarnings("unchecked") |
| 67 | - // TODO 芋艿:稍后重构 | ||
| 68 | private Class<T> getMessageClass() { | 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,14 +2,16 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.mail; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; |
| 4 | import cn.iocoder.dashboard.modules.system.mq.message.mail.SysMailSendMessage; | 4 | import cn.iocoder.dashboard.modules.system.mq.message.mail.SysMailSendMessage; |
| 5 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | import org.springframework.stereotype.Component; | 6 | import org.springframework.stereotype.Component; |
| 6 | 7 | ||
| 7 | @Component | 8 | @Component |
| 9 | +@Slf4j | ||
| 8 | public class SysMailSendConsumer extends AbstractStreamMessageListener<SysMailSendMessage> { | 10 | public class SysMailSendConsumer extends AbstractStreamMessageListener<SysMailSendMessage> { |
| 9 | 11 | ||
| 10 | @Override | 12 | @Override |
| 11 | public void onMessage(SysMailSendMessage message) { | 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,14 +2,16 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; | 3 | import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; |
| 4 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; | 4 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; |
| 5 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | import org.springframework.stereotype.Component; | 6 | import org.springframework.stereotype.Component; |
| 6 | 7 | ||
| 7 | @Component | 8 | @Component |
| 9 | +@Slf4j | ||
| 8 | public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { | 10 | public class SysSmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { |
| 9 | 11 | ||
| 10 | @Override | 12 | @Override |
| 11 | public void onMessage(SysSmsSendMessage message) { | 13 | public void onMessage(SysSmsSendMessage message) { |
| 12 | - System.out.println(message); | 14 | + log.info("[onMessage][消息内容({})]", message); |
| 13 | } | 15 | } |
| 14 | 16 | ||
| 15 | } | 17 | } |