推送系统填坑
1:推送系统智能心跳,服务器怎么知道 某个客户端下次心跳时间?并以此设置超时时间?
魅族:我们是长连接服务,手机端和服务端要维持这个长连接,需要定期的发送心跳消息,我们为了节约电量和流量,手机端采用的是智能心跳模式。那么对服务端来说,它是不知道手机端下次是几分钟之后会发送心跳上来的,那么这个连接在服务端的超时时间应该设置多久就是一个问题了。我们的作法是:客户端在每一次的心跳消息中携带下一次的心跳时间,服务端就根据这个时间来设置连接的超时时间(即时通讯网注:这个主意很好!)。
参考:专访魅族架构师:海量长连接的实时消息推送系统的心得体会:http://www.52im.net/thread-750-1-1.html
2:消息重发问题 - (我们的解决是,服务器控制策略,尽量避免重发,要还是重发了,那就重发了)
魅族:设置了消息基于***的交互方式,首先推送消息的时候,不是把消息直接推送下去,是发一个通知到客户端,告诉你有消息,客户端拿到这个通知,发送一个指令上来,说获取这个消息,会带上一个收到最近消息的最大的***。
参考:魅族2500万长连接的实时消息推送架构的技术实践分享:http://www.52im.net/thread-723-1-1.html
基于魅族的这个方法,我们可以考虑我们的实现 - 每个消息,带上***,客户端判断,如果***重复,就丢弃消息
3:海量长链接推动的 集群负载均衡问题:我们暂时没考虑这个问题
魅族:比较常规的做法是前面加LVS,后台有几台服务器,但是LVS并不适合我们的业务场景,一台服务器可以接受400万的长连接,一个LVS,下面挂三台服务器,LVS首先存在单点问题,LVS一台就要承受1200万连接,这个是肯定扛不住这样多的连接数的,我们就放弃了使用单点运营。第一个是服务端做,第二个是客户端做,前面也讲过,获取IP列表,在获取的时候,我们LVS已经对那些IP进行过排序了,负载比较低的服务器是排在前面,拉下来之后,客户端直接拿前面的IP地址连就可以了,在这个地方直接解决负载均衡。解决负载均衡的同时也解决了跨运营商网络慢的问题。第一次客户端拉取IP列表,前面的服务器都是负载比较低的,但是客户端是对这个IP列表进行缓存的,缓存之后,如果断线重连,再连的时候,就不知道哪台服务器负载高,哪台服务器负载高,这里就有一个策略,客户端把IP划分成多个IP区间,每一个区间有多个IP,选取IP发一个探测包到服务器,服务器会针对探测包有响应包回来的,哪个先回来,就用哪个IP地址,这样就同时解决了跨运营商的网络慢的问题。
但是这样做的话,其实还是有问题的,不能单独在客户端简单做一个发一个探测包,先收到就用这个,这个也不行,服务端也需要做一些相应的策略假设某一台服务器负载比较高的时候,刚好收到探测包,立刻回一个响应,很快的客户端收到这个响应就会连上来,但是本身的负载已经比较高了,那服务端怎么解决这个问题?根据自身的负载情况来做延迟响应,负载达到一定的阀值的时候,比如说是300万,每超过10万,延迟时间稍微加50毫秒,收到你的响应包,我晚个50毫秒之后再回应答,这样跑马策略是可以解决整个的负载均衡。
参考:魅族2500万长连接的实时消息推送架构的技术实践分享:http://www.52im.net/thread-723-1-1.html
按我的理解,是客户端使用全IP方式连接服务器(我们这里是通过域名),在给客户端IP时,有一个策略,可以达到负载均衡的目的,为了确保缓存导致 这个策略不太准确,又采用了延时回执的策略。
总的来说,这些解决方法也不是多么精妙,但别人就是这样实现了,然后把问题解决了,你想精妙的没想的,问题也还是问题。
4:IOS推送(新一版暂时不考虑,如果需要做,先参考下面这篇文章)
iOS的推送服务APNs详解:设计思路、技术原理及缺陷等:http://www.52im.net/thread-345-1-1.html
5:推送技术点考虑
下面这篇文章,算是我见过的文章中,讨论推送技术点最完整的,可惜没有架构上的东西
Android端消息推送总结:实现原理、心跳保活、遇到的问题等:http://www.52im.net/thread-341-1-1.html
6:长链接安全性:如果不对长链接进行管控,恶意用户只需要分析出服务器IP和端口,就可以伪造大量的长链接,给服务器造成压力
(1)客户端每次登陆长链接服务时,服务端为这个ID的客户端生成一个密码或session,客户端长链接时,带上这个session,服务器检查session和ID的对应性和有效性
参考:http://www.360doc.com/content/17/1108/17/16915_702114603.shtml
(2)在长链接之前,做一个网关服务或鉴权服务,目的就是鉴权,鉴权成功,就让建立长链接
参考:http://www.52im.net/thread-776-1-1.html
7:TCP协议安全性:如果不对数据进行加密,TCP传输的数据就被窃取、伪造的风险
8:推送集群管理方案:
使用zookeeper类似的服务,来发现推送服务节点,并通过路由记录 长链接和 推送节点的对应关系,以此管理集群;
参考:http://www.360doc.com/content/17/1108/17/16915_702114710.shtml
美拍直播:使用推送层和连接层配合实现,按我理解,推送层其实就是网关层/服务发现层(貌似用etcd?),这里记录了用户和长链接的对应关系,连接层就是长链接,并且包含了长链接的安全校验,不过他这里实现的推送和连接层不是用消息中间件这种事务触发,而是告知/拉取模式
参考:http://www.52im.net/thread-1236-1-1.html
京东京麦商圈:京东京麦整体推送设计结构划分为五块,感觉跟我们的设计还是很有共同点的:
第一部分:消息接入层:京麦消息包含业务系统类消息、服务资讯类消息以及其他各类消息类型,消息来源多种多样;当时为了快速的接入各种消息源,提供了servlet接入、client接入、JMQ接入等,接入方式多样化,加上没有完善的监控系统,这样就导致了一个很尴尬的问题,我们自己都不清楚我们的消息系统到底接入了多少种类型的消息;
改进:
1)所有的系统消息统一由Anycall(这应该是他们自己的一个什么系统名)进行接入,清晰化消息类型边界;
2)京麦消息的接入方式统一:所有京麦消息统一通过JMQ异步化接入,并且根据不同业务通过不同的topic进行隔离,避免数据量大的业务(比如订单消息)对其他业务的阻塞;
原设计结构图及弊端分析:
如上图所示,原先京麦消息推送的主要痛点如下:
- 1)接入方式不统一;
- 2)不稳定、大促被降级;
- 3)消息处理逻辑复杂,接入新的消息源困难;
- 4)没有完善的消息追踪,消息统计。
新设计结构图
基于上述原因,重新打造了一个稳定、专一的消息处理中心——MC系统(如上图所示):
- 1)统一的JMQ接入,在上一部分已经介绍过了;
- 2)MC系统与其他系统没有耦合,不在存在由于消息量过大对京麦其他业务造成影响的问题,实现了在大促时可以提供稳定的服务;
- 3)MC系统使用了broker分发的模式:模块化可插拔的处理方式,使得新消息源的接入变的极其简单,大大的缩短了开发的周期。正是这种broker分发模式的存在,咚咚离线消息、ISV消息订阅等项目实现了快速接入,并提供服务;
- 4)在MC系统搭建的过程中,全链路消息追踪、消息统计也得到了实现(在第五节消息监控会详细讲解)。
全链路消息追踪系统,整合从消息源到最终的消息推送,整个链路各个节点消息的流转状况,并且异步化存储。从上图可以看到系统中的处理方式是,分别订阅JMQ的同一个topic实现将消息日志分别存储在ES和HBase,存ES保证了我可以在消息管理后台对所有消息进行清晰透明化的追踪查询,存HBase是为了可以将数据长久的保存并且进一步的分析。
消息统计是依托于京东大数据平台来实现的。将HBase里的数据导入到京东数据集市,从而对消息数据进行各个维度的统计分析。
京麦实时消息推送架松经过一年的成长,在稳定、监控、内容丰富程度上有了长足的发展。下一步的规划是完整的消息失败重试机制、提高消息送达率、消息推送产品化等。
消息过滤、消息组装、消息存储、消息推送是京麦消息中心的四大核心。消息组装是根据不同消息的不同配置来进行的,而这些配置是在开发侧的config配置中心来配置的,因此产品或者运营想从Anycall新接入一种系统消息所做的工作量是极其大的。
基于这个原因,我们将所有的配置环节统一到了一个页面。配置信息的获取添加三层缓存(Guava Cache+redis+DB)来应对海量调用。统一配置页面的存在使得业务类系统消息的接入变的简单快捷。
注:GuavaCache见:https://blog.csdn.net/guozebo/article/details/51590517
参考:http://www.52im.net/thread-1321-1-1.html
58到家:
整个系统的分层架构如上图(本文最重要的2张图之二),整个消息平台系统由:
- 消息平台在APP里的msg-sdk,向APP提供帅气的接口
- msg-gate,整个消息平台的tcp接入门户,保持tcp长连接,初步攻防,加解密,压缩解压缩
- msg-logic,整个消息平台逻辑处理的部分
- redis,高可用redis集群存储用户在线状态online/offline,以及用户在哪一台msg-gate接入(如果在线)
- DB,存储离线消息
对他这个设计,我不清楚的是,DB存储离线消息,客户端上线时,怎样及时获取?还有,包括上面几个系统设计也是,都有用户长链接位置和用户标识对应关系的缓存,那么这个用户长链接位置,是系统给每各节点配置了名字吗?应该不是,因为不方便扩展,那么怎么定位节点名的呢?
9:推送消息缓存集群:
对每个客户端,设置一个消息缓存队列,有客户端SDK拉取和删除消息
参考:http://www.360doc.com/content/17/1108/17/16915_702114710.shtml
我觉得这个应用场景应该是长链接不存在,无法下发时,消息的存储位置吧,客户端启动时,先到自己队列上拉取下消息;
10:自定义协议设计
这篇文章不错,可参考:http://www.52im.net/thread-298-1-1.html
其他:
Netty系列之Netty百万级推送服务设计要点:http://www.infoq.com/cn/articles/netty-million-level-push-service-design-points/
Mpush开源:https://www.cnblogs.com/ohun/p/5906591.html
极光推送技术博客:http://blog.jiguang.cn/category/tech/ (备注:不得不说,写文章的人很屌,一堆技术文章,看着都是干货,细细看下来,什么技术点架构设计都没有)
基于长连接的推送,集群,负载均衡:https://blog.csdn.net/cmdssd1/article/details/52195479