Commit 69a27b1ee2024510bd4b8c3bb28a014927cdbbcc

Authored by puhui999
1 parent 1b15fb38

【功能新增】IoT: 新增 Kafka 数据桥梁实现

yudao-dependencies/pom.xml
@@ -35,6 +35,7 @@ @@ -35,6 +35,7 @@
35 <taos.version>3.3.3</taos.version> 35 <taos.version>3.3.3</taos.version>
36 <!-- 消息队列 --> 36 <!-- 消息队列 -->
37 <rocketmq-spring.version>2.3.2</rocketmq-spring.version> 37 <rocketmq-spring.version>2.3.2</rocketmq-spring.version>
  38 + <kafka-spring.version>3.3.3</kafka-spring.version>
38 <!-- 服务保障相关 --> 39 <!-- 服务保障相关 -->
39 <lock4j.version>2.2.7</lock4j.version> 40 <lock4j.version>2.2.7</lock4j.version>
40 <!-- 监控相关 --> 41 <!-- 监控相关 -->
@@ -285,13 +286,16 @@ @@ -285,13 +286,16 @@
285 <artifactId>yudao-spring-boot-starter-mq</artifactId> 286 <artifactId>yudao-spring-boot-starter-mq</artifactId>
286 <version>${revision}</version> 287 <version>${revision}</version>
287 </dependency> 288 </dependency>
288 -  
289 <dependency> 289 <dependency>
290 <groupId>org.apache.rocketmq</groupId> 290 <groupId>org.apache.rocketmq</groupId>
291 <artifactId>rocketmq-spring-boot-starter</artifactId> 291 <artifactId>rocketmq-spring-boot-starter</artifactId>
292 <version>${rocketmq-spring.version}</version> 292 <version>${rocketmq-spring.version}</version>
293 </dependency> 293 </dependency>
294 - 294 + <dependency>
  295 + <groupId>org.springframework.kafka</groupId>
  296 + <artifactId>spring-kafka</artifactId>
  297 + <version>${kafka-spring.version}</version>
  298 + </dependency>
295 <!-- 服务保障相关 --> 299 <!-- 服务保障相关 -->
296 <dependency> 300 <dependency>
297 <groupId>cn.iocoder.boot</groupId> 301 <groupId>cn.iocoder.boot</groupId>
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -81,6 +81,11 @@ @@ -81,6 +81,11 @@
81 <artifactId>rocketmq-spring-boot-starter</artifactId> 81 <artifactId>rocketmq-spring-boot-starter</artifactId>
82 <optional>true</optional> 82 <optional>true</optional>
83 </dependency> 83 </dependency>
  84 + <dependency>
  85 + <groupId>org.springframework.kafka</groupId>
  86 + <artifactId>spring-kafka</artifactId>
  87 + <optional>true</optional>
  88 + </dependency>
84 89
85 <dependency> 90 <dependency>
86 <groupId>org.pf4j</groupId> <!-- PF4J:内置插件机制 --> 91 <groupId>org.pf4j</groupId> <!-- PF4J:内置插件机制 -->
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java
@@ -170,4 +170,38 @@ public class IotDataBridgeDO extends BaseDO { @@ -170,4 +170,38 @@ public class IotDataBridgeDO extends BaseDO {
170 170
171 } 171 }
172 172
  173 + /**
  174 + * Kafka 配置
  175 + */
  176 + @Data
  177 + public static class KafkaMQConfig implements Config {
  178 +
  179 + /**
  180 + * Kafka 服务器地址
  181 + */
  182 + private String bootstrapServers;
  183 + /**
  184 + * 用户名
  185 + */
  186 + private String username;
  187 + /**
  188 + * 密码
  189 + */
  190 + private String password;
  191 + /**
  192 + * 是否启用 SSL
  193 + */
  194 + private Boolean ssl;
  195 +
  196 + /**
  197 + * 生产者组 ID
  198 + */
  199 + private String groupId;
  200 + /**
  201 + * 主题
  202 + */
  203 + private String topic;
  204 +
  205 + }
  206 +
