1. 卓聘IM开发背景
智联卓聘是智联旗下高端人才招聘平台,成立快4年了,业务增涨每年以100%速度增涨,业务增涨快在开发和上线速度要求也比较高。
2016年6月提出IM开发需求,7月初上线,开发人员三名,开发时间20多天,后期可以不断满足业务需求。前期阶段我们考虑网上各种提供IM的云平台,这些平台都有一个问题,聊天记录管理上,有着各种限制和不方便,所以我们决定自己去完成一个。
一个完整的IM,需考虑通信协议和传输协议。通信协议目前XMPP、MQTT ...传输协议TCP、HTTP,下面我就从前期技术选择和我们自己在IM开发中遇到问题和如何解决做一些描述。
2. IM 技术选型
IM在选择上,更多我们需要考虑传输协议与通信协议使用。一个实时不经常掉线的IM必须有具有稳定可靠通信。
通信协议无非是在UDP、TCP、HTTP 选择。传输协议最多使用XMPP 它是基于XML,每次一大段XML Post到服务端。如果是PC还好,手机端让人抓狂。MQTT反而短小,更适合当下,易用、耗能少。不过目前还没找到使用MQTT协议像Openfire一样强大的开源项目,就是一但出了问题我们风险无法控制。
以下就是经常被用于IM的相关选择。
2.1 Openfire
在Openfire 之前,IM开发者们,一边考虑着通信协议,一边考虑使用什么样传输协议,一切从0开始做起,从Openfire开始,IM开发者发现开发IM简单很多,只要把XMPP协议了解了都可以快速开发,无论你是Web、还是手机,都有着不错开源项目可用。效率也高,后面使用了Mina,想要功能直接写插件、XMPP协议也适应了当时流行的XML。但这一切只是暂时的。
为了保证通信稳定与安全XMPP协议XML冗余太长了,我们也有了更高效的Netty可用,关系性数据库变的不再适合存储这类数据。 XMPP要传一张图变的很痛苦。
2.2 WebSocket
像socket一样提供了一组API,它基于了HTPP协议,它并不是一套完整IM解决方案。在HTTP熟悉的协议中多了Upgrade: websocket,Connection: Upgrade这二项,你的Nginx和新版Tomcat对这二项会有支持。最重要二个函数一个就是Send方法,一个就是Receive,简单像是我们回到了HTTP Request和Response。可是最大问题在低版本浏览器上不能使用。
2.3 长轮询
谈到长轮询就不得不说Comet的技术,使的HTTP协议有了主动推送机制。Comet本身有长连接与长轮询二种方式。但是长连接在IE、 Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。长轮询本身技术很简单,是利用HTTP超时机制,重发,看起来像是与客户端建立了个永久连接。现在的开源有Comet4j、CometD。
缺点:消息实时性差,每打开一个网页都要保持一个连接,对服务器资源消耗大,发一条信息速度还好,如果是一张图或者大量信息接收时,都会直接影响服务器资源,在手机上使用不稳定。
优点:实现简单、部署成本低、兼容性好,只要可以HTTP请求各种平台都可以实现,开源多相关解决方案成熟,一但出现问题我们可以第一时间解决。这是最重要的。
在开始做IM之前,我们有几个需要考虑的自身业务特点需要考虑。
首先,我们需要兼容全部浏览器。
让我们在客户端实现方式并不多,WebSocket好用简单,可PC低版本浏览器不能使用。但在手机上又有着广泛的支持。Openfire 有Smack可它的协议冗余太长,要是在客户端重新定义再到服务器端转化,效率不高。要是使用长轮询如何避免用户打个过多页,白白消耗服务器资源,使用了HTTP协议如何保持它的状态和集群搭建也是问题。
可以同时在线多种客户端,离线后可推送。
手机App、PC浏览器也可以同时在线、同时收发信息,我们既要可以在浏览器上使用像长轮询这样方式,同是也可以在手机上使用WebSocket这样稳定收发方式。App发PC收,或PC发App收取。而且需要在App退出后,又可以使用推送平台用户手机上。
3. 卓聘聊聊架构
在分析了各种方案优缺点、根据我们目前业务需求和后期扩展,我们将架构做了如下设计。
通知服务可以随时上线、下线,动态注册在路由规则器中,前端根据需要或长轮询、或WebSocket,在路由规则器获取双方(自己和对方)ID,在通过REST服务发送的数据是带有ID的报文。通知服务在推给推送服务时,推送服务计算“规则”就可准确推送给指定客户。客户接到通知,再次通过REST服务拉取具体内容。
架构虽然简单,可同时也遇到不少问题,以下就是架构详细介绍及遇到问题和改进方式。
4. 存在问题及改进方式
4.1 用户频繁在线打开N多页面产生大量长轮询
用户浏览器可以多开,同一用户打开不同浏览器、同一用户每开一个浏览器打开N多个页面。每个一个页,都会多产生一个连接,长轮询很消耗服务资源,在CometD代码中,就发现同一用户会限制长轮询个数,这是必要的,安全和减少服务器资源。 但是不能解决实际问题。 每个用户会打开很多页签,这是用户习惯问题,我们无法限制。而同过观察用户行为,平时我们IM在收起状态时,在整个页面右下角。(见图一),用户本身可能不会主动去操作IM工具,如果能减少这样不必要连接可以减少50%-60%无用长轮询,但是有二点要解决。用户不主动点击登录IM。
1、你的好友可以看到你在线 。
2、你如何收到你的好友推给你的消息
图一、
我们采用了虚拟登录,你没有展开时不去开启长轮询,只有点击展开窗口才真正的长轮询。
1、用户页面加载时,发出你登录信息,同过心跳保持在线状态
2、你的好友推你信息时,我们通过短轮询拉取,我们采用 Nginx+Lua+Redis ,这样请求不会打到后面服务。
3、可以将上面二个步骤做成一个连接,减少连接数。
4.2 发送数据大小与接收速度
最早开始做时我们对IM了解比较少,做到一半时测试时,输入内容越多或随着业务发展,要求发表情,图文时,会随着长度增加接收和发送会有影响,后来同过学习和观察发现,IM需要推拉同时使用,这也是IM普遍做法,可介绍的人比较少 见下图。好处显而易见,推送时解析协议获取到一个个短小命令,而客户端接到命令再去主动拉去实际内容。
1、发送客户端把发送内容发到指定Web服务器。
2、Web服务器收到后,生成相应指令,和具体发送内容。
3、分别把指令传送到接收服务器,内容直接存放到数据库中
4、接收服务收到指令后发送到后面推送服务。
5、推送服务推到指定用户。
6、接收用户收到指令后,再通过拉取,从数据库中拉到实际内容。
4.3如何集群
Web集群很简单,因为是无状态的,用户每次请求都会被负载到不同服务器,不会有问题,而IM麻烦就在状态性,你不可能简单把它负载到不同服务器,我们使用Hash算法可以将同一用户会被分到一台指定服务器,同时又出现其它问题,跨服务器消息传递,如A用户(AM)机器向B用户(BM)机器 ,实现如下图。
路由选择器保存我们全部用户及用户连接,我们可以很轻松知道,每个用户具体指向的推送服务器。
1、用户在登录时在路由规则器注册自己。
2、当A用户发向B用户信息时,通过路由规则器找到B对应的推送服务
3、推送服务相应信息通过Comet推送给指定客户。
4.4 后续迁移
由于前期时间短和业务要求,我们很快上线,可我们要考虑后期代码重构,业务在增长,重构时我们不是推翻重来,我们考虑仅替换瓶颈服务。所以我们将发送服务和推送服务分开,(见下图)发送接口和定义协议不会变(变的成本更高),我们要变的是,可以方便增加或替换推送模块。图中红色区域就是可以随时被我们替换和增加的,黄色通信协议模块相对稳定。
1、发送者发送数据时,我们会把内容写到数据库把命令发到接收服务中。
2、命令接收服务可是一个消息中间件,也可以是自己写一个Netty服务去解析。
3、把解析后结果再次发到推送服务,推送服务在PC上可能它是一个Web服务,也可能是WebSocket服务,也可以是第三方平台。当然也可以二种或多种同时存在。
5. 总结与未来方向
IM是一种时实要求高的系统,最大问题是用户是否可以正常登录,一但登录,就可以收发信息,而当用户量和用户同时在线交流不断增加时,集群的建设和收发速度都是影响用户体验的重要因素。IM中每个用户状态,信息敏感度都是必不可少监控项。目前我们也在不断优化自动扩容、风险监控这些方向。希望给猎头、企业HR、应聘者提供更好交流方式。