什么是会话?
我们将从客户端向服务端发起 MQTT 连接请求开始,到连接中断直到会话过期为止的消息收发序列称之为会话。因此,会话可能仅持续一个网络连接,也可能跨越多个网络连接存在,如果客户端能在会话过期之前重新建立了连接的话。
在 MQTT v5 中会话过期时间由 Session Expiry Interval 字段决定,早前版本的协议没有限制会话过期时间,但通常由 MQTT 服务端决定。
什么是会话状态?
MQTT
要求客户端与服务端在会话有效期内存储一系列与客户端标识相关联的状态,称之为会话状态。
客户端需要存储以下会话状态:
已发送给服务端,但是还没有完成确认的
QoS 1
与QoS 2
消息。从服务端收到的,但是还没有完成确认的
QoS 2
消息。
服务端需要存储以下会话状态:
会话是否存在,即使会话状态其余部分为空。
客户端订阅信息,包括任何
订阅标识符
。已发送给客户端,但是还没有完成确认的
QoS 1
与QoS 2
消息。等待传输给客户端的
QoS 0
消息(可选),QoS 1
与QoS 2
消息。从客户端收到的,但是还没有完成确认的
QoS 2
消息,遗嘱消息
和遗嘱延时间隔
。会话过期时间。
会话状态的使用
如果客户端因为网络波动等原因导致连接短暂中断,但在会话过期前重新与服务端建立了连接,那么就可以沿用上次连接建立的订阅关系,不需要重新订阅一遍。在低带宽、不稳定的网络场景下,网络中断可能会发生得很频繁,保存会话状态的方式避免了每次连接都需要重新订阅,降低了重连时客户端和服务端的资源消耗。服务端在客户端脱机期间为其保留未完成确认的以及后续到达的消息,客户端重新连接时再一并转发,既可以避免消息丢失,也能够降低某些场景下用户对网络变化的感知度。
会话的开始与结束
MQTT v5.0 与 v3.1.1 在会话上有着较为显著的变化。MQTT v3.1.1 只有一个 Clean Session 字段,由客户端在连接时指定,为 1 表示客户端和服务器必须丢弃任何先前的会话并创建一个新的会话,且这个会话的生命周期与网络连接保持一致;为 0 则表示服务端必须使用与 Client ID 关联的会话来恢复与客户端的通信(除非会话不存在),客户端和服务器在断开连接后必须存储会话的状态。
客户端如何知道这是被恢复的会话?
显而易见的是,当客户端以期望从先前建立的会话恢复状态的方式发起连接,它需要知道服务端是否存在相应的会话,才能决定在连接建立后是否需要重复一遍订阅操作。关于这一点,MQTT 协议从 v3.1.1 开始,就为 CONNACK 报文设计了 Session Present 字段,用于表示当前连接使用的是否是一个全新会话,客户端可以根据这个字段的值进行判断。
使用建议
开发者需要特别注意 ClientID 与会话之间的联系,如果某些场景下同一个 ClientID 会被不同的应用或者用户多次使用,即每次连接都会有完全不同的行为,那么就需要确保每次连接时都请求了全新的会话。合理地评估是否需要持久会话,如非必要可以在正常离线时将会话设置为立即过期减少服务端资源占用。设置合适的会话过期时间,设置过短,可能会失去存储会话状态的意义,设置过长,可能会过多地占用服务端资源。