173 } 207 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java
@@ -8,7 +8,7 @@ import lombok.extern.slf4j.Slf4j; @@ -8,7 +8,7 @@ import lombok.extern.slf4j.Slf4j;
8 import java.time.Duration; 8 import java.time.Duration;
9 9
10 /** 10 /**
11 - * 带缓存功能的数据桥接执行器抽象类 11 + * 带缓存功能的数据桥梁执行器抽象类
12 * 12 *
13 * @author HUIHUI 13 * @author HUIHUI
14 */ 14 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java
@@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
12 public interface IotDataBridgeExecute { 12 public interface IotDataBridgeExecute {
13 13
14 /** 14 /**
15 - * 执行数据桥接操作 15 + * 执行数据桥梁操作
16 * 16 *
17 * @param message 设备消息 17 * @param message 设备消息
18 * @param dataBridge 数据桥梁 18 * @param dataBridge 数据桥梁
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java
@@ -32,7 +32,7 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { @@ -32,7 +32,7 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute {
32 32
33 @Override 33 @Override
34 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { 34 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
35 - // 1.1 校验数据桥接的类型 == HTTP 35 + // 1.1 校验数据桥梁的类型 == HTTP
36 if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { 36 if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) {
37 return; 37 return;
38 } 38 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
  2 +
  3 +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
  4 +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.apache.kafka.clients.producer.ProducerConfig;
  8 +import org.apache.kafka.common.serialization.StringSerializer;
  9 +import org.springframework.kafka.core.DefaultKafkaProducerFactory;
  10 +import org.springframework.kafka.core.KafkaTemplate;
  11 +import org.springframework.stereotype.Component;
  12 +
  13 +import java.time.LocalDateTime;
  14 +import java.util.HashMap;
  15 +import java.util.Map;
  16 +import java.util.concurrent.TimeUnit;
  17 +import java.util.concurrent.TimeoutException;
  18 +
  19 +/**
  20 + * Kafka 的 {@link IotDataBridgeExecute} 实现类
  21 + *
  22 + * @author HUIHUI
  23 + */
  24 +@Component
  25 +@Slf4j
  26 +public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute {
  27 +
  28 + @Override
  29 + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
  30 + // 1.1 校验数据桥梁的类型 == KAFKA
  31 + if (!IotDataBridgTypeEnum.KAFKA.getType().equals(dataBridge.getType())) {
  32 + return;
  33 + }
  34 + // 1.2 执行 Kafka 发送消息
  35 + executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig());
  36 + }
  37 +
  38 + private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) {
  39 + try {
  40 + // 1. 获取或创建 KafkaTemplate
  41 + KafkaTemplate<String, String> kafkaTemplate = (KafkaTemplate<String, String>) getProducer(config);
  42 +
  43 + // 2. 发送消息并等待结果
  44 + kafkaTemplate.send(config.getTopic(), message.toString())
  45 + .get(10, TimeUnit.SECONDS); // 添加超时等待
  46 + log.info("[executeKafka][message({}) 发送成功]", message);
  47 + } catch (TimeoutException e) {
  48 + log.error("[executeKafka][message({}) config({}) 发送超时]", message, config, e);
  49 + } catch (Exception e) {
  50 + log.error("[executeKafka][message({}) config({}) 发送异常]", message, config, e);
  51 + }
  52 + }
  53 +
  54 + @Override
  55 + protected Object initProducer(Object config) {
  56 + IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config;
  57 +
  58 + // 1.1 构建生产者配置
  59 + Map<String, Object> props = new HashMap<>();
  60 + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers());
  61 + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
  62 + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
  63 +
  64 + // 1.2 如果配置了认证信息
  65 + if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) {
  66 + props.put("security.protocol", "SASL_PLAINTEXT");
  67 + props.put("sasl.mechanism", "PLAIN");
  68 + props.put("sasl.jaas.config",
  69 + "org.apache.kafka.common.security.plain.PlainLoginModule required username=\""
  70 + + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";");
  71 + }
  72 +
  73 + // 1.3 如果启用 SSL
  74 + if (Boolean.TRUE.equals(kafkaConfig.getSsl())) {
  75 + props.put("security.protocol", "SSL");
  76 + }
  77 +
  78 + // 2. 创建 KafkaTemplate
  79 + DefaultKafkaProducerFactory<String, String> producerFactory = new DefaultKafkaProducerFactory<>(props);
  80 + return new KafkaTemplate<>(producerFactory);
  81 + }
  82 +
  83 + @Override
  84 + protected void closeProducer(Object producer) {
  85 + if (producer instanceof KafkaTemplate) {
  86 + ((KafkaTemplate<?, ?>) producer).destroy();
  87 + }
  88 + }
  89 +
  90 + // TODO @芋艿:测试代码,后续清理
  91 + public static void main(String[] args) {
  92 + // 1. 创建一个共享的实例
  93 + IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute();
  94 +
  95 + // 2. 创建共享的配置
  96 + IotDataBridgeDO.KafkaMQConfig config = new IotDataBridgeDO.KafkaMQConfig();
  97 + config.setBootstrapServers("127.0.0.1:9092");
  98 + config.setTopic("test-topic");
  99 + config.setSsl(false);
  100 + config.setUsername(null);
  101 + config.setPassword(null);
  102 +
  103 + // 3. 创建共享的消息
  104 + IotDeviceMessage message = IotDeviceMessage.builder()
  105 + .requestId("TEST-001")
  106 + .productKey("testProduct")
  107 + .deviceName("testDevice")
  108 + .deviceKey("testDeviceKey")
  109 + .type("property")
  110 + .identifier("temperature")
  111 + .data("{\"value\": 60}")
  112 + .reportTime(LocalDateTime.now())
  113 + .tenantId(1L)
  114 + .build();
  115 +
  116 + // 4. 执行两次测试,验证缓存
  117 + log.info("[main][第一次执行,应该会创建新的 producer]");
  118 + action.executeKafka(message, config);
  119 +
  120 + log.info("[main][第二次执行,应该会复用缓存的 producer]");
  121 + action.executeKafka(message, config);
  122 + }
  123 +
  124 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java
@@ -24,7 +24,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe @@ -24,7 +24,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe
24 24
25 @Override 25 @Override
26 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { 26 public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) {
27 - // 1.1 校验数据桥接的类型 == ROCKETMQ 27 + // 1.1 校验数据桥梁的类型 == ROCKETMQ
28 if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { 28 if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) {
29 return; 29 return;
30 } 30 }