【如何构建商业级别聊天系统】 MQTT 篇(五)保活 Keep Alive,请不要让你的 MQTT 服务变成小猪佩奇!

853 阅读5分钟

【如何构建商业级别聊天系统】 MQTT 篇(五)保活 Keep Alive,请不要让你的 MQTT 服务变成小猪佩奇!

特关人上人!dying 搁浅 神秘连接 哥哥姐姐弟弟妹妹叔叔阿姨们~

说点闲话

keep alive 保活,不光是对于 MQTT 来说需要保活,其实我们很多的系统,在需要确定对方是否处于可通信状态的时候都是需要这种保活机制。比如音频聊天,那么音频聊天的双方和服务器端同样也是需要一套 keep alive 保活的机制来确定双方的状态以便进行相应的处理。

了解 keep alive 对于系统设计来说是具备指导性意义。

MQTT 的 Keep Alive 保活机制

为什么需要保活?

TCP 半开连接问题 half-open 。

首先 MQTT 是基于 TCP 协议的,那么 TCP 的特性同样适用于 MQTT :可靠、有序、错误检测。

然而使用 TCP 连接的通信双方之间的传输有时不会同步。

例如,通信过程中一方崩溃或者传输错误,这种不完全连接的状态称为 ”half-open“

那么此时的问题就是,通信双方,一方崩溃并不会通知另一方,那么仍然连接的一方会继续请求并等待回复。这显然对于仍然连接的一方是体验很差的,或者说不合理的。

在这里插入图片描述

对于此 MQTT 的发明者 Andy Stanford-Clark 是如此解释的。

进一步说明规范的内容,keepalive 的目的是让应用程序 级别(客户端应用程序和代理)可以知道底层连接仍然是端到端的。

尽管理论上 TCP/IP 会在 socket 中断时通知你,但实际上,特别是在移动和卫星链接等情况下,通常会通过空中 “伪造” TCP 并在每一端放回标头,极有可能 TCP 会话到“黑洞”,即它似乎仍然打开,但实际上只是将你写入的任何内容倾倒在地板上。

因此,keepalive 会确认你确实仍在与代理交谈(并且从代理端,客户端确实仍处于连接状态),特别是当处于长时间连接的连接上时,或者订阅了不常发布的主题,或在 qos0(即无确认)发布给 borker 代理。

应使用(由 MQTT 库)从代理返回的响应客户端启动的“ping-req”的 ping-resp(“pong”!)来告诉应用程序连接是否已消失,或触发重新连接。

那么 MQTT 包含了这样一个 ”保活“ 的功能,该功能为 half-open 问题提供了一个解决方案,或者说一个评估连接是否断开的依据。

keep alive 确认 broker 代理 和 client 客户端 之间的连接仍然打开,并且 broker 和 client 之间是可以感知到彼此是有连接的

具体操作就是,当客户端与服务端建立连接后,客户端向服务端进行以秒未间隔的通讯,这个时间间隔定义了,客户端和服务器没有通讯的最大时长。

MQTT 这样对其定义:

"The Keep Alive ... is the maximum time interval that is permitted to elapse between the point at which the Client finishes transmitting one Control Packet and the point it starts sending the next. It is the responsibility of the Client to ensure that the interval between Control Packets being sent does not exceed the Keep Alive value. In the absence of sending any other Control Packets, the Client MUST send a PINGREQ Packet."

即: keep alive 是客户端完成发送数据包的时间点到下一个发送数据包的时间点所允许经过的最大时间间隔,准确发送 PINGREQ 数据包是客户端的责任。

只要消息交换频繁,且在 keep alive 所定义的时间范围内,就不需要发送额外的消息来确认连接。

但如果,在此期间,客户端没有发送消息,它必须发送一个 PINGREQ 数据包 来确认 客户端和代理双方都是可用的状态。

代理如果在 1.5 倍的 keep alive 时间间隔中 没有收到客户端的任何消息或者 PINGREQ 保活数据包,则断开客户端连接。同样的对于 客户端,在合理的时间内没有收到代理的响应也应该关闭连接。

Keep Alive Flow

keep alive 功能使用两个数据包来保证: PINGREQ 和 PINGRESP

PINGREQ 由客户端发送,无有效负载,客户端可以在任何时候发送该包来确认连接状态。

在这里插入图片描述

当服务端收到 PINGREQ 数据包时,必须回复一个 PINGRESP 数据包来表示其仍然可用,同样的 PINGRESP 也不包含有效负载

必须要知道的事

  • 如果客户端在 keep alive 的时间段内 没有发送任何数据包或者 PINGREQ 给服务端代理,则代理关闭连接,并且发送 LWT 最后的遗嘱消息 (如果客户端有设置的话)
  • MQTT 客户端有责任设置合适的 keep alive 值。例如可以根据当前信号的强度来调整 keep alive 的时间间隔。
  • 最大的 keep alive 间隔为 18h 12min 15 sec。如果 keep alive 的间隔为 0 则 keep alive 机制是无效的。

Client Task-Over 客户端接管

通常,客户端断开连接后可能会重新连接。有时 broker 服务器仍然会提供 half-open 半开连接给客户端(如在 1.5 倍的 keep alive 间隔内,客户端尝试断线重连,此时就会有两个客户端连接),在 MQTT 中,如果 broker 代理检测到了 半开连接,则会执行 客户端接管,borker 会断开先前的连接,并和客户端建立新的连接,这一行为保证了 半开连接 不会阻止客户端断线重连。