Commit 05ac902dc925f78193afe15998867c276977bc3b
1 parent
19cf311b
feat:【IoT 物联网】增加 redis + event-bus 的实现(增加 job 清理能力)
Showing
4 changed files
with
91 additions
and
9 deletions
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java
| ... | ... | @@ -53,6 +53,12 @@ public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedis |
| 53 | 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | + protected AbstractRedisStreamMessageListener(String streamKey, String group) { | |
| 57 | + this.messageType = null; | |
| 58 | + this.streamKey = streamKey; | |
| 59 | + this.group = group; | |
| 60 | + } | |
| 61 | + | |
| 56 | 62 | @Override |
| 57 | 63 | public void onMessage(ObjectRecord<String, String> message) { |
| 58 | 64 | // 消费消息 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.mq.consumer.rule; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | |
| 4 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | |
| 3 | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 4 | 6 | import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; |
| 7 | +import jakarta.annotation.PostConstruct; | |
| 5 | 8 | import jakarta.annotation.Resource; |
| 6 | 9 | import lombok.extern.slf4j.Slf4j; |
| 7 | -import org.springframework.context.event.EventListener; | |
| 8 | -import org.springframework.scheduling.annotation.Async; | |
| 9 | 10 | import org.springframework.stereotype.Component; |
| 10 | 11 | |
| 11 | 12 | // TODO @puhui999:后面重构哈 |
| ... | ... | @@ -16,14 +17,34 @@ import org.springframework.stereotype.Component; |
| 16 | 17 | */ |
| 17 | 18 | @Component |
| 18 | 19 | @Slf4j |
| 19 | -public class IotRuleSceneMessageHandler { | |
| 20 | +public class IotRuleSceneMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { | |
| 20 | 21 | |
| 21 | 22 | @Resource |
| 22 | 23 | private IotRuleSceneService ruleSceneService; |
| 23 | 24 | |
| 24 | - @EventListener | |
| 25 | - @Async | |
| 25 | + @Resource | |
| 26 | + private IotMessageBus messageBus; | |
| 27 | + | |
| 28 | + @PostConstruct | |
| 29 | + public void init() { | |
| 30 | + messageBus.register(this); | |
| 31 | + } | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public String getTopic() { | |
| 35 | + return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; | |
| 36 | + } | |
| 37 | + | |
| 38 | + @Override | |
| 39 | + public String getGroup() { | |
| 40 | + return "iot_rule_consumer"; | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Override | |
| 26 | 44 | public void onMessage(IotDeviceMessage message) { |
| 45 | + if (true) { | |
| 46 | + return; | |
| 47 | + } | |
| 27 | 48 | log.info("[onMessage][消息内容({})]", message); |
| 28 | 49 | ruleSceneService.executeRuleSceneByDevice(message); |
| 29 | 50 | } | ... | ... |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.core.messagebus.config; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; | |
| 4 | +import cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob; | |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.job.RedisStreamMessageCleanupJob; | |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage; | |
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; | |
| 3 | 8 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | 9 | import cn.iocoder.yudao.module.iot.core.messagebus.core.local.IotLocalMessageBus; |
| 5 | 10 | import cn.iocoder.yudao.module.iot.core.messagebus.core.redis.IotRedisMessageBus; |
| ... | ... | @@ -8,6 +13,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; |
| 8 | 13 | import lombok.extern.slf4j.Slf4j; |
| 9 | 14 | import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; |
| 10 | 15 | import org.apache.rocketmq.spring.core.RocketMQTemplate; |
| 16 | +import org.redisson.api.RedissonClient; | |
| 11 | 17 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 12 | 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 13 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| ... | ... | @@ -18,6 +24,10 @@ import org.springframework.context.annotation.Configuration; |
| 18 | 24 | import org.springframework.data.redis.core.RedisTemplate; |
| 19 | 25 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 20 | 26 | |
| 27 | +import java.util.List; | |
| 28 | + | |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 30 | + | |
| 21 | 31 | /** |
| 22 | 32 | * IoT 消息总线自动配置 |
| 23 | 33 | * |
| ... | ... | @@ -40,7 +50,7 @@ public class IotMessageBusAutoConfiguration { |
| 40 | 50 | public static class IotLocalMessageBusConfiguration { |
| 41 | 51 | |
| 42 | 52 | @Bean |
| 43 | - public IotMessageBus iotLocalMessageBus(ApplicationContext applicationContext) { | |
| 53 | + public IotLocalMessageBus iotLocalMessageBus(ApplicationContext applicationContext) { | |
| 44 | 54 | log.info("[iotLocalMessageBus][创建 IoT Local 消息总线]"); |
| 45 | 55 | return new IotLocalMessageBus(applicationContext); |
| 46 | 56 | } |
| ... | ... | @@ -55,8 +65,8 @@ public class IotMessageBusAutoConfiguration { |
| 55 | 65 | public static class IotRocketMQMessageBusConfiguration { |
| 56 | 66 | |
| 57 | 67 | @Bean |
| 58 | - public IotMessageBus iotRocketMQMessageBus(RocketMQProperties rocketMQProperties, | |
| 59 | - RocketMQTemplate rocketMQTemplate) { | |
| 68 | + public IotRocketMQMessageBus iotRocketMQMessageBus(RocketMQProperties rocketMQProperties, | |
| 69 | + RocketMQTemplate rocketMQTemplate) { | |
| 60 | 70 | log.info("[iotRocketMQMessageBus][创建 IoT RocketMQ 消息总线]"); |
| 61 | 71 | return new IotRocketMQMessageBus(rocketMQProperties, rocketMQTemplate); |
| 62 | 72 | } |
| ... | ... | @@ -65,17 +75,55 @@ public class IotMessageBusAutoConfiguration { |
| 65 | 75 | |
| 66 | 76 | // ==================== Redis 实现 ==================== |
| 67 | 77 | |
| 78 | + /** | |
| 79 | + * 特殊:由于 YudaoRedisMQConsumerAutoConfiguration 关于 Redis stream 的消费是动态注册,所以这里只能拷贝相关的逻辑!!! | |
| 80 | + * | |
| 81 | + * @see cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration | |
| 82 | + */ | |
| 68 | 83 | @Configuration |
| 69 | 84 | @ConditionalOnProperty(prefix = "yudao.iot.message-bus", name = "type", havingValue = "redis") |
| 70 | 85 | @ConditionalOnClass(RedisTemplate.class) |
| 71 | 86 | public static class IotRedisMessageBusConfiguration { |
| 72 | 87 | |
| 73 | 88 | @Bean |
| 74 | - public IotMessageBus iotRedisMessageBus(StringRedisTemplate redisTemplate) { | |
| 89 | + public IotRedisMessageBus iotRedisMessageBus(StringRedisTemplate redisTemplate) { | |
| 75 | 90 | log.info("[iotRedisMessageBus][创建 IoT Redis 消息总线]"); |
| 76 | 91 | return new IotRedisMessageBus(redisTemplate); |
| 77 | 92 | } |
| 78 | 93 | |
| 94 | + /** | |
| 95 | + * 创建 Redis Stream 重新消费的任务 | |
| 96 | + */ | |
| 97 | + @Bean | |
| 98 | + public RedisPendingMessageResendJob iotRedisPendingMessageResendJob(IotRedisMessageBus messageBus, | |
| 99 | + RedisMQTemplate redisTemplate, | |
| 100 | + RedissonClient redissonClient) { | |
| 101 | + List<AbstractRedisStreamMessageListener<?>> listeners = getListeners(messageBus); | |
| 102 | + return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient); | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 创建 Redis Stream 消息清理任务 | |
| 107 | + */ | |
| 108 | + @Bean | |
| 109 | + public RedisStreamMessageCleanupJob iotRedisStreamMessageCleanupJob(IotRedisMessageBus messageBus, | |
| 110 | + RedisMQTemplate redisTemplate, | |
| 111 | + RedissonClient redissonClient) { | |
| 112 | + List<AbstractRedisStreamMessageListener<?>> listeners = getListeners(messageBus); | |
| 113 | + return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient); | |
| 114 | + } | |
| 115 | + | |
| 116 | + private List<AbstractRedisStreamMessageListener<?>> getListeners(IotRedisMessageBus messageBus) { | |
| 117 | + return convertList(messageBus.getSubscribers(), subscriber -> | |
| 118 | + new AbstractRedisStreamMessageListener<>(subscriber.getTopic(), subscriber.getGroup()) { | |
| 119 | + | |
| 120 | + @Override | |
| 121 | + public void onMessage(AbstractRedisStreamMessage message) { | |
| 122 | + throw new UnsupportedOperationException("不应该调用!!!"); | |
| 123 | + } | |
| 124 | + }); | |
| 125 | + } | |
| 126 | + | |
| 79 | 127 | } |
| 80 | 128 | |
| 81 | 129 | } |
| 82 | 130 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/redis/IotRedisMessageBus.java
| ... | ... | @@ -6,12 +6,15 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 7 | 7 | import jakarta.annotation.PostConstruct; |
| 8 | 8 | import jakarta.annotation.PreDestroy; |
| 9 | +import lombok.Getter; | |
| 9 | 10 | import lombok.extern.slf4j.Slf4j; |
| 10 | 11 | import org.springframework.data.redis.connection.stream.*; |
| 11 | 12 | import org.springframework.data.redis.core.RedisTemplate; |
| 12 | 13 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 13 | 14 | |
| 14 | 15 | import java.lang.reflect.Type; |
| 16 | +import java.util.ArrayList; | |
| 17 | +import java.util.List; | |
| 15 | 18 | |
| 16 | 19 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.buildConsumerName; |
| 17 | 20 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.checkRedisVersion; |
| ... | ... | @@ -28,6 +31,9 @@ public class IotRedisMessageBus implements IotMessageBus { |
| 28 | 31 | |
| 29 | 32 | private final StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer; |
| 30 | 33 | |
| 34 | + @Getter | |
| 35 | + private final List<IotMessageSubscriber<?>> subscribers = new ArrayList<>(); | |
| 36 | + | |
| 31 | 37 | public IotRedisMessageBus(RedisTemplate<String, ?> redisTemplate) { |
| 32 | 38 | this.redisTemplate = redisTemplate; |
| 33 | 39 | checkRedisVersion(redisTemplate); |
| ... | ... | @@ -87,6 +93,7 @@ public class IotRedisMessageBus implements IotMessageBus { |
| 87 | 93 | // ack 消息消费完成 |
| 88 | 94 | redisTemplate.opsForStream().acknowledge(subscriber.getGroup(), message); |
| 89 | 95 | }); |
| 96 | + this.subscribers.add(subscriber); | |
| 90 | 97 | } |
| 91 | 98 | |
| 92 | 99 | } | ... | ... |