Dotnetty Mqtt Azure 网关服务端的实现步骤

    Dotnetty项目提供了mqtt的编码和解码,但没有提供mqtt客户端和服务端的例子,Azure的另一个项目 azure-iot-protocol-gateway 是基于 dotnetty 实现的 mqtt 网关,该项目用途是设备通过 mqtt 与 网关通讯,网关再通过AMQP协议与 Azure IoT Hub 通讯,从而实现了设备与hub的桥接。本文通过翻译文档的部分内容,来说明 mqtt server 的主要逻辑。

一、启动服务端

            return new ServerBootstrap()
                .Group(this.parentEventLoopGroup, this.eventLoopGroup)
                .Option(ChannelOption.SoBacklog, ListenBacklogSize)
                .Option(ChannelOption.AutoRead, false)
                .ChildOption(ChannelOption.Allocator, UnpooledByteBufferAllocator.Default)
                .ChildOption(ChannelOption.AutoRead, false)
                .Channel<TcpServerSocketChannel>()
                .Handler(acceptLimiter)
                .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    channel.Pipeline.AddLast(
                        TlsHandler.Server(this.tlsCertificate),
                        new AcceptLimiterTlsReleaseHandler(acceptLimiter),
                        MqttEncoder.Instance,
                        new MqttDecoder(true, maxInboundMessageSize),
                        new MqttAdapter(
                            this.settings,
                            this.sessionStateManager,
                            this.authProvider,
                            this.qos2StateProvider,
                            bridgeFactory));
                }));
  1. 程序入口处初始化 MqttAdapter(处理 Mqtt 的 Handler) 所需的参数(settings, sessionStateManager, authProvider, qos2StateProvider, bridgeFactory);
  2. 执行new ServerBootstrap(),即上面代码部分,属于 netty 服务端的常规套路;
  3. Bootstrapper 根据 CPU 核数来初始化 EventLoopGroup;
  4. Bootstrapper 创建并配置 ServerBootstrap.
    1. TcpServerSocketChannel 用作监听通道.
    2. 将 ActionChannelInitializer<ISocketChannel> 添加到监听通道的 管道(pipeline)中,当客户端连接通道建立后,为其创建管道,管道中的handler有: TlsHandler <-> MqttEncoder <-> MqttDecoder <-> MqttAdapter.
  5. Bootstrapper 调用 ServerBootstrap.BindAsync :
    • 创建一个监听通道实例
    • 将监听通道注册到Event Loop
    • 将 ServerBootstrapperAcceptor 添加到监听通道的 管道(译注:见Dotnetty 代码 ServerBootstrap init)
    • 调用监听通道的BindAsync方法
  6. 监听通道内部创建socket对象,绑定到地址/端口。

二、建立连接

  1. 一旦接收到一个客户端socket连接,监听通道创建TcpSocketChannel(客户端通道),并将TcpSocketChannel传给自己管道的 ServerBootstrapAcceptor.
  2. ServerBootstrapAcceptor 将TcpSocketChannel加入Event Loop,并将 ActionChannelInitializer添加到TcpSocketChannel的管道中.
  3. ActionChannelInitializer 将自己从管道中移除,并顺次执行管道中的handler(TlsHandler <-> MqttEncoder <-> MqttDecoder <-> MqttAdapter).
  4. 如果有 TlsHandler, 会执行TLS握手,不向后续handler发送数据.
  5. 如果有 MqttAdapter, 会在ChannelActive时调用context.Read(). TlsHandler 记录该请求但在TLS握手完成前不会放行该请求.
  6. 一旦TLS握手完成, TlsHandler 放行上一步的读请求, TcpSocketChannel从 socket 读取数据.
  7. socket收到数据后放入 ByteBuffer,发送给管道.
  8. TlsHandler收到数据后解析SSL帧. 如果数据不足帧的预期长度, 追加读请求. 否则解密帧,将解密数据发给下一个handler.
  9. MqttEncoder 不属于InBound handler(因为没有重载任何 inbound 方法),跳过.
  10. MqttDecoder 接收解密数据,尝试解析 MQTT 包(packet). 如果数据不足,向前一个handler 追加读请求。一旦解析到MQTT包,发送到下一个handler. 根据MQTT规范, 客户端首先发送 CONNECT 包. 我们假设 MqttDecoder 首先解析到的是 CONNECT 包.
  11. MqttAdapter 收到 CONNECT 包.
  12. MqttAdapter 创建连接(译注:本段是 Azure IoT Hub 的建立连接步骤,仅供参考):
    • MqttAdapter 调用 IDeviceIdentityProvider 使用CONNECT包的client ID, user name, and password认证设备. IDeviceIdentityProvider 返回用于与IoT Hub建立连接的设备信息.
    • MqttAdapter 创建 IoT Hub 连接,如果成功,返回一个 IDeviceClient 对象用于将来跟 IoT Hub 通讯.
    • MqttAdapter 调用 ISessionStateManager 查询设备的缓存会话. 如果CONNECT包的 CleanSession为真, MqttAdapter 会删除缓存会话.
    • MqttAdapter 开始从IoT Hub接收消息.
    • MqttAdapter 调用 context.WriteAsync() 发送 CONNACK 包.
  13. CONNACK 包发送给前一个handler.
  14. MqttDecoder 不属于OutBound handler(因为没有重写任何outbound 方法), 跳过.
  15. MqttEncoder 编码 CONNACK 包到字节数组并发给 TlsHandler.
  16. TlsHandler 加密 CONNACK 包为 SSL 帧并放入字节数组. TlsHandler 是打头的handler,因此字节数组被发送到socket对象. 须知,实际发送行为在调用Channel.Flush()发生.
  17. MqttAdapter处理CONNECT包时,如果又来了其他包,会被入队到 CONNACK 成功发给设备后再处理.

