资讯中心

挂机宝:服务器推送技术常用的三个解决方案

  

挂机宝:到今天为上与app配合做接口的经验也有三年了,现在的app大多都有IM服务,在选择上无外乎有两种选择:一是自建IM服务,二是用第三方的云服务。

暂不表第三方的云服务如何如何。

在自建IM服务上,每家公司的选择往往逃不出下面的三个方案:

一是普通的http解决方案:app端通用http服务定时拉取消息,比例每隔3秒,虽然你和我可能都很鄙视这个方案,但确实有公司在用。

二是基于comet的解决方案(其实也是基于http):app端通过comet服务拉取消息,即app端发起一次http请求,然后服务端检查有无待接收的消息,如果有立即返回给app端,如果无,则把当前http请示挂起多少多少秒,如30秒,在这30秒内,如果他人给当前的app用户发送消息,服务端能在这30秒任意一点立即结束当前挂起的http请求,并把消息一起返回给app端。此方案我熟悉的有icomet服务。

三是socket解决方案:app端通过socket与服务端通信,目前比较常用的服务端socket解决方案有nodejs,swoole,workerman等等。一般游戏类app服务端和app端采用此方案的比较多。



在耗电量和耗流量上第一个是最耗电的,第二个次之,第三个是最优,但通过下面的设计方案,第二个方案和第三个在耗电量和耗流量上差别不大:主要理由是考虑到用户在线的时长及socket也要维持一套心跳服务上来推论。

我只介绍本人比较熟悉的基于icomet所支持的longpolling或HTTP Live Streaming的解决方案。

前提条件:服务器端先维护一自增的消息id服务,不管是点聊消息和群聊消息,自增id不要用同一套(主要是基于两者的消息量上来考虑),但所有用户的点聊都用同一套自增的针点聊的消息id服务,所有用户的群聊消息都用同一套自增的针群聊的消息id服务。这样每发送的每一条消息都有一个唯一的id。注意:点聊消息和群聊消息都只存储一条,哪怕一个群内有2000个群成员,在数据库中存储的也只有一条消息记录。

app端直接请求icomet服务(注意:app不是直接请求php或java或nodejs或其它服务),每个app端用户分配一个专用的icomet通道接收消息和提供长连接服务,由icomet服务来负责维持与app端的长连接。然后用php或java或其它语言负责往发送消息,还是以我熟悉的php为例,当php端接收到发送消息请求时,在处理完相关业务逻辑后,往消息接收方的icomet通道中推送消息(包括消息唯一id和具体消息内容等),这样如果app端用户在线的话,且还维持着icomet的长连接的话,就能立即接收到消息。app端接收到消息后处理完成后再发起下次长连接请求(注意:app端接收到消息后没有回调告诉服务器端已接收到,没有回调,没有回调!重要的事说三遍!)。

或许会有这样的需求:要支持查看历史消息的功能,即使是换终端设备了,也要能支持查看历史消息。此时该如何进一步优化?

很简单:由于消息id都是自增的,只需要另开一个http消息查询接口,按消息id的大小往前或往后查询即可。


服务器端IM消息存储方案:
由于IM消息写入和查询比较频繁,常用的无外乎这两种方案:存mongodb或存redis。至于是选择mongodb还是选择redis看各自的资金实力。单论两者的查询或写入的速度上,肯定都是redis快些,但两者的读写速度app端用户是感觉不到差别的。

写此博文的背景是回到机遇后发现定的新的socket方案流程太复杂,实在不敢恭维,但本人并不是反对socket方案,先看下机遇app的新的IM服务的大概的交互流程:

app端调用php写的接口实现发消息功能,php负责处理相关业务逻辑,并把消息写入mongodb和rabbitmq队列中写,然后由java与app端做的一套socket服务消费rabbitmq队列,来实现通知app端再调用php的消息查询接口去拉取消息。

即消息的通知流程为:app发消息给php,php往rabbitmq中写,java消费rabbitmq并告诉app端去拉取消息,app端再调用php的消息查询接口到mongodb中查询消息数据。

我反对上流程方案的主要原因是:如果查询为何某条点对点的消息为何这么慢才接收到,那么就得查:查下app端与php端的环节,再查下php与rabbitmq的环节,再查下java消费rabbitmq环节,再查下java与app端的socket服务环节,想想吧,到时是谁会疯?

?