创建MQTT设备
模拟MQTT请求
- 安装
brew install mosquitto
- 发送请求
mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry -i no3ldom6ynybtqr7gayl -u 8xhr0hif932okkbvtsi8 -P ndsuskmfh1ddrke6e1ij -m "{temperature:25}"
找到切入点
这里跟HTTP有所不同,HTTP是Controller直接提供的接口,MQTT则是Thingsboard自己实现了MQTT服务端,通过订阅的形式进行的数据处理,那我们如何找到入口呢?
在此之前我们先了解下MQTT协议消息处理机制
消息时序图
从上图中可以知道,虽然我们模拟只发送了一条命令,但是实际上消息分为3次进行的处理
因此我们后续分析时需要分开进行分析,否则会出现消息处理对应不上的情况
MQTT服务
刚开始接触源码,哪个包是用于处理MQTT消息会找不到,这边通过配置文件入手的,因为是自己实现的MQTT服务,所以1883端口肯定在使用
MqttTransportService.java
public class MqttTransportService implements TbTransportService {
@PostConstruct
public void init() throws Exception {
log.info("Setting resource leak detector level to {}", leakDetectorLevel);
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
log.info("Starting MQTT transport...");
bossGroup = new NioEventLoopGroup(bossGroupThreadCount);
workerGroup = new NioEventLoopGroup(workerGroupThreadCount);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MqttTransportServerInitializer(context, false))
.childOption(ChannelOption.SO_KEEPALIVE, keepAlive);
serverChannel = b.bind(host, port).sync().channel();
if (sslEnabled) {
b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new MqttTransportServerInitializer(context, true))
.childOption(ChannelOption.SO_KEEPALIVE, keepAlive);
sslServerChannel = b.bind(sslHost, sslPort).sync().channel();
}
log.info("Mqtt transport started!");
}
}
找到对应的启动类后,不难发现底层是通过Netty实现的,Netty中消息处理的解析类为
childHandler(new MqttTransportServerInitializer(context, false))
MqttTransportServerInitializer.java
public class MqttTransportServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
SslHandler sslHandler = null;
if (context.isProxyEnabled()) {
pipeline.addLast("proxy", new HAProxyMessageDecoder());
pipeline.addLast("ipFilter", new ProxyIpFilter(context));
} else {
pipeline.addLast("ipFilter", new IpFilter(context));
}
if (sslEnabled && context.getSslHandlerProvider() != null) {
sslHandler = context.getSslHandlerProvider().getSslHandler();
pipeline.addLast(sslHandler);
}
pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize()));
pipeline.addLast("encoder", MqttEncoder.INSTANCE);
MqttTransportHandler handler = new MqttTransportHandler(context, sslHandler);
pipeline.addLast(handler);
ch.closeFuture().addListener(handler);
}
}
MqttTransportHandler.java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.trace("[{}] Processing msg: {}", sessionId, msg);
if (address == null) {
address = getAddress(ctx);
}
try {
if (msg instanceof MqttMessage) {
MqttMessage message = (MqttMessage) msg;
if (message.decoderResult().isSuccess()) {
processMqttMsg(ctx, message);
} else {
log.error("[{}] Message decoding failed: {}", sessionId, message.decoderResult().cause().getMessage());
closeCtx(ctx);
}
} else {
log.debug("[{}] Received non mqtt message: {}", sessionId, msg.getClass().getSimpleName());
closeCtx(ctx);
}
} finally {
ReferenceCountUtil.safeRelease(msg);
}
}
处理流程图
这里只把消息的处理流程发出来,消息的消费流程后续再处理