作者回复: 👍,嗯,即使数据成功发送到接收方设备了,tcp层再把数据交给应用层时也可能出现异常情况,比如存储客户端的本地db失败,导致消息在业务层实际是没成功收到的。这种情况下,可以通过业务层的ack来提供保障,客户端只有都执行成功才会回ack给服务端。
作者回复: 👍
作者回复: 是的,如果只是时间戳或者“只是有序但不连续的序号”的话,是只能保证消息的时序性,不能保证消息的连续性。这种情况可以通过版本号机制来解决,通过两个版本号组成的链表(推送的每条消息携带前一条消息的版本号和当前这条消息的版本号)来检测消息的连续性和时序性。
作者回复: 用户上线的时候携带本地最新一条消息的时间戳给服务端,服务端从离线缓存里取比这个时间戳大的消息发给客户端就行了呀
作者回复: A. 接收方本地去重只需要针对本机已经接收到的存在的消息来做就可以了,服务端接收时实际上已经会做一次存储层的去重了,只会存在没有回ack的消息导致接收方重复接收的情况,这种两次之间一般时间间隔都比较短的。
B. 如果低序号的消息还没到,由于没有收到客户端的ack服务端会有超时重传机制会重传这条低序号的消息,另外即使这个时候用户关机不等那条消息了,再次上线时,采用版本号机制的话客户端也是可以知道消息不完整,可以触发服务端进行重推。
作者回复: 是的,对于重试不可能保证一定会成功,这些情况一般会以服务端中真实处理为准,通过多终端消息同步机制来让客户端有机会重新同步状态。比如发消息服务端处理成功,但是客户端接收响应超时'这种情况,服务端在成功处理完后会给发送方的发送设备推送当前消息的版本号,如果发送方设备没收到这个版本号,下次上线时会重新同步服务端的状态,用服务端消息进行覆盖。对于你说的第二种情况也比较简单,接收方b需要对重复接收的消息进行去重处理就可以了。
作者回复: 看量级吧,我们自己的场景里mysql和hbase做为永久存储,pika作为离线消息的buffer存储 没有碰到瓶颈。
作者回复: 👍
作者回复: 1没问题哈,对于2的话如果出现接收端进程崩溃,一般这个时候接收端APP也是处于不可用状态了,这种情况实际上也没法通过这个ACK机制来避免丢消息。可以在用户再次上线时进行完整性检查的确认,如果有消息没有被正确接收,再由服务补推。
作者回复: 离线消息可以采取批量方式进行下推。文中有提到时间戳只能保证时序性,要确保消息不丢还需要进一步采取链表校验方式来验证一致性。对于时间戳方式客户端可以简单按时间戳进行排序。
作者回复: 一般可以通过业务层的多个字段一起来排重:比如接收方uid和内容,发送时间等,不需要客户端额外生成一个字段。去重实现上可以通过上面字段组合成生成一个hash然后根据消息收到的时间加上一个比较短的过期时间来写入到一个中央存储里。
作者回复: 消息ID要求全局唯一且时间相关,这个时间相关的精度是可以自行控制的,比如说是毫秒级有序还是秒级有序,通过内存级资源是能够实现每秒百万级发号能力的。我了解到的,类似微信这种IM,不需要支持多终端消息同步的场景,不需要通过拉取来对消息进行排序,所以实际上这个消息ID全局有序的必要性就很小了,只需要保证每个人的维度消息有序就可以,所以发号可以做到人维度。整体实现上也不需要时间相关了,只需要保证序号递增就能在接收端进行排序。腾讯整体据对外的分享,是采用多机房Set架构,其中包括海外机房,每个用户消息发送只会到所属的Set,然后通过多机房同步工具进行同步,所以消息发送方面延迟控制上应该也是有保障的。
作者回复: 是的,单纯的时间戳只能解决时序性问题,并不能解决丢消息的问题,这个可以采用链表式版本号方式或者保证连续递增序号推送。
作者回复: 每次下推都需要重开定时器的,一般重启几次仍然失败就放弃不推了。超时这个一般我们是几十秒,太长了也没必要,太短的话会出现重复推送比较多。重推都是针对单条消息的。
作者回复: 👍
作者回复: 1. 会有重试次数,毕竟即使收不到还有离线消息来补充。
重试多次仍然失败服务端可以主动断连来避免资源消耗。
2. 一般等着服务端重推就好了。
作者回复: 对,一般至少会缓存到离线消息的buffer中。
作者回复: 留言没完整哈,如果是重复接收的问题,需要客户端来进行去重的。
作者回复: 首先这里要能让客户端知道有一条消息msg1还没到,所以单纯使用时间戳可能是不够的,时间戳只能代表时序,并不能判断出完整性,所以可能还需要配合连续的序号来实现感知(比如每次建连,服务端针对这条连接的所有推送从0开始自增,随消息一起下推)。至于你说的类似tcp的重传机制,这个理论上是可以这么实现的,就是复杂度上会高一些。