大部分场景中,业务层ACK确认机制 + 消息重传机制 + 消息完整性检查,能解决消息丢失的问题。

业务层的ACK确认机制和重传机制

ACK是确认字符(Acknowledge character)的意思,TCP协议默认提供了ACK机制,如果接收方成功接收到数据,就会回复一个ACK数据,表示发送方发出的数据已确认接收无误,在“三次握手”、“四次挥手”中经常见到。 ACK确认机制:TCP传输时将每个字节的数据都进行编号,即序列号。TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答,也就是发送ACK报文。 这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。 有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。这也是TCP传输可靠性的保证之一。

重传机制

发送方发送一部分数据后,都会等待接收方发送的ACK报文,并解析ACK报文,判断数据是否传输成功。发送方迟迟收不到ACK报文的原因可能有两个:

  • 数据在传输过程中由于网络原因等直接全体丢包,接收方没有接收到;
  • 接收方接收到了响应的数据,但是发送的ACK报文响应却由于网络原因丢包了。

超时重传机制就是发送方在发送完数据后等待一个时间,如果在超时时间内没有接收到ACK报文,就重新发送数据。如果是上述的第一个原因,接收方收到二次重发的数据后,便进行ACK应答。如果是第二个原因,接收方发现接收的数据已存在,就直接丢弃,仍旧发送ACK应答。

业务层的ACK确认机制参考了TCP的ACK确认机制,其策略是IM服务器在推送消息时,携带一个标识SID(安全标识符,类似TCP的sequenceId),推送出消息后会将当前消息添加到“待ACK消息列表”,客户端B成功接收完消息后,会给IM服务器回一个业务层的ACK包,包中携带有本条接收消息的SID,IM服务器接收后,会从“待ACK消息列表”记录中删除此条消息,本次推送才算真正结束。

业务层的消息重传机制也参考了TCP协议的重传机制,IM服务器的“等待ACK队列”一般会维护一个超时计时器,一定时间内如果没有收到用户B发回的ACK包,就从“等待ACK队列”中重新拉取并进行重推。

为什么有了TCP协议本身的ACK机制,还需要业务层的ACK机制? 这是因为TCP属于传输层,而IM服务属于应用层。 TCP的ACK保证网络传输层的可靠性,即消息是否送达,但不能保证数据能够被应用层正确可靠处理;业务层ACK进行消息是否送达和是否正确处理的逻辑,达到不丢消息、消息不重复的目的。

时间戳比对检查消息完整性

在上面列举的丢失消息的第 4 种可能性中,如果步骤 4 IM服务器将消息推送出去后就宕机了,而这条消息又因为某些原因丢失了,服务器由于宕机无法触发重传机制,导致用户B收不到该消息。可以采取“时间戳比对”机制进行完整性检查。

消息不重复

消息重复的原因 在上面列举的丢失消息的几种可能性中,第 3 种可能性存在一种场景,步骤 4 将消息成功推送给用户B,但步骤 3 因为某些原因导致超时、用户A收不到响应,这个时候会触发重传机制,用户A重新发送请求,用户B可能会收到重复消息。

消息重复的解决方案 IM服务器推送消息时,携带一个Sequence ID,这个Sequence ID在本次连接会话中唯一,同时针对同一条消息不变。当接收方接收到消息后,会根据这个Sequence ID来进行业务层的去重,可以有效地保证消息的不重复。

小结

通过业务层的ACK机制、重传机制和完整性检查,可以有效解决推送过程中消息丢失的问题; 通过客户端的去重机制,可以有效解决消息重复的问题。