做项目时涉及到了android消息推送功能实现,选用了MQTT协议,具体MQTT协议实现服务器选用mosquitto,在这里将学习的MQTT相关知识做一个总结记录。
为什么选用mqtt协议而不是其他协议比如amqp,xmpp实现消息推送?
mqtt轻量,为物联网而设计,协议简单,所以客户端使用简单,服务端处理速度快。
amqp是为即时通信设计的吗???
xmpp相比于mqtt协议要复杂的多,使用起来复杂,服务端处理速度也会慢一些。
XMPP协议较复杂、冗余(基于XML)、费流量、费电,部署硬件成本高。 MQTT最快速,也最省流量(固定头长度仅为2字节),且极易扩展,适合二次开发 。
MQTT与XMPP协议对比: MQTT协议设计简单轻量、路由灵活,将在移动互联网物联网消息领域,全面取代PC时代的XMPP协议:
- MQTT协议一个字节固定报头,两个字节心跳报文,报文体积小编解码容易。XMPP协议基于繁重的XML,报文体积大且交互繁琐。
- MQTT协议基于主题(Topic)发布订阅模式消息路由,相比XMPP基于JID的点对点消息路由更为灵活。
- MQTT协议未定义报文内容格式,可以承载JSON、二进制等不同类型报文。XMPP协议采用XML承载报文,二进制必须Base64编码等处理。
- MQTT协议支持消息收发确认和QoS保证,XMPP主协议并未定义类似机制。MQTT协议有更好的消息可靠性保证。
MQTT会话(Clean Session)
MQTT客户端向服务器发起CONNECT请求时,可以通过’Clean Session’标志设置会话。
‘Clean Session’设置为0,表示创建一个持久会话,在客户端断开连接时,会话仍然保持并保存离线消息,直到会话超时注销。
‘Clean Session’设置为1,表示创建一个新的临时会话,在客户端断开时,会话自动销毁。
或者客户端主动发送DisConnect报文正常断开连接,之前的会话也会注销,不会再保存离线消息。
MQTT连接保活心跳
MQTT客户端向服务器发起CONNECT请求时,通过KeepAlive参数设置保活周期。
客户端在无报文发送时,按KeepAlive周期定时发送2字节的PINGREQ心跳报文,服务端收到PINGREQ报文后,回复2字节的PINGRESP报文。
服务端在1.5个心跳周期内,既没有收到客户端发布订阅报文,也没有收到PINGREQ心跳报文时,主动心跳超时断开客户端TCP连接。
TCP连接超时断开后是不是不再保存之前会话状态???如果不保存的话,那么下次客户端再连上时使用的就是新的会话,如何保证之前会话中推送的客户端未接收到的消息不丢失呢???
消息推送问题总结
推送消息前应先将其持久化到数据库中,持久化到数据库中的每条消息应该包含三个状态:尚未推送到mqtt、到达mqtt但未到达客户端,已经推送到客户端但未读、已经推送到且已读。
原则:只推送一次。 客户端与mqtt之间的连接正常断开:将数据库中与当前用户相关的所有到达mqtt但未到达客户端的消息设置为尚未推送到mqtt。 客户端与mqtt之间的连接异常断开,但会话未删除: 客户端与mqtt之间的连接异常断开,且会话已经删除
mqtt出了问题怎么办???推送的消息丢失了怎么处理?
YY: 在keep-Alive时间之内,客户端断线,mqtt server会保持它的会话状态(也就是订阅的主题接收到的消息等信息保留在内存中),在客户端重新连上之后会给它推送。
在keep-Alive时间之外,客户端断线,mqtt server不会再保持其会话状态,其订阅的主题新接收到的消息会先将其持久化到文件中,等客户端重新上线之后在给其推送。这个文件会保存多长时间呢???
客户端正常发送Disconnect报文断开连接,之后推送到其订阅主题上的消息会不会为其保存呢??? (如果clean session为false,会为其保存的,在其重新上线时会再为他传输此消息)。
会话状态里面都包含些什么东西呢???
根本问题就是如何知道当客户端重新上线连接的时候,如何知道之前推送到mqtt的但没有推送到客户端的消息有没有丢失。
消息推送具体使用
- 每条消息可以指定发送到的Topic、QOS、Retain status;
- 我读retained message(保留消息)的理解:不仅当前在线的订阅了当前主题的客户端能接收到,之后新订阅的所有客户端也要求能接收到。
- 服务质量为2的一条非保留消息在当前订阅此主题的所有客户端都确认已经接收到此消息之后,是不是就可以从持久化(磁盘)中移除了???在此非保留消息发送之后再对此主题进行订阅的客户端是不是不会收到此消息???(猜测应该是的)。
- persistent_client_expiration :为客户端设置持久化消息的保留时间,超期则删除,默认是永远不删除。