Commit 61ea09488ed624d1f15fc01b8ffa98aa717484b7

Authored by puhui999
1 parent 3c998597

【代码优化】IoT: 优化数据桥梁执行器抽象类增加泛型,减少子类类型强转

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
3 3 import com.google.common.cache.CacheBuilder;
4 4 import com.google.common.cache.CacheLoader;
5 5 import com.google.common.cache.LoadingCache;
  6 +import com.google.common.cache.RemovalListener;
6 7 import lombok.extern.slf4j.Slf4j;
7 8  
8 9 import java.time.Duration;
... ... @@ -15,22 +16,36 @@ import java.time.Duration;
15 16 /**
16 17 * 带缓存功能的数据桥梁执行器抽象类
17 18 *
  19 + * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。
  20 + *
  21 + * 主要特点:
  22 + * - 基于Guava Cache实现高效的生产者实例缓存管理
  23 + * - 自动处理生产者的生命周期(创建、获取、关闭)
  24 + * - 支持30分钟未访问自动过期清理机制
  25 + * - 异常处理与日志记录,便于问题排查
  26 + *
  27 + * 子类需要实现:
  28 + * - initProducer(Config) - 初始化特定类型的生产者实例
  29 + * - closeProducer(Producer) - 关闭生产者实例并释放资源
  30 + *
  31 + * @param <Config> 配置信息类型,用于初始化生产者
  32 + * @param <Producer> 生产者类型,负责将数据发送到目标系统
18 33 * @author HUIHUI
19 34 */
20 35 @Slf4j
21   -public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute {
  36 +public abstract class AbstractCacheableDataBridgeExecute<Config, Producer> implements IotDataBridgeExecute {
22 37  
23   - // TODO @huihui:AbstractCacheableDataBridgeExecute<Producer> 这样,下面的 Object, Object 就有了类型;另外 IotDataBridgeDO.Config 可以替代一个 Object 哇,
24 38 /**
25 39 * Producer 缓存
26 40 */
27   - private final LoadingCache<Object, Object> PRODUCER_CACHE = CacheBuilder.newBuilder()
28   - .expireAfterAccess(Duration.ofMinutes(30))
29   - .removalListener(notification -> {
30   - Object producer = notification.getValue();
  41 + private final LoadingCache<Config, Producer> PRODUCER_CACHE = CacheBuilder.newBuilder()
  42 + .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期
  43 + .removalListener((RemovalListener<Config, Producer>) notification -> {
  44 + Producer producer = notification.getValue();
31 45 if (producer == null) {
32 46 return;
33 47 }
  48 +
34 49 try {
35 50 closeProducer(producer);
36 51 log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey());
... ... @@ -38,15 +53,18 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
38 53 log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e);
39 54 }
40 55 })
41   - .build(new CacheLoader<Object, Object>() {
42   -
  56 + .build(new CacheLoader<Config, Producer>() {
43 57 @Override
44   - public Object load(Object config) throws Exception {
45   - Object producer = initProducer(config);
46   - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config);
47   - return producer;
  58 + public Producer load(Config config) throws Exception {
  59 + try {
  60 + Producer producer = initProducer(config);
  61 + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config);
  62 + return producer;
  63 + } catch (Exception e) {
  64 + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e);
  65 + throw e; // 抛出异常,触发缓存加载失败机制
  66 + }
48 67 }
49   -
50 68 });
51 69  
52 70 /**
... ... @@ -55,7 +73,7 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
55 73 * @param config 配置信息
56 74 * @return 生产者对象
57 75 */
58   - protected Object getProducer(Object config) throws Exception {
  76 + protected Producer getProducer(Config config) throws Exception {
59 77 return PRODUCER_CACHE.get(config);
60 78 }
61 79  
... ... @@ -66,13 +84,13 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg
66 84 * @return 生产者对象
67 85 * @throws Exception 如果初始化失败
68 86 */
69   - protected abstract Object initProducer(Object config) throws Exception;
  87 + protected abstract Producer initProducer(Config config) throws Exception;
70 88  
71 89 /**
72 90 * 关闭生产者
73 91 *
74 92 * @param producer 生产者对象
75 93 */
76   - protected abstract void closeProducer(Object producer);
  94 + protected abstract void closeProducer(Producer producer) throws Exception;
77 95  
78 96 }
79 97 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java
... ... @@ -24,7 +24,8 @@ import java.util.concurrent.TimeoutException;
24 24 */
25 25 @Component
26 26 @Slf4j
27   -public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  27 +public class IotKafkaMQDataBridgeExecute extends
  28 + AbstractCacheableDataBridgeExecute<IotDataBridgeDO.KafkaMQConfig, KafkaTemplate<String, String>> {
28 29  
29 30 private static final Duration SEND_TIMEOUT = Duration.ofMillis(10);
30 31  
... ... @@ -38,11 +39,10 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
38 39 executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig());
39 40 }
40 41  
41   - @SuppressWarnings("unchecked")
42 42 private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) {
43 43 try {
44 44 // 1. 获取或创建 KafkaTemplate
45   - KafkaTemplate<String, String> kafkaTemplate = (KafkaTemplate<String, String>) getProducer(config);
  45 + KafkaTemplate<String, String> kafkaTemplate = getProducer(config);
46 46  
47 47 // 2. 发送消息并等待结果
48 48 kafkaTemplate.send(config.getTopic(), message.toString())
... ... @@ -56,24 +56,22 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
56 56 }
57 57  
58 58 @Override
59   - protected Object initProducer(Object config) {
60   - IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config;
61   -
  59 + protected KafkaTemplate<String, String> initProducer(IotDataBridgeDO.KafkaMQConfig config) {
62 60 // 1.1 构建生产者配置
63 61 Map<String, Object> props = new HashMap<>();
64   - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers());
  62 + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers());
65 63 props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
66 64 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
67 65 // 1.2 如果配置了认证信息
68   - if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) {
  66 + if (config.getUsername() != null && config.getPassword() != null) {
69 67 props.put("security.protocol", "SASL_PLAINTEXT");
70 68 props.put("sasl.mechanism", "PLAIN");
71 69 props.put("sasl.jaas.config",
72 70 "org.apache.kafka.common.security.plain.PlainLoginModule required username=\""
73   - + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";");
  71 + + config.getUsername() + "\" password=\"" + config.getPassword() + "\";");
74 72 }
75 73 // 1.3 如果启用 SSL
76   - if (Boolean.TRUE.equals(kafkaConfig.getSsl())) {
  74 + if (Boolean.TRUE.equals(config.getSsl())) {
77 75 props.put("security.protocol", "SSL");
78 76 }
79 77  
... ... @@ -83,10 +81,8 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec
83 81 }
84 82  
85 83 @Override
86   - protected void closeProducer(Object producer) {
87   - if (producer instanceof KafkaTemplate) {
88   - ((KafkaTemplate<?, ?>) producer).destroy();
89   - }
  84 + protected void closeProducer(KafkaTemplate<String, String> producer) {
  85 + producer.destroy();
90 86 }
91 87  
92 88 // TODO @芋艿:测试代码,后续清理
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java
... ... @@ -19,7 +19,8 @@ import java.time.LocalDateTime;
19 19 */
20 20 @Component
21 21 @Slf4j
22   -public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  22 +public class IotRabbitMQDataBridgeExecute extends
  23 + AbstractCacheableDataBridgeExecute<IotDataBridgeDO.RabbitMQConfig, Channel> {
23 24  
24 25 @Override
25 26 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
... ... @@ -34,7 +35,7 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
34 35 private void executeRabbitMQ(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) {
35 36 try {
36 37 // 1. 获取或创建 Channel
37   - Channel channel = (Channel) getProducer(config);
  38 + Channel channel = getProducer(config);
38 39  
39 40 // 2.1 声明交换机、队列和绑定关系
40 41 channel.exchangeDeclare(config.getExchange(), "direct", true);
... ... @@ -52,16 +53,14 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
52 53  
53 54 @Override
54 55 @SuppressWarnings("resource")
55   - protected Object initProducer(Object config) throws Exception {
56   - IotDataBridgeDO.RabbitMQConfig rabbitConfig = (IotDataBridgeDO.RabbitMQConfig) config;
57   -
  56 + protected Channel initProducer(IotDataBridgeDO.RabbitMQConfig config) throws Exception {
58 57 // 1. 创建连接工厂
59 58 ConnectionFactory factory = new ConnectionFactory();
60   - factory.setHost(rabbitConfig.getHost());
61   - factory.setPort(rabbitConfig.getPort());
62   - factory.setVirtualHost(rabbitConfig.getVirtualHost());
63   - factory.setUsername(rabbitConfig.getUsername());
64   - factory.setPassword(rabbitConfig.getPassword());
  59 + factory.setHost(config.getHost());
  60 + factory.setPort(config.getPort());
  61 + factory.setVirtualHost(config.getVirtualHost());
  62 + factory.setUsername(config.getUsername());
  63 + factory.setPassword(config.getPassword());
65 64  
66 65 // 2. 创建连接
67 66 Connection connection = factory.newConnection();
... ... @@ -71,20 +70,13 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
71 70 }
72 71  
73 72 @Override
74   - protected void closeProducer(Object producer) {
75   - if (producer instanceof Channel) {
76   - try {
77   - Channel channel = (Channel) producer;
78   - if (channel.isOpen()) {
79   - channel.close();
80   - }
81   - Connection connection = channel.getConnection();
82   - if (connection.isOpen()) {
83   - connection.close();
84   - }
85   - } catch (Exception e) {
86   - log.error("[closeProducer][关闭 RabbitMQ 连接异常]", e);
87   - }
  73 + protected void closeProducer(Channel channel) throws Exception {
  74 + if (channel.isOpen()) {
  75 + channel.close();
  76 + }
  77 + Connection connection = channel.getConnection();
  78 + if (connection.isOpen()) {
  79 + connection.close();
88 80 }
89 81 }
90 82  
... ... @@ -124,4 +116,5 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
124 116 log.info("[main][第二次执行,应该会复用缓存的 channel]");
125 117 action.executeRabbitMQ(message, config);
126 118 }
  119 +
127 120 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java
... ... @@ -29,7 +29,8 @@ import java.time.LocalDateTime;
29 29 */
30 30 @Component
31 31 @Slf4j
32   -public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  32 +public class IotRedisStreamMQDataBridgeExecute extends
  33 + AbstractCacheableDataBridgeExecute<IotDataBridgeDO.RedisStreamMQConfig, RedisTemplate<String, Object>> {
33 34  
34 35 @Override
35 36 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
... ... @@ -46,7 +47,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
46 47 private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) {
47 48 try {
48 49 // 1. 获取 RedisTemplate
49   - RedisTemplate<String, Object> redisTemplate = (RedisTemplate<String, Object>) getProducer(config);
  50 + RedisTemplate<String, Object> redisTemplate = getProducer(config);
50 51  
51 52 // 2. 创建并发送 Stream 记录
52 53 ObjectRecord<String, IotDeviceMessage> record = StreamRecords.newRecord()
... ... @@ -59,17 +60,15 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
59 60 }
60 61  
61 62 @Override
62   - protected Object initProducer(Object config) {
63   - IotDataBridgeDO.RedisStreamMQConfig redisConfig = (IotDataBridgeDO.RedisStreamMQConfig) config;
64   -
  63 + protected RedisTemplate<String, Object> initProducer(IotDataBridgeDO.RedisStreamMQConfig config) {
65 64 // 1.1 创建 Redisson 配置
66 65 Config redissonConfig = new Config();
67 66 SingleServerConfig serverConfig = redissonConfig.useSingleServer()
68   - .setAddress("redis://" + redisConfig.getHost() + ":" + redisConfig.getPort())
69   - .setDatabase(redisConfig.getDatabase());
  67 + .setAddress("redis://" + config.getHost() + ":" + config.getPort())
  68 + .setDatabase(config.getDatabase());
70 69 // 1.2 设置密码(如果有)
71   - if (StrUtil.isNotBlank(redisConfig.getPassword())) {
72   - serverConfig.setPassword(redisConfig.getPassword());
  70 + if (StrUtil.isNotBlank(config.getPassword())) {
  71 + serverConfig.setPassword(config.getPassword());
73 72 }
74 73  
75 74 // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。
... ... @@ -90,17 +89,10 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid
90 89 }
91 90  
92 91 @Override
93   - protected void closeProducer(Object producer) {
94   - // TODO @huihui:try catch 交给父类来做。子类不处理异常
95   - if (producer instanceof RedisTemplate) {
96   - RedisConnectionFactory factory = ((RedisTemplate<?, ?>) producer).getConnectionFactory();
97   - try {
98   - if (factory != null) {
99   - ((RedissonConnectionFactory) factory).destroy();
100   - }
101   - } catch (Exception e) {
102   - log.error("[closeProducer][关闭 redisson 连接异常]", e);
103   - }
  92 + protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {
  93 + RedisConnectionFactory factory = producer.getConnectionFactory();
  94 + if (factory != null) {
  95 + ((RedissonConnectionFactory) factory).destroy();
104 96 }
105 97 }
106 98  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java
... ... @@ -20,7 +20,8 @@ import java.time.LocalDateTime;
20 20 */
21 21 @Component
22 22 @Slf4j
23   -public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  23 +public class IotRocketMQDataBridgeExecute extends
  24 + AbstractCacheableDataBridgeExecute<IotDataBridgeDO.RocketMQConfig, DefaultMQProducer> {
24 25  
25 26 @Override
26 27 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
... ... @@ -35,7 +36,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
35 36 private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) {
36 37 try {
37 38 // 1. 获取或创建 Producer
38   - DefaultMQProducer producer = (DefaultMQProducer) getProducer(config);
  39 + DefaultMQProducer producer = getProducer(config);
39 40  
40 41 // 2.1 创建消息对象,指定Topic、Tag和消息体
41 42 Message msg = new Message(
... ... @@ -57,19 +58,16 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
57 58 }
58 59  
59 60 @Override
60   - protected Object initProducer(Object config) throws Exception {
61   - IotDataBridgeDO.RocketMQConfig rocketMQConfig = (IotDataBridgeDO.RocketMQConfig) config;
62   - DefaultMQProducer producer = new DefaultMQProducer(rocketMQConfig.getGroup());
63   - producer.setNamesrvAddr(rocketMQConfig.getNameServer());
  61 + protected DefaultMQProducer initProducer(IotDataBridgeDO.RocketMQConfig config) throws Exception {
  62 + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup());
  63 + producer.setNamesrvAddr(config.getNameServer());
64 64 producer.start();
65 65 return producer;
66 66 }
67 67  
68 68 @Override
69   - protected void closeProducer(Object producer) {
70   - if (producer instanceof DefaultMQProducer) {
71   - ((DefaultMQProducer) producer).shutdown();
72   - }
  69 + protected void closeProducer(DefaultMQProducer producer) {
  70 + producer.shutdown();
73 71 }
74 72  
75 73 // TODO @芋艿:测试代码,后续清理
... ...