随着直播、视频等应用的兴起,消息场景也丰富了起来,最典型的就是聊天。具体到教育行业,场景更多,比如签到、答题等,这些场景对消息可靠性提了更高的要求,毕竟不能老师点“签到”学生收不到。本文就聊一聊消息可靠性的方案设计。文章较长,欢迎收藏。
这是企鹅辅导的老师正在直播上课。在线直播课堂是在线教育的核心业务。直播和录播、回放的区别在于,在上课的过程中,老师和学生可以进行实时的互动。 老师可以发鱼饼进行活跃气氛。可以发起签到,用来检查学生的到课情况。可以发答题卡,用来提问学生问题。辅导老师可以在聊天区,和学生进行沟通,或者学生回答老师的问题,达到一个互动的目标。这些直播互动的依赖是消息通道。 在直播课堂中的消息通道是一个核心功能模块,它承载了直播课堂中的所有的课堂互动(答题卡,习题,红包,签到,举手,上下课命令,禁言等)和聊天。除了课堂中的互动消息之外,还承载了课堂中一些比较频繁的 CGI 请求,比如维持课堂在线的心跳,成员列表的更新等等。另外还有一些非课堂内的消息推送,也会通过这个消息通道下发,比如站内私信等。
一条消息从一个用户发送到服务端,再发送到另外一个用户,这中间经过了 N 个模块的转发和网络传输,如果有一个模块发送失败,就涉及到重试,重试也有最大的次数,多了可能阻塞后面的消息发送,少了可能消息就这样丢失了,这就意味着你可能丢消息,也可能收到重复的消息;由于模块之间都是异步的,消息在不同的服务进程上去处理,这就意味着,你收到的消息可能是乱序的。
丢消息最主要的原因是多节点消息流动、网络抖动、单连接通道过载,而这些或多或少是比较难避免的。
企鹅辅导老师 PC 端和学生手机端在直播间都是通过下发 push 进行交互的,其中直播间举手,答题,签到等类型的 push 称之为可靠 push,直播间公屏的聊天消息属于普通 push。由于在线用户多 push 数量过大单通道压力增大或者网络卡顿等原因可能会导致学生端无法正常收到 push。对于普通 push 来说丢失一到两条并不会引起多大的问题,但是对于可靠 push 的丢失,往往会引起客户端比较严重的问题。如老师下发了一个开启课中练习的 push,客户端正确收到了,显示出了题目完整的遮住了直播画面,结束时老师下发关闭课中练习的 push,因为学生端是多个,若是一个学生端未正常收到关闭 push,那课中练习的画面就会一直遮挡住直播画面,该生只能退出直播间,重新进房拉取状态。
比如老师发了个鱼饼红包,有些同学没有收到,感受仿佛错过了一个亿。
2.1.1 TCP协议可靠传输分析
刚开始讨论在端上一起做消息可靠性的时候,有人说端上做的方案过于复杂,能不能单纯从推送上面去做的得更可靠一些呢?单纯从推送上面可以怎么去提升推送的可靠性?
我们来看看 TCP 协议如何保证可靠传输:
1.确认和重传:接收方收到报文就会确认,发送方发送一段时间后没有收到确认就重传。
2.数据校验
3.数据合理分片和排序:UDP:IP 数据报大于 1500 字节,大于 MTU。这个时候发送方 IP 层就需要分片 (fragmentation).把数据报分成若干片,使每一片都小于 MTU.而接收方 IP 层则需要进行数据报的重组.这样就会多做许多事情,而更严重的是,由于 UDP 的特性,当某一片数据传送中丢失时,接收方便无法重组数据报.将导致丢弃整个 UDP 数据报. TCP 会按 MTU 合理分片,接收方会缓存未按序到达的数据,重新排序后再交给应用层。
4.流量控制:当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。5. 拥塞控制:当网络拥塞时,减少数据的发送。
这是 TCP 连接上保证可靠传输的方案。
这是两个端之间的一条连接,而且是底层连接之间的数据传输,这个只能保证一条连接是连通性的情况下,数据比较可靠。但是数据量大的话,还是会出现超时,如果网络卡顿的话,重传次数变多,消息出现阻塞;如果连接断开了,那么已经扔给这个连接的数据也会随之丢失。
2.1.2 思路推演
如上分析,基于连接上层,还需要做一层应用层的可靠传输。在接入服务层(ACC 模块)和端之间的传输也有一层轻量级的可靠方案,其中包括确认和重传,数据合理合并,流量控制和拥塞控制(这个方案另外再起文章讨论)。但是这个依然是两个直接连接的端点之间的可靠方案,如果中途有多个节点的话,这个难度会直线上升。
如果数据在多个节点之间流动,比如 A-B-C , A 与 B 之间有确认重试的机制,但是B并不会等待 B 收到 C 的确认,才给 A 返回确认,这个节点之间是异步的;如果 B 等待收到 C 的确认,才给 A 回复确认的话,这种是同步方案。节点链路越长,前面的节点消耗就越大。
而且消息确认对方收到之前,消息存放什么地方,单连接的内存空间,还是单进程的内存空间,还是通过第三方存储(redis / kafka)来做缓存?应该由哪个模块来做这么复杂的可靠存储?
不难想象,中间多节点的话,每个节点都这样做可靠是不太可能的,比较理想的方案就在消息开始端来做这样的存储,消息终端来重试比较好。
参考一下一般情况下怎么提高上行消息(CGI,指客户端主动向服务端请求的数据)的可靠性?
1.提高单通道的连通性和稳定性
2.通过多通道保证通道的可靠性
3.重试机制:因为拉取消息是知道客户端需要什么数据,失败了客户端是可以重试的,可以决定重试多少次。
4.退而使用缓存:某些业务场景下可以使用缓存来兜底
1 和 2在推送模式下都是可以去做的。比如单通道下,提升 dns 解析成功率,多地域多运营商接入地,端上跑马竞速选取接入 vip 方案。在多通道上,我们在 wns 和 tiny 通道下也一起互备了很长一段时间,不过在流量特别大的情况下,完全互备的话,成本也是加倍的。
4缓存的话,在推送消息下并不适用
3重试机制:因为拉取消息是知道客户端需要什么数据,失败了客户端是可以重试的,可以决定重试多少次。那推送消息的话,客户端如何知道自己将会收到什么消息呢?可以针对可靠的消息生成连续递增的 seq,客户端就可以根据到达消息的 seq,知道自己是缺了哪些,然后去重试拉取。
最终思路:主要是在逻辑层来做一个重试和多通道兜底的可靠方案。 后台将消息打上有序的 seq 标志,在端上来判断消息的可靠性,并且通过 http 拉的方式补全,排序,去重,再下发给业务侧使用,从而解决下行 push 系统存在丢失,重复,乱序的问题。
目标:在保证去重和有序的情况下,尽量不丢失消息
设计要点1:消息在入库的时候生成连续递增的 seq
客户端就可以根据到达消息的 seq,知道自己是缺了哪些,然后去重试拉取缺失的消息、进行去重和排序。
设计要点2:保证不丢 vs 保证有序
最主要的问题点是:收到了超过当前下发最大 seq 的消息要不要下发?
这里主要看业务需要,这里在通道层来看,有一些消息需要有序,有一些消息不需要有序,所以通道层主要保证不丢失,业务层自己来保证有序。
当时和业务开发激烈讨论之后,因为整体的消息系统设计上,只有部分是可靠消息,而单类型消息命令字来说并没有产生自己的有序的 seq,另外业务层在处理逻辑上只需要处理消息有丢的情况,而无需再去管消息是否有序,如果同时需要处理这两种情况,是很复杂的,而且每个业务都需要单独的去处理,这样成本很大。所以最后决定,保证有序,然后才是不丢失。
设计要点3:push 通道 + 空洞拉取 + 后缀拉取 3个通道来提高到达率
这样就可以比较有效的保证了消息的可靠性。
设计要点4:拉取长连接通道 vs http 短连接通道
由于 acc 长连接集群,高负载自动扩容无法将连接自动无损重连到新机器上,需要手动预先部署。解决当前无法面对突峰情况,需要自动扩容的问题。减少机器成本成本,动态调整提高机器利用率。
结论:使用短连接,可以动态扩缩容。
设计要点5:空洞拉取要点如何进行空洞拉取,也是可靠方案中的关键,所以空洞拉取方案迭代过多次,这里说一下以前的做法,避免回头踩坑。
历史方案1:
消息序列:1(2)3(4)5, 2和4产生两个空洞,触发两个独立的空洞任务去处理,谁先回来先处理谁,2个任务之间无关联。后到的消息如果小于当前已处理的消息 seq 则丢弃。–>保证有序,但是丢失可能性大。
历史方案2:
基于 1 改动:后到的消息如果小于当前已处理的消息 seq 继续下发。–>尽量保证不丢,不保证有序。
历史方案3:
消息序列:1(2)3(4)5。空洞2触发的时候,启动 2s 等待,空洞 4 被触发的时候,如果发现当前有空洞任务的时候,就合并进去,并不重置等待计时器。和空洞 2 一起开始拉取。–>减少了拉取次数,但是增加了拉取的压力,因为空洞 4 并没有等待够 2s,可能推送的 4 马上也到达了,而且消息可能还没入拉取的 redis,拉取失败导致消息失败下发。特别是瞬时大量消息的时候,这种情况尤甚。
历史方案4:
消息序列:1(2)… 1000。触发大空洞的时候,分页进行拉取,减少 redis 的拉取压力。–>但分页的话,如果排队等待拉取的话,造成消息顺延滞后,如果同时发起的话,没有起到 redis 减压的作用,在业务场景下来看,一般不需要那么多老消息,最终改成只需要拉取最新一定数量的消息。
最终设计要点:
实现:空洞1等待 2000+300ms,空洞2如果发现距离空洞1时间内 300ms,则合并空洞任务,否则起新空洞任务。
设计要点6:所有可以配置的地方可以在后台返回,动态改动配置,本地保底配置
比如空洞拉取的等待时间,合并拉取时间,一次拉取条数。比如定时后缀拉取的时间,可以由后台根据消息密集程度动态算出。如果持续失败的话,端上需要进行退火策略进行重试。本地设置最小最大有效值,预防后台出错。
设计要点7:下发逻辑
设置一个当前处理消息的 maxHandledSeq,递增进行循环消息处理。判断 maxHandledSeq+1 的消息:如果属于下面 3 种情况,则进行正常下发或伪下发(伪下发:此消息标记为处理过,但实际上没有下发消息给业务)。
1.正常下发:消息状态为 DONE
2.伪下发:消息状态为 FAILED, 等待够 2s 再下发,主要是因为拉取失败会触发消息为 FAILED 的状态,等待够2s,可以使用 PUSH 迟到的消息补齐。
3.伪下发:其他消息状态,本地滞留 2s (空洞等待时间)+ 3s(拉取超时时间)+300ms(消息合并时间)以上的时候直接下发了。
下发逻辑是触发式的:两种情况下会触发。
1.push 推送,如果推送的 seq == maxHandledSeq+1,则触发下发。
2.空洞或者后缀拉取回调,会触发下发。
2.2 PUSH SDK 可靠推送模块整体设计方案
2.2.1 消息队列设计 下面主要说一下 JS 的实现逻辑:在这个整个过程中,一个可靠队列只需要一个 JS 对象来表示,所有消息的状态流动都可以通过修改消息的状态来表示。在 JS 中可以直接使用一个对象来表示。只要指定 seq,则可以快速取到相应的消息。在取消息和去重上都是非常快的。有序下发,只要按序递增 seq,就可以按序取消息下发,不需要对消息进行排序。记录消息队列中消息数量,超过一定值之后,消息从旧到新进行批量删除。队列中始终保持最新一定数量的消息,用于去重。
2.2.2 消息状态转换
2.3 前后端整体设计方案 消息入库的时候同时入 kafka 和 redis,kafka 用于推送,redis 用于拉取。推送消息通过长连接通道下发,拉取消息通过 http 短连接进行拉取。
CASE举例:
备注:空洞产生时间==为空洞消息后面的消息 push 的到达时间,也就是空洞消息被触发的时间
拉取触发场景:
1,2,(3)
预期操作:后缀拉取3
1, 2,(3),4
预期操作:空洞拉取3
1, 2,(3,时间t0),4,(5,时间t0+tns, tn<0.3s),6
预期操作:空洞3,5 间隔未超过0.3s,空洞一次拉取3,5。
1, 2,(3,时间t0),4,(5,时间t0+tns, tn>0.3s),6
预期操作:空洞3,5间隔超过0.3s, 空洞两次拉取,第一次拉取3,第二次拉取5。
拉取请求回包的场景的处理:
1, 2,(3,时间t0),4,(5,时间t0+0.1s(0.1<0.3s)),6。这里t0+2.3s时会触发空洞一次拉取3,5。
场景一:正常拉取场景。这里触发空洞一次拉取3,5。接口耗时1.5s。接口返回消息3,5。预期操作:业务侧收到1,2,(这里停顿到t0+3.8s(3.8s=空洞等待时间2s+合并区间0.3s+接口耗时1.5s))3,4,5,6。
场景二:丢失数据场景。这里触发空洞一次拉取3,5。接口耗时1.5s。接口返回消息3。预期操作:业务侧收到1,2,(这里停顿到t0+3.8s(3.8s=空洞等待时间2s+合并区间0.3s+接口耗时1.5s))3,4,6。
场景三:接口超时场景。这里触发空洞一次拉取3,5。接口耗时超过3s。客户端主动结束请求,请求失败。预期操作:业务侧收到1,2,(这里停顿到t0+5.3s(5.3s=空洞等待时间2s+合并区间0.3s+接口耗时5s)),4,6。
保证可靠,就是要保证一切异常的情况,所以这里面的策略测试也是比较困难的,因为要模拟一切异常的情况。
由于逻辑比较复杂,在功能上测试很难测试到里面细节策略和异常策略。比较理想的方案是:需要有自动化的单元测试,不过这个单元测试的模拟,复杂度和实现这个可靠方案少不了多少。后面继续另外起项目完成。在保证一定的快速迭代的计划下,一些半手动的测试方案也是必须的。
当前的在 Web 实现的半手动测试方案:
1.打印日志
2.提供简单的模拟工具,模拟推送,模拟消息丢失。
以下是一些模拟 case 的代码片段:
在console面板中看日志来验证策略:
看日志的方式还比较原始,只有很理解方案的人才可以看得懂。除此之外只能靠单元测试来认识这些数据日志了。
可以算出到达率是近似100%。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/oXId6M_cX-1bpzoPZcN8kA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。