文档章节

移动端即时通讯系统实践

杭城小刘
 杭城小刘
发布于 2016/07/25 11:36
字数 2323
阅读 41
收藏 2

1、即时通讯系统的需求

任何技术系统都来源于真实业务的需求,做架构设计之前应该先设定好目标。作为一个即时通讯应用,可以参考微信的使用体验,你需要保证以下特性:
1、实时。消息的接收端应该能够及时收到并处理消息。
2、不丢。需要保证所有的消息都顺利送达。
3、不重。重复的消息对用户来说是一种糟糕的体验。
4、保序。只要顺序一乱,消息根本没发看。
5、节能。流量可贵,电量可贵,能省则省。
6、安全。如果涉及敏感数据,安全必须重视。
7、流畅。卡顿的应用是不会被用户接受的。

2、关键技术点

为了保证消息的实时性,有两种思路:
1、长轮询方式,高频率地从服务端拉取新消息。这种方式其实就是传统的请求-响应模型,现在很多体育文字直播软件也采取这种方式。这种方法虽然简单,但有很多缺点。一是会产生很多请求,这对服务器的压力和用户的流量都是浪费。二是消息仍然不够及时,不考虑传输时间,最长的延迟就是轮询的间隔。
2、消息的生产者主动推送消息。这应该是更好的选择,可以解决长轮询的缺点。我们的即时通讯系统也会采用这种方式。使用长连接,而且连接必须是稳定可靠的,才能确保消息的实时性。

2.1 数据通信协议

        服务端与客户端之间需要协商好数据格式,这是数据传输和数据处理的基础。协议的设计需要着重考虑第一节提到几点需求。
        XMPP和MQTT是当前比较成熟的两种消息协议。如果能较好地处理你的业务需求,就没有必要重复造轮子。有很多企业的业务有特殊需求,可以考虑根据实际情况自定义协议,从头开始显然是不现实的,可以参考已经成熟的协议再做设计和开发。具体协议内容在此不详细展开,只做优劣的比较。

2.1.1 XMPP

XMPP是一种以XML为基础的开放式即时通讯协议。
XMPP的优点是安全,SASL及TL等技术的可靠安全性已内置于核心XMPP技术规格中。
XMPP 协议的最主要的一点就是开放,不管是协议、客户端,还是 Server 端,都有成熟的实现方案。
基于XML,它天生拥有很强的灵活性,可以在核心协议之上方便地进行定制化。Google Talk就是采用这种协议。
但是XMPP的缺点也很明显。首先,XMPP协议的方式被编码为一个单一的长的XML文件,因此无法提供修改二进制数据。其次,XML 有大量的标签冗余信息,网络流量的 70% 都消耗在 XMPP 协议层了,这在移动互联网时代,流量和电量是一个不可忽视的消耗。

2.1.2 MQTT

        MQTT协议是由IBM提出的基于发布/订阅模型的消息传输协议,相比于XMPP,它显得非常轻量小巧,协议内容包括固定头部+可变头部+消息体,最下的情况下头部只需要两个字节,在传输开销上有着巨大的优势,可以节省流量和电量。
MQTT可以保证消息的可靠性,它包括三种不同的服务质量(最多只传一次、最少被传一次、一次且只传一次),如果客户端意外掉线,可以使用“遗愿”发布一条消息,同时支持持久订阅。
        XMPP使用XML,是一个历史的选择,在现在移动应用的场景下,个人更加推荐MQTT。据了解,不少企业,包括做IM SDK的厂商,也是在MQTT的基础上进行自定义的扩展和修改。

2.2 连接的稳定性

移动互联网的场景下,网络环境经常变化,需要保证连接是稳定的。

2.2.1 心跳
        最经典的做法就是使用心跳,实时地检测连接状态。通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。如果在一定时间内服务器没有响应,则认为连接可能已经断开,重新尝试连接。
伪代码如下:

while(true) {
    if (now - last_pong_msg > keep_alive) {
        socket_close();
        reconnect();
    }
    send_heartbeat_ping();
    // 只是为了表示每keep_alive时间段发一次心跳
    sleep(keep_alive);
}

       上述代码的心跳间隔是固定的。由于心跳包也是会消耗流量的,因此应该找到一个理想的心跳周期,在能敏锐地察觉连接变化的前提下,尽量大地增加周期间隔。因此可以做一个优化,是使心跳间隔动态增加。

2.2.2 多连接尝试
(1)多连接尝试
        考虑到不同地区不同网络运营商的情况下,用户可能因为网络限制,连接不上我们的服务或者比较慢。我们在实践中就发现,某些网络运营商将某些端口封禁了,导致部分用户连接不上服务。为了解决这个问题,可以提供多个ip和多个端口,客户端在连接某个ip比较慢的情况下,可以进行轮询,切换到一个更快的ip。
(2)长连接与短连接结合
        这只是一条退路,而不是常规武器。
在长连接实在连接不上的情况下,可以考虑做降级,使用短连接长轮询的方式进行替代。

2.3 服务质量

        系统的设计往往存在着取舍和妥协。正如TCP比UDP更加可靠,但它的负载会更高。
在即时通讯系统中也存在着取舍的问题,是追求极速送达,还是在传输上做可靠性的保证,确保不丢不重?不同的业务类型可能需要不同的服务质量,MQTT协议提供了三种服务质量,可以作为参考:
        QoS 0: 至多发送一次,发送即丢弃。没有确认消息,也不知道对方是否收到。针对的消息不重要,丢失也无所谓。
        QoS 1: 至少发送一次。发送之后,会等待接收方ack确认。在一定时间之内,如果没有收到ack,则会再发一次,一直到接收方收到。重发的消息会在头部有dup标示。这种QoS可以保证消息不丢,但接收方可能会有重复消息,需要做去重。如下图所示:

         QoS 2:有且仅有一次。可以保证不丢不重,但是通信压力高,需要多次握手。如下图所示:

