整体方案
当前 EMQX 中的 MQTT 通信数据均通过程序中日志打印的方式查看,查阅和筛选极其不方便;另外当数据丢失时,日志中无法体现。我们需要将经过 MQTT 服务的所有消息记录下来,做数据的持久化,方便未来的问题定位
新的 EMQX 开源版本(5.0.13)支持了数据桥接功能,我们通过 webhookde 的数据桥接的方式将 MQTT 消息发送到 Web 服务,由 Web 服务存入数据库中。
目前 EMQX 开源版本不支持将消息直接转存到 MySql。
桥接服务
桥接服务主要接受来自 EMQX 推送的消息,中转后存入 MySql 数据库
数据表设计
CREATE TABLE mqtt_message_log(
id BIGINT NOT NULL COMMENT 'ID' ,
client_id VARCHAR(511) NULL COMMENT '客户端ID' ,
from_client_id VARCHAR(511) NULL COMMENT '来源客户端ID' ,
topic VARCHAR(511) NULL COMMENT '主题' ,
payload JSON NULL COMMENT '报文内容' ,
event VARCHAR(32) NULL COMMENT '事件' ,
reason VARCHAR(32) NULL COMMENT '原因' ,
revision INT NULL COMMENT '乐观锁' ,
created_by VARCHAR(32) NULL COMMENT '创建人' ,
created_time timestamp(3) NULL COMMENT '创建时间' ,
updated_by VARCHAR(32) NULL COMMENT '更新人' ,
updated_time timestamp(3) NULL COMMENT '更新时间' ,
PRIMARY KEY (id)
) COMMENT = 'MQTT消息日志 ';
BridgeController接口定义及处理
我们从 body 中获取 payload 的数据,其它信息从消息头中获取,解包方式依赖EMQX 的数据桥接配置中的数据包装,下文中会讲。
@RestController
public class BridgeController {
@Autowired
IMqttMessageLogService mqttMessageLogService;
@PostMapping
public void test(@RequestBody JSONObject payload,
@RequestHeader(name = "client-id") String clientId,
@RequestHeader(name = "topic") String topic,
@RequestHeader(name = "event") String event,
@RequestHeader(name = "from-client-id") String fromClientId,
@RequestHeader(name = "reason") String reason
) {
MqttMessageLog message=new MqttMessageLog();
message.setClientId(clientId);
message.setFromClientId(fromClientId);
message.setReason(reason);
message.setPayload(payload);
message.setEvent(event);
message.setTopic(topic);
mqttMessageLogService.save(message);
}
}
MqttMessageLog 实体类
@Getter
@Setter
@TableName("mqtt_message_log")
@ApiModel(value = "MqttMessageLog对象", description = "")
public class MqttMessageLog implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
@ApiModelProperty("客户端ID")
@TableField("client_id")
private String clientId;
@ApiModelProperty("来源客户端ID")
@TableField("from_client_id")
private String fromClientId;
@ApiModelProperty("主题")
@TableField("topic")
private String topic;
@ApiModelProperty("报文内容")
@TableField(value = "payload", typeHandler = JacksonTypeHandler.class)
private JSONObject payload;
@ApiModelProperty("事件")
@TableField("event")
private String event;
@ApiModelProperty("原因")
@TableField("reason")
private String reason;
@ApiModelProperty("乐观锁")
@TableField("revision")
@Version
private Integer revision;
@ApiModelProperty("创建人")
@TableField(value = "created_by", fill = FieldFill.INSERT)
private String createdBy;
@ApiModelProperty("创建时间")
@TableField(value = "created_time", fill = FieldFill.INSERT)
private LocalDateTime createdTime;
@ApiModelProperty("更新人")
@TableField(value = "updated_by", fill = FieldFill.UPDATE)
private String updatedBy;
@ApiModelProperty("更新时间")
@TableField(value = "updated_time", fill = FieldFill.UPDATE)
private LocalDateTime updatedTime;
}
MQTT 配置持久化
因为我们需要配置数据桥接与规则,因此这些配置信息我们需要持久化出来,否则在 EMQX 服务重启会丢失这些信息。
MQTT数据桥接与规则信息存储在/opt/emqx/data 中
mkdir -p /data/emqx
docker cp emqx:/opt/emqx/data /data/emqx
chown -R 1000:1000 /data/emqx/
chmod -R 755 /data/emqx/
卷挂载
-v /data/emqx/data:/opt/emqx/data
MQTT 数据桥接配置
配置数据桥接
创建规则,并选择动作(刚刚配置数据桥接)
桥接服务的部署
因为交接服务与 EMQX 深度依赖,因此,我们将 EMQX 和桥接服务放在同一个 pod 内,这样部署的另外一个好处是 EMQX 通过访问 127.0.0.1 即可访问桥接服务
参考资料
https://www.emqx.io/docs/zh/v5.0/data-integration/introduction.html#%E6%95%B0%E6%8D%AE%E6%A1%A5%E6%8E%A5
https://blog.youkuaiyun.com/qq_45894067/article/details/125045183
https://199604.com/2270