标题想了半小时还是没想好。
算了。
反正大概就是这个意思。
上个月组里出了个大事故。
凌晨两点,值班的同事打来电话,声音都在抖。
说IM推送服务崩了。5w人连不上。线上炸了锅。
你知道吗,那一刻我突然想起三年前。
我第一次做推送模块的那天。
得意洋洋写了几百行代码,心想“不就发个消息嘛”。
这一回首次进行压测,当第2000个用户连接上来之际,程序一下子就出现 ,直接在原地失败了。
脸红得像煮熟的虾。
自那以后我再也不敢说推送简单。
到底什么是高性能推送
说白了就是两层。
第一层,连接。
你得想办法让客户端和服务端之间那条绳子不断。
冲进地铁隧道,是不是断掉了?App程序被强制关闭了,是不是消失了?手机因为电量耗尽而自动关机了,是不是没辙了?
每一层都是坑。
我见过最蠢的方案——短轮询。
每隔几秒钟问一次服务器“有新消息吗?”
你想啊,就算没有新消息,客户端也在疯狂发请求。
服务器的CPU,被烤得出现冒烟的情况,而用户体验糟糕到了极点,延迟了好几秒,才收到反馈。
这不是推送,这是折磨。
王道乃长连接,为当下主流之选,其具全双工通道,于服务端而言,可随时主动推送数据。
但这只是第一步。
消息队列才是救命稻草
给你讲个真事儿。
去年压测,50w在线用户,峰值每秒10w条消息。
初期方案:固定线程池200个。
一开始还行,等并发冲到8w,砰——线程池满了。
消息堆积。延迟飙到5秒以上。监控大盘报警声此起彼伏。
那晚,我们三个人,蹲在会议室,那会议室,实际是线上的,可气氛,却好似机房那般。
改了一夜。
最后确定方案:异步+消息队列。
将消息直接写入里,在该MQ顶上,主流程不会出现卡顿情况,由消费者缓慢进行推送操作,以此实现削峰填谷的效果。
改完再测,延迟稳定在200ms以内。爽。
实际上,其原理并非复杂万分,并非要在单一的路径之上,把所有事情都给堵塞住,而是应当依据实际情况,该进行卸货操作的,就去实施卸货,该稍作等待的,那就等待片刻。
Go还是Java?吵了三年还在吵
我给不了你标准答案。
维护海量长连接于接入层,Go原生有着GMP并发模型,单机能够把几十万并发连接支撑,确实是很香的。
但你让Go去写复杂业务逻辑试试?
啧。
所以现在的套路是:接入层Go,逻辑层Java或者PHP。
各干各的。分工明确。
接口一对接,世界清净了。
有的人呀,时而会陷入想不明白的状况,非得借助一把锤子去敲击每一颗钉子,实际上更换一种工具并非是什么丢人的事情。
消息要是丢了怎么办
这个问题最让我头疼。
说白了,保证消息不丢就一招——不管发没发出去,都当没发过。
直到收到ACK确认信号。
三层保险:
1. 客户端先存本地。只要是没收到ACK,死活不删。
2. 服务端获取到消息,随即撰写,同步进行复制,且复制至最少3个副本。
3. 进行定时扫描,针对待确认集合,若超时未回,便实施重发操作。通过幂等方式去除重复,以此防止出现重复情况。
好麻烦对吧。
但没办法。
你可还记得呀?往昔之时QQ呈现出怎样的情形呢?发送一句“在吗”,倘若对方未曾接收到,你全然无从知晓。
现在的用户可不一样。
丢一条消息,客服工单直接爆掉。产品经理第二天就杀到工位。
宁可慢点,也不能丢。
千万别忘了心跳
我踩过这个坑。
时间段内,系统连接数攀升,然而推送成功率与起始状态相近。
翻代码看了半天才反应过来——心跳检测形同虚设。
相当数量的僵尸连接附着于服务器之上,占据着内存以及CPU,而真正的用户却连连接的地方都没有。
进行心跳间隔的设置,开展定期的检查工作,将超时的连接直接予以踢掉。服务端会同时展开检查,客户端自身也会进行检查。
效果立竿见影,连接健康度从70%提升到95%。
群消息是分布式下的噩梦
私聊还好说。一对一发完就完事儿。
群聊呢?一个500人的群,发一条消息,服务器要推500次。
万人群?我这辈子都不想碰。
通常来讲,是进行写扩散,即消息抵达服务端之后,依据群成员列表逐个去投递。又或者是开展读扩散,也就是存储一份,不管是谁来读取,都由其自行拉取。
各有各的苦。写扩散服务端压力大,读扩散客户端拉取慢。
怎么办?长轮询+M后面加缓存。或者干脆混合模式。
但这问题其实到现在,我也没有完美答案。
压测做了三遍,还是怕
第一次压测直接崩了。
第二次压测撑住了10w连接,发现内存泄漏。
第三次压测撑住50w,发现日志打印太狠,磁盘写满了。
每一次都有新问题。每一次都觉得自己傻逼。
现在学聪明了。
在进行压测之际,要将监控妥善布置好,此监控涵盖CPU,还有内存,以及带宽,另外包括连接数,再就是消息延迟百分位数。
特别是P99延迟,低于500ms才算合格。
并且,不能够仅仅去看平均值,平均值会对你造成误导,而实际上真正存在的问题在于那占比1%的速度较慢的用户,这些用户是会责骂你的。
最后说点心里话
经历了从事IM推送工作的这几年时间,内心深处所获得的最为深刻的体会便是,永远都不要去相信“应当不会存在问题”这种说法。
你觉得没问题,那就一定有问题。
高并发这东西,你永远不知道哪块短板会最先断裂。
会有可能是代码,会有可能是网络,会有可能是运维人员操作上稍微疏忽,会有可能是用户所使用的设备质量欠佳。
但话说回来。
当那一万条消息真的都在500ms内送到了对方手机上的时候。
当深夜看监控面板,发现一切平稳运行的时候。
那种感觉。
还挺爽的。
写着写着就晚了。
肚子饿了。拉面去了。
明天继续搞那个离线消息的重复推送问题。据说又是新坑。
不过也算了吧。写的也不是啥NB的文章。
就是个踩坑记录。
希望对你有用。
就这样。
