Commit 05ac902dc925f78193afe15998867c276977bc3b

Authored by YunaiV
1 parent 19cf311b

feat:【IoT 物联网】增加 redis + event-bus 的实现(增加 job 清理能力)

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 }
... ...