作者回复: 👍,嗯,即使数据成功发送到接收方设备了,tcp层再把数据交给应用层时也可能出现异常情况,比如存储客户端的本地db失败,导致消息在业务层实际是没成功收到的。这种情况下,可以通过业务层的ack来提供保障,客户端只有都执行成功才会回ack给服务端。
作者回复: 👍
作者回复: 是的,如果只是时间戳或者“只是有序但不连续的序号”的话,是只能保证消息的时序性,不能保证消息的连续性。这种情况可以通过版本号机制来解决,通过两个版本号组成的链表(推送的每条消息携带前一条消息的版本号和当前这条消息的版本号)来检测消息的连续性和时序性。
作者回复: 用户上线的时候携带本地最新一条消息的时间戳给服务端,服务端从离线缓存里取比这个时间戳大的消息发给客户端就行了呀
作者回复: A. 接收方本地去重只需要针对本机已经接收到的存在的消息来做就可以了,服务端接收时实际上已经会做一次存储层的去重了,只会存在没有回ack的消息导致接收方重复接收的情况,这种两次之间一般时间间隔都比较短的。
B. 如果低序号的消息还没到,由于没有收到客户端的ack服务端会有超时重传机制会重传这条低序号的消息,另外即使这个时候用户关机不等那条消息了,再次上线时,采用版本号机制的话客户端也是可以知道消息不完整,可以触发服务端进行重推。
作者回复: 是的,对于重试不可能保证一定会成功,这些情况一般会以服务端中真实处理为准,通过多终端消息同步机制来让客户端有机会重新同步状态。比如发消息服务端处理成功,但是客户端接收响应超时'这种情况,服务端在成功处理完后会给发送方的发送设备推送当前消息的版本号,如果发送方设备没收到这个版本号,下次上线时会重新同步服务端的状态,用服务端消息进行覆盖。对于你说的第二种情况也比较简单,接收方b需要对重复接收的消息进行去重处理就可以了。
作者回复: 看量级吧,我们自己的场景里mysql和hbase做为永久存储,pika作为离线消息的buffer存储 没有碰到瓶颈。
作者回复: 1没问题哈,对于2的话如果出现接收端进程崩溃,一般这个时候接收端APP也是处于不可用状态了,这种情况实际上也没法通过这个ACK机制来避免丢消息。可以在用户再次上线时进行完整性检查的确认,如果有消息没有被正确接收,再由服务补推。
作者回复: 👍
作者回复: 留言没完整哈,如果是重复接收的问题,需要客户端来进行去重的。
作者回复: 首先这里要能让客户端知道有一条消息msg1还没到,所以单纯使用时间戳可能是不够的,时间戳只能代表时序,并不能判断出完整性,所以可能还需要配合连续的序号来实现感知(比如每次建连,服务端针对这条连接的所有推送从0开始自增,随消息一起下推)。至于你说的类似tcp的重传机制,这个理论上是可以这么实现的,就是复杂度上会高一些。
作者回复: 👍
作者回复: 如果只是离线消息暂存的话,可以用pika或者redis,如果是消息的永久存储还是落db比较好。
作者回复: sid是一个在一个长连接的会话期间内连续且有序的id,它的作用除了让客户端识别消息的顺序还能让客户端识别消息是否有丢失的情况。而timestamp可以认为是一个全局的用于跨多次长连接的消息排序的。所以不推荐sid来直接代替。
另外还可以让客户端通过链表的方式来识别消息的顺序性,这样就可以让每条消息都只需要携带一个uuid就能解决排序和丢失识别的问题了。
作者回复: 是的,ack一般都需要设置次数限制避免由于对端异常导致无法正常回ack包,线上的经验一般是2-3次就可以了。
作者回复: 👍
作者回复: 是的,传输层可靠不代表业务层可靠。