三、设备发布数据到网关

  1. 当设备发送 PUBLISH 包给网关时, 步骤同处理 CONNECT 包的 7-10 步.
  2. 一旦 MqttAdapter 收到PUBLISH 包, 就发给 MessageAsyncProcessor 做顺序异步处理.
  3. MessageAsyncProcessor 处理完再回调 MqttAdapter.PublishToServerAsync.
  4. MqttAdapter 根据PUBLISH包创建 Iot Hub 消息.
  5. MqttAdapter 从PUBLISH 包解析数据.
  6. MqttAdapter 将数据加到消息属性并发给 IDeviceClient(上面第12步创建).
  7. 如果QoS=1 (最少一次), MqttAdapter 会发送 PUBACK 包, 步骤类似于上面的第 13-16 步.

四、网关从IoT Hub接收数据

一旦 MqttAdapter 与 IoT hub 建立接收链路, 就可以在任何时间接收到消息,然后:

  1. MqttAdapter 找到对应的topic,准备 PUBLISH 给设备.(译注:设备对应的 PUBLISH topic,形如 ‘devices/{deviceId}/messages/events’)
  2. MqttAdapter 根据topic找设备的订阅列表,如果没有设备订阅该主题,则拒绝处理该消息..
  3. 根据匹配的订阅和 QoS,分别处理.
  4. 如果 QoS=0,
    • MqttAdapter发送 PUBLISH 包(见上面的第 13-16 步)
    • 同时从 IoT hub 删除该消息.
  5. 如果 QoS=1,
    • MqttAdapter 给RequestAckPairProcessor 发送PUBLISH 包,进行 PUBLISH-PUBACK 通讯.
    • RequestAckPairProcessor 发送 PUBLISH 包(见上面的第 13-16 步).
    • 同时,RequestAckPairProcessor 入队消息关键字. 
    • 正常情况下, PUBLISH对应的 PUBACK 包回到MqttAdapter.
    • MqttAdapter 将 PUBACK 包转给 RequestAckPairProcessor.
    • RequestAckPairProcessor 从队列中 匹配消息,如果没匹配到就丢弃 PUBACK 包,停止后续处理.
    • RequestAckPairProcessor 出队消息,调用 MqttAdapter 通知处理完成.
    • MqttAdapter 从IoT hub删除消息.
  6. 如果 QoS=2,
    • MqttAdapter 给RequestAckPairProcessor 发送PUBLISH 包,进行 PUBLISH-PUBREC 通讯.
    • RequestAckPairProcessor 发送 PUBLISH 包(见上面的第 13-16 步).
    • 同时,RequestAckPairProcessor 入队消息关键字. 
    • 正常情况下, PUBLISH对应的 PUBREC 包回到MqttAdapter.
    • MqttAdapter 将 PUBREC 包转给 RequestAckPairProcessor.
    • RequestAckPairProcessor 从队列中 匹配消息,如果没匹配到就丢弃 PUBREC 包,停止后续处理.
    • RequestAckPairProcessor 出队消息,调用 MqttAdapter 通知处理完成.
    • MqttAdapter 调用 IQos2StatePersistenceProvider.SetMessageAsync 持久化 PUBLISH 包已处理完毕.
    • MqttAdapter 发送 PUBREL 包给 RequestAckPairProcessor 进行  PUBREL-PUBCOMP 通讯.
    • RequestAckPairProcessor 发送 PUBREL 包(见上面的第 13-16 步).
    • 同时,RequestAckPairProcessor 入队消息关键字. 
    • 正常情况下, PUBREL对应的 PUBCOMP 包回到MqttAdapter.
    • MqttAdapter 将 PUBCOMP 包转给 RequestAckPairProcessor.
    • RequestAckPairProcessor从队列中 匹配消息,如果没匹配到就丢弃 PUBCOMP 包,停止后续处理. 
    • RequestAckPairProcessor出队消息,调用 MqttAdapter 通知处理完成. 
    • MqttAdapter 从IoT hub删除消息.
    • MqttAdapter 调用 IQos2StatePersistenceProvider.DeleteMessageAsync 删除 QoS 2 记录.

总结

     azure-iot-protocol-gateway 项目在dotnetty基础上,实现了azure mqtt 网关(一种特殊的服务端),其代码可作为基于dotnetty 开发mqtt服务端的参考。

猜你喜欢

转载自blog.csdn.net/xhydongda/article/details/113845940