Apache Kafka 驱动
PlantPulse Edge 的 Apache Kafka 驱动是对 kafka-clients 4.1.2 库进行封装的 Java 实现。 它通过 consumer 订阅外部流处理平台(Apache Kafka / Confluent Cloud / AWS MSK / Azure Event Hubs Kafka API)的主题, 将记录保存在内存缓存中,并在每个标签轮询周期返回缓存值。 写入使用 producer.send(异步)。
来源:plantpulse.driver.protocol.kafka.*
| 类 | 职责 |
|---|---|
KafkaDriver | ProtocolDriver 实现 — 连接/读取/写入/关闭、主题缓存、惰性订阅、后台轮询线程 |
库:lib/kafka-clients-4.1.2.jar
与 MQTT / WebSocket 的差异
PlantPulse 提供 三种基于消息的驱动 —— 它们都共享相同的 JsonExtract 4 模式 JSON 解码器。
| 驱动 | 传输协议 | 负载 | 使用场景 |
|---|---|---|---|
MQTT_CLIENT | MQTT v3.1.1 | 任意(通常为 JSON/纯文本) | IIoT broker(HiveMQ / Mosquitto / EMQX),QoS 0/1/2 |
WEBSOCKET | ws/wss | 任意(通常为 JSON) | 流式服务器 / 专有推送 API |
KAFKA(本页面) | Kafka 传输协议 | 记录键/值(通常为 JSON 字符串) | 高吞吐量流处理、数据重放(offset)、consumer group |
SPARKPLUG_B | MQTT + Protobuf | Sparkplug B 负载 | Cirrus Link / Tahu / Ignition 标准(基于 MQTT 的 IIoT 规范) |
Kafka 的核心是 持久化日志 + offset 重放 —— 当新 consumer 以 auto-offset=earliest 注册时,
它可以接收历史数据(在 broker 的保留期限内)。
操作流程
broker ──ConsumerRecord──> KafkaConsumer.poll() ──> pollerLoop (background thread)
│
├─ rawByTopic.put(topic, value)
└─ JsonExtract.updateCache(topicCache, topic, value)
PlantPulse 폴러 ──read(addr)──> JsonExtract.parse(addr.address) ──> JsonExtract.extract(...) (in-memory hit)
write(addr, value) ──KafkaProducer.send(ProducerRecord(topic, value))──> broker
首次调用 read 时,如果缓存未命中,会触发惰性订阅(调用 consumer.subscribe(...)),然后返回空字符串——
从下一个周期开始,实际值才会返回。在 tag_map 中注册的主题会在连接后立即通过
preSubscribeFromTagMap() 批量订阅,因此第一个周期就可能收到值。
4 模式 JSON 解码规范
JsonExtract 工具统一了 MQTT / WebSocket / Kafka 三个驱动的消息解码。
| 模式 | 地址格式 | 操作 |
|---|---|---|
| SCALAR | <topic> 或 <topic>.value | 将整个消息作为字符串返回。如果是 JSON 对象,则回退到原始数据。 |
| KEY | <topic>:<json-key> | 获取顶层 JSON 对象中某个键的值 |
| PATH | <topic>:$.<json-path> | JSON Pointer 动态求值(例如:$.data.tags.T1 → /data/tags/T1) |
| RAW | <topic>:_raw_ | 最后的原始消息(用于调试) |
缓存结构 —— 使用单个 Map<String, String>,键为 topic + "|" + field:
| 键 | 含义 |
|---|---|
topic|_raw_ | 最后的原始消息 |
topic|value | 标量(仅当消息不是 JSON 时) |
topic|<json-key> | 顶层 JSON 键对应的值(仅当是对象时) |
PATH 模式不经过缓存,而是每次都通过 JSONPointer.queryFrom(...) 对 rawByTopic 中最后的原始消息进行求值。
选项
| 键 | 默认值 | 说明 |
|---|---|---|
group-id | plantpulse-edge-<opc_id> | consumer group ID。offset 提交单位。 |
security-protocol | PLAINTEXT | PLAINTEXT / SSL / SASL_PLAINTEXT / SASL_SSL |
sasl-mechanism | (无) | PLAIN / SCRAM-SHA-256 / SCRAM-SHA-512 |
sasl-jaas-config | (无) | JAAS 配置(例如:...PlainLoginModule required username="u" password="p";) |
auto-offset | latest | earliest / latest —— 新 group 的起始位置 |
enable.auto.commit=true 始终被设置,broker 会自动提交 offset(默认 5 秒周期)。
连接生命周期
| 方法 | 操作 |
|---|---|
connect() | 应用选项 → 创建 consumer / producer → 启动 poller 线程 → 批量订阅 tag_map |
pollerLoop() | 循环处理 consumer.poll(500ms) → 按记录执行 JsonExtract.updateCache(...) |
read() | 解析 → 缓存未命中则惰性订阅 → 缓存命中则返回值 |
write() | producer.send(new ProducerRecord<>(topic, value))(异步) |
close() | consumer.wakeup() → 线程 join → 关闭 consumer/producer → 清空缓存 |
限制及后续工作
- 重连 —— kafka-clients 自带重连逻辑,但当整个 broker 宕机而导致长生命周期
poll()失败时, 驱动侧不会单独标记setConnected(false)→ 建议通过 broker 侧指标监控运行状态。 - AWS MSK IAM —— 需要添加
aws-msk-iam-authjar(当前未包含)。未来考虑将aws-msk-iam-auth-1.x.jar放入 lib/ , 并公开sasl.client.callback.handler.class作为选项。 - TLS truststore —— 无法通过驱动选项指定,使用 JVM 系统 truststore。如果 broker 使用自签名私有 CA,
请将 CA 证书导入 JVM truststore,或在网关启动选项中添加
-Djavax.net.ssl.trustStore=…。 - 分区/键指定 producer —— 写入时始终不携带键,因此分区是轮询分配的。这意味着 同一标签的写入不保证按顺序到达。如果需要按分区顺序,请将主题设置为 1 个分区,或在 consumer 端处理顺序保证。
- 事务性 producer —— 不支持。当前也未显式启用幂等 producer。
测试位置
test/java/plantpulse/driver/protocol/kafka/KafkaDriverTest.java —— 18 个测试。
- 类加载 / 层级(
BaseProtocolDriver/ProtocolDriver) - 初始状态 / 元数据(
isConnected/isWriteSupported/isExternal/getDriverSource) - 未连接时的读取/写入/空值安全
getDebugBaseUrl——kafka://,默认端口(9092)- 选项解析 ——
group-id/security-protocol/sasl-*/auto-offset,options=null 安全 - 4 模式解码 —— 直接填充缓存验证 KEY / PATH / RAW / SCALAR /
.value后缀 close()未连接安全
test/java/plantpulse/driver/protocol/common/JsonExtractTest.java —— 23 个测试(集成验证 4 模式解析器/缓存/提取)。
依赖真实 broker 的集成测试单独进行 —— 本单元测试无需外部 Kafka 即可运行。