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,6 +53,12 @@ public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedis | ||
| 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); | 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 | @Override | 62 | @Override |
| 57 | public void onMessage(ObjectRecord<String, String> message) { | 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 | package cn.iocoder.yudao.module.iot.mq.consumer.rule; | 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 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 4 | import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; | 6 | import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; |
| 7 | +import jakarta.annotation.PostConstruct; | ||
| 5 | import jakarta.annotation.Resource; | 8 | import jakarta.annotation.Resource; |
| 6 | import lombok.extern.slf4j.Slf4j; | 9 | import lombok.extern.slf4j.Slf4j; |
| 7 | -import org.springframework.context.event.EventListener; | ||
| 8 | -import org.springframework.scheduling.annotation.Async; | ||
| 9 | import org.springframework.stereotype.Component; | 10 | import org.springframework.stereotype.Component; |
| 10 | 11 | ||
| 11 | // TODO @puhui999:后面重构哈 | 12 | // TODO @puhui999:后面重构哈 |
| @@ -16,14 +17,34 @@ import org.springframework.stereotype.Component; | @@ -16,14 +17,34 @@ import org.springframework.stereotype.Component; | ||
| 16 | */ | 17 | */ |
| 17 | @Component | 18 | @Component |
| 18 | @Slf4j | 19 | @Slf4j |
| 19 | -public class IotRuleSceneMessageHandler { | 20 | +public class IotRuleSceneMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { |
| 20 | 21 | ||
| 21 | @Resource | 22 | @Resource |
| 22 | private IotRuleSceneService ruleSceneService; | 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 | public void onMessage(IotDeviceMessage message) { | 44 | public void onMessage(IotDeviceMessage message) { |
| 45 | + if (true) { | ||
| 46 | + return; | ||
| 47 | + } | ||
| 27 | log.info("[onMessage][消息内容({})]", message); | 48 | log.info("[onMessage][消息内容({})]", message); |
| 28 | ruleSceneService.executeRuleSceneByDevice(message); | 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 | package cn.iocoder.yudao.module.iot.core.messagebus.config; | 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 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 8 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.local.IotLocalMessageBus; | 9 | import cn.iocoder.yudao.module.iot.core.messagebus.core.local.IotLocalMessageBus; |
| 5 | import cn.iocoder.yudao.module.iot.core.messagebus.core.redis.IotRedisMessageBus; | 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,6 +13,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; | ||
| 8 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 9 | import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; | 14 | import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; |
| 10 | import org.apache.rocketmq.spring.core.RocketMQTemplate; | 15 | import org.apache.rocketmq.spring.core.RocketMQTemplate; |
| 16 | +import org.redisson.api.RedissonClient; | ||
| 11 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 17 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| @@ -18,6 +24,10 @@ import org.springframework.context.annotation.Configuration; | @@ -18,6 +24,10 @@ import org.springframework.context.annotation.Configuration; | ||
| 18 | import org.springframework.data.redis.core.RedisTemplate; | 24 | import org.springframework.data.redis.core.RedisTemplate; |
| 19 | import org.springframework.data.redis.core.StringRedisTemplate; | 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 | * IoT 消息总线自动配置 | 32 | * IoT 消息总线自动配置 |
| 23 | * | 33 | * |
| @@ -40,7 +50,7 @@ public class IotMessageBusAutoConfiguration { | @@ -40,7 +50,7 @@ public class IotMessageBusAutoConfiguration { | ||
| 40 | public static class IotLocalMessageBusConfiguration { | 50 | public static class IotLocalMessageBusConfiguration { |
| 41 | 51 | ||
| 42 | @Bean | 52 | @Bean |
| 43 | - public IotMessageBus iotLocalMessageBus(ApplicationContext applicationContext) { | 53 | + public IotLocalMessageBus iotLocalMessageBus(ApplicationContext applicationContext) { |
| 44 | log.info("[iotLocalMessageBus][创建 IoT Local 消息总线]"); | 54 | log.info("[iotLocalMessageBus][创建 IoT Local 消息总线]"); |
| 45 | return new IotLocalMessageBus(applicationContext); | 55 | return new IotLocalMessageBus(applicationContext); |
| 46 | } | 56 | } |
| @@ -55,8 +65,8 @@ public class IotMessageBusAutoConfiguration { | @@ -55,8 +65,8 @@ public class IotMessageBusAutoConfiguration { | ||
| 55 | public static class IotRocketMQMessageBusConfiguration { | 65 | public static class IotRocketMQMessageBusConfiguration { |
| 56 | 66 | ||
| 57 | @Bean | 67 | @Bean |
| 58 | - public IotMessageBus iotRocketMQMessageBus(RocketMQProperties rocketMQProperties, | ||
| 59 | - RocketMQTemplate rocketMQTemplate) { | 68 | + public IotRocketMQMessageBus iotRocketMQMessageBus(RocketMQProperties rocketMQProperties, |
| 69 | + RocketMQTemplate rocketMQTemplate) { | ||
| 60 | log.info("[iotRocketMQMessageBus][创建 IoT RocketMQ 消息总线]"); | 70 | log.info("[iotRocketMQMessageBus][创建 IoT RocketMQ 消息总线]"); |
| 61 | return new IotRocketMQMessageBus(rocketMQProperties, rocketMQTemplate); | 71 | return new IotRocketMQMessageBus(rocketMQProperties, rocketMQTemplate); |
| 62 | } | 72 | } |
| @@ -65,17 +75,55 @@ public class IotMessageBusAutoConfiguration { | @@ -65,17 +75,55 @@ public class IotMessageBusAutoConfiguration { | ||
| 65 | 75 | ||
| 66 | // ==================== Redis 实现 ==================== | 76 | // ==================== Redis 实现 ==================== |
| 67 | 77 | ||
| 78 | + /** | ||
| 79 | + * 特殊:由于 YudaoRedisMQConsumerAutoConfiguration 关于 Redis stream 的消费是动态注册,所以这里只能拷贝相关的逻辑!!! | ||
| 80 | + * | ||
| 81 | + * @see cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration | ||
| 82 | + */ | ||
| 68 | @Configuration | 83 | @Configuration |
| 69 | @ConditionalOnProperty(prefix = "yudao.iot.message-bus", name = "type", havingValue = "redis") | 84 | @ConditionalOnProperty(prefix = "yudao.iot.message-bus", name = "type", havingValue = "redis") |
| 70 | @ConditionalOnClass(RedisTemplate.class) | 85 | @ConditionalOnClass(RedisTemplate.class) |
| 71 | public static class IotRedisMessageBusConfiguration { | 86 | public static class IotRedisMessageBusConfiguration { |
| 72 | 87 | ||
| 73 | @Bean | 88 | @Bean |
| 74 | - public IotMessageBus iotRedisMessageBus(StringRedisTemplate redisTemplate) { | 89 | + public IotRedisMessageBus iotRedisMessageBus(StringRedisTemplate redisTemplate) { |
| 75 | log.info("[iotRedisMessageBus][创建 IoT Redis 消息总线]"); | 90 | log.info("[iotRedisMessageBus][创建 IoT Redis 消息总线]"); |
| 76 | return new IotRedisMessageBus(redisTemplate); | 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 | \ No newline at end of file | 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,12 +6,15 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 6 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | 6 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 7 | import jakarta.annotation.PostConstruct; | 7 | import jakarta.annotation.PostConstruct; |
| 8 | import jakarta.annotation.PreDestroy; | 8 | import jakarta.annotation.PreDestroy; |
| 9 | +import lombok.Getter; | ||
| 9 | import lombok.extern.slf4j.Slf4j; | 10 | import lombok.extern.slf4j.Slf4j; |
| 10 | import org.springframework.data.redis.connection.stream.*; | 11 | import org.springframework.data.redis.connection.stream.*; |
| 11 | import org.springframework.data.redis.core.RedisTemplate; | 12 | import org.springframework.data.redis.core.RedisTemplate; |
| 12 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; | 13 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 13 | 14 | ||
| 14 | import java.lang.reflect.Type; | 15 | import java.lang.reflect.Type; |
| 16 | +import java.util.ArrayList; | ||
| 17 | +import java.util.List; | ||
| 15 | 18 | ||
| 16 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.buildConsumerName; | 19 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.buildConsumerName; |
| 17 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.checkRedisVersion; | 20 | import static cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQConsumerAutoConfiguration.checkRedisVersion; |
| @@ -28,6 +31,9 @@ public class IotRedisMessageBus implements IotMessageBus { | @@ -28,6 +31,9 @@ public class IotRedisMessageBus implements IotMessageBus { | ||
| 28 | 31 | ||
| 29 | private final StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer; | 32 | private final StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer; |
| 30 | 33 | ||
| 34 | + @Getter | ||
| 35 | + private final List<IotMessageSubscriber<?>> subscribers = new ArrayList<>(); | ||
| 36 | + | ||
| 31 | public IotRedisMessageBus(RedisTemplate<String, ?> redisTemplate) { | 37 | public IotRedisMessageBus(RedisTemplate<String, ?> redisTemplate) { |
| 32 | this.redisTemplate = redisTemplate; | 38 | this.redisTemplate = redisTemplate; |
| 33 | checkRedisVersion(redisTemplate); | 39 | checkRedisVersion(redisTemplate); |
| @@ -87,6 +93,7 @@ public class IotRedisMessageBus implements IotMessageBus { | @@ -87,6 +93,7 @@ public class IotRedisMessageBus implements IotMessageBus { | ||
| 87 | // ack 消息消费完成 | 93 | // ack 消息消费完成 |
| 88 | redisTemplate.opsForStream().acknowledge(subscriber.getGroup(), message); | 94 | redisTemplate.opsForStream().acknowledge(subscriber.getGroup(), message); |
| 89 | }); | 95 | }); |
| 96 | + this.subscribers.add(subscriber); | ||
| 90 | } | 97 | } |
| 91 | 98 | ||
| 92 | } | 99 | } |