3 客户端实现

3.1 消息处理

发送消息比较简单,只需要往某一个topic发布即可。
接收消息的流程如下:

        收消息:客户端需要保持一个长连接,并且确保连接稳定,如上章节所示。
        消息过滤:如果发送端不能确保消息不重(如mqtt中QoS为0或1),客户端需要做去重,因此消息需要有一个唯一的id。
        消息合并和分发:在实际使用场景中,往往有各种各样的消息类型(如聊天消息、系统通知等),对同一类型的消息可以做合并,以加快后续消息处理速度。而不同类型的消息则分发到各自的处理器当中,如存储到本地数据库,通知页面更新等)
        UI更新:客户端一般是使用列表来展示消息(iOS中是UITableView,Android中是ListView),而列表的数据量可能很大,数据源更新频率也可能很频繁,因此需要对列表做性能优化,以确保用户体验。以iOS为例,使用Instruments监控性能瓶颈的地方,对内存占用和CPU占用大户进行优化。确定问题后,常用的技巧有:对cell高度做缓存;简化UI层次结构;避免大量的离屏渲染;减少混合图层;等等。对症下药,各个击破。

3.2 其他

IM应用中,还有很多常用的实现需求,例如表情键盘,图片语音等多媒体的存储和下载队列等。但这不在系统实现的范畴中,将来后有文章进行详细阐述。

另外:关于Tcp三次握手和四次关闭握手有篇文章:http://blog.csdn.net/whuslei/article/details/6667471/讲的不错。

 

 

本文转载自:http://www.jianshu.com/p/718fd04d6cd9

共有 人打赏支持
杭城小刘
粉丝 14
博文 109
码字总数 54955
作品 0
杭州
iOS工程师
加载中

评论(1)

panda大侠
panda大侠
79
现代IM系统中聊天消息的同步和存储方案探讨

本文原作者:木洛,阿里云高级技术专家,内容有删减和修订,感谢原作者。 1、前言 IM全称是『Instant Messaging』,中文名是即时通讯。在这个高度信息化的移动互联网时代,生活中IM类产品已经...

JackJiang2011
2017/11/24
0
0
新手入门一篇就够:从零开发移动端IM

一、前言 IM发展至今,已是非常重要的互联网应用形态之一,尤其移动互联网时代,它正以无与论比的优势降低了沟通成本和沟通代价,对各种应用形态产生了深远影响。 做为IM开发者或即将成为IM开...

JackJiang-
2016/08/03
703
4
移动端IM中大规模群消息的推送如何保证效率、实时性?

本文原题为“大规模群消息推送如何保证实时性?”,来自瓜子二手车IM负责人:封宇,本次内容有修订,感谢原作者(原文链接在文末)。 1、编者注 众所周之,群聊是移动端IM的服务端技术难点所...

JackJiang2011
2017/11/20
0
0
移动端IM中大规模群消息的推送如何保证效率、实时性?

本文原题为“大规模群消息推送如何保证实时性?”,来自瓜子二手车IM负责人:封宇,本次内容有修订,感谢原作者(原文链接在文末)。 1、编者注 众所周之,群聊是移动端IM的服务端技术难点所...

JackJiang-
2017/11/20
158
0
如何选择即时通讯应用的数据传输格式

前言 即时通讯应用(包括IM聊天应用、实时消息推送应用等)开发的前期技术选型时,关于数据传输格式的选择,在即时通讯开发者同行的眼里,是个极富争议话题。 精略分析一下,大概的原因在于:...

JackJiang-
2016/08/04
372
1

没有更多内容

加载失败,请刷新页面

加载更多

Linux命令备忘录: jobs 显示Linux中的任务列表及任务状态命令

jobs命令用于显示Linux中的任务列表及任务状态,包括后台运行的任务。该命令可以显示任务号及其对应的进程号。其中,任务号是以普通用户的角度进行的,而进程号则是从系统管理员的角度来看的...

开元中国2015
45分钟前
1
0
springboot Whitelabel Error Page(Not Found)解决方案

当出现上图图的错误时注意 报错信息 There was an unexpected error (type=Not Found, status=404). Not Found代表未访问到资源 解决方案:比较访问路径和代码的路径有没有写错 正确的访问路...

斩神魂
45分钟前
1
0
记一次hbase master停止服务的原因以及恢复

在Hdfs空间不足的情况下,拒绝写入,hbase会down掉。如果hdfs空间没有清理的情况下,重新启动hbase,会报splitlog失败,原因是wal日志重写过程中会写hdfs,写不进去导致的。重启不成功。 解决...

PageYi
48分钟前
1
0
如何从平面设计转行到UI设计?

时代的变迁,科技的进步,工具的发展,薪资的差距,促使许多人转行的原因,但平面与界面两者之间有着哪些的差异呢?如果,想要转行又该具备哪些条件呢? 平面、界面设计之间的差异性 平面设计...

mo311
51分钟前
4
0
线程池整理

一般在生产环境中,我们都不会直接new一个Thread,然后再去start(),因为这么做会不断频繁的创建线程,销毁线程,过大的线程会耗尽CPU和内存资源,大量的垃圾回收,也会给GC带来压力,延长GC停顿时间...

算法之名
53分钟前
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部