移动端即时通讯系统实践

在信息高度发达的今天,IM基本上已经成为了一个社交应用的标配。本文将以一个移动开发者的视角,探讨移动端即时通讯系统的技术选型和关键要点。

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应用中,还有很多常用的实现需求,例如表情键盘,图片语音等多媒体的存储和下载队列等。但这不在系统实现的范畴中,将来后有文章进行详细阐述。
参考:
https://en.wikipedia.org/wiki/XMPP
http://www.blogjava.net/yongboy/archive/2014/02/15/409893.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • 前言 本文会用实例的方式,将iOS各种IM的方案都简单的实现一遍。并且提供一些选型、实现细节以及优化的建议。 注:...
    涂耀辉阅读 93,573评论 232 1,742
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 2016年,上班的第一天。一切都要全新开始。
    kind_lion阅读 100评论 0 0
  • 变量和常量 注释 Integers 类型别名 Bool //元组 可选型 Swift’s nil is not t...
    岁月蹉跎繁华落寞阅读 446评论 0 0
  • -1- 一位朋友上传到微信群一段本地视频,看得我身临其境,胆战心惊,手心冒汗。 视频显示,一辆黑色越野与一辆白色轿...
    荆向莹阅读 537评论 7 7