原文地址:https://github.com/ChenYilong/iOSBlog
演讲视频(上下两部,时长将近 2 个半小时)以及 PPT 下载:链接: http://pan.baidu.com/s/1i5oH6LZ 密码: 4ayq
2016 年 9 月份我参加了 MDCC2016 (中国移动开发者大会),
在 MDCC2016 上我做了关于 IM 相关分享,会上因为有 50 分钟的时间限制 ,所以有很多东西都没有展开,这篇是演讲稿的博文版本,比会上讲得更为详细。有些演讲时一笔带过的部分,在文中就可以展开讲讲。
注:
我现在任职于 LeanCloud (原名 AVOS
) 。 LeanCloud 是国内较早提供 IM 服务飞 Paas 厂商,提供 IM 相关的 SDK 供开发者使用,现在采纳我们 IM 方案的 APP 有:知乎 Live 、掌上链家、懂球帝等等,在 IM 方面也积累了一些经验,这次就在这篇博文分享下。
IM 系列文章分为下面这几篇:
本文是第一篇。
[应用场景]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#应用场景)
[IM 发展史]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#im-发展史)
[大家都在使用什么技术]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#大家都在使用什么技术)
[社交场景]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#社交场景)
[直播场景]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#直播场景)
[数据自动更新场景]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#数据自动更新场景)
[电梯场景(假在线状态处理)]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#电梯场景假在线状态处理)
[技术实现细节]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#技术实现细节)
[基于 WebSocket 的 IM 系统]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#基于-websocket-的-im-系统)
[更多]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#更多)
[性能调优 -- 针对移动网络特点的性能调优]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#性能调优----针对移动网络特点的性能调优)
[极简协议,传输协议 Protobuf]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#极简协议传输协议-protobuf)
[在安全上需要做哪些事情?]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#在安全上需要做哪些事情)
[防止 DNS 污染]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#防止-dns-污染)
[账户安全]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#账户安全)
[重连机制]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#重连机制)
[使用 HTTP/2 减少不必要的网络连接]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#使用-http2-减少不必要的网络连接)
[设置合理的超时时间]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#设置合理的超时时间)
[图片视频等文件上传]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#图片视频等文件上传)
[使用缓存:类似 E-Tag 的本地消息缓存校验]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /IM%20 即时通讯技术在多应用场景下的技术实现,以及性能调优( iOS 视角).md#使用缓存类似-e-tag-的本地消息缓存校验)
思考几个问题:
一个 IM 服务最大的价值在于什么?
可复用的长连接。一切高实时性的场景,都适合使用 IM 来做。
比如:
下文会挑一些典型的场景进行介绍,并涉及到技术细节。
基本的发展历程是:轮询、长轮询、长连接。
挑一些代表性的技术做下介绍:
一般的网络请求:一问一答
轮询:频繁的一问一答。
长轮询:耐心地一问一答
一种轮询方式是否为长轮询,是根据服务端的处理方式来决定的,与客户端没有关系。
短轮询很容易理解,那么什么叫长轮询?与短轮询有什么区别。
长轮询和短轮询最大的区别是,短轮询去服务端查询的时候,不管服务端有没有变化,服务器就立即返回结果了。而长轮询则不是,在长轮询中,服务器如果检测到库存量没有变化的话,将会把当前请求挂起一段时间(这个时间也叫作超时时间,一般是几十秒)。在这个时间里,服务器会去检测库存量有没有变化,检测到变化就立即返回,否则就一直等到超时为止。
长轮询曾被 Facebook 早起版本采纳:
HTML5 WebSocket: 双向
参考: What are Long-Polling, Websockets, Server-Sent Events (SSE) and Comet?
我们可以看到,发展历史是这样:从长短轮询到长短连接,使用 WebSocket 来替代 HTTP 。
长短轮询到长短连接的区别主要有:
在移动端上长连接是趋势。
其最大的特点是节省 Header 。
轮询与 WebSocket 所花费的 Header 流量对比:
如何测试:
假设 Header 是 871 字节,
我们以相同的频率 10W/s 去做网络请求, 对比下轮询与 WebSocket 所花费的 Header 流量:
Header 包括请求和响应头信息。
出于兼容性考虑,一般建立 WebSocket 连接也采用 HTTP 请求的方式,那么从这个角度讲无论请求如何频繁,都只需要一个 Header 。
并且 Websocket 的数据传输是 frame 形式传输的,帧传输更加高效,对比轮询的 2 个 Header ,这里只有一个 Header 和一个 frame 。
而 Websocket 的 Frame 仅仅用 2 个字节就代替了轮询的 871 字节!
相同的每秒客户端轮询的次数,当次数高达 10W/s 的高频率次数的时候, Polling 轮询需要消耗 665Mbps ,而 WebSocket 仅仅只花费了 1.526Mbps ,将近 435 倍!!
数据参考:
下面探讨下长连接实现方式里的协议选择:
最近做了两个 IM 相关的问卷,累计产生了 900 多条的投票数据:
注:本次投票是发布在微博 @iOS 程序犭袁 ,鉴于微博关注机制,本数据只能反映出 IM 技术在 iOS 领域的使用情况,并不能反映出整个 IT 行业的情况。
下文会对这个投票结果进行下分析。
协议如何选择?
IM 协议选择原则一般是:易于拓展,方便覆盖各种业务逻辑,同时又比较节约流量。后一点的需求在移动端 IM 上尤其重要。常见的协议有: XMPP 、 SIP 、 MQTT 、私有协议。
我们这里只关注前三名,
名称 | 优点 | 缺点 -------------|-------------|------------- XMPP | 优点:协议开源,可拓展性强,在各个端(包括服务器)有各种语言的实现,开发者接入方便; | 缺点:缺点也是不少, XML 表现力弱、有太多冗余信息、流量大,实际使用时有大量天坑。 MQTT | 优点:协议简单,流量少;订阅+推送模式,非常适合 Uber 、滴滴的小车轨迹的移动。 | 缺点:它并不是一个专门为 IM 设计的协议,多使用于推送。 IM 情景要复杂得多, pub 、 sub ,比如:加入对话、创建对话等等事件。 私有协议 | 市面上几乎所有主流 IM APP 都是是使用私有协议,一个被良好设计的私有协议优点非常明显。优点:高效,节约流量(一般使用二进制协议),安全性高,难以破解;| 缺点:在开发初期没有现有样列可以参考,对于设计者的要求比较高。
一个好的协议需要满足如下条件:高效,简洁,可读性好,节约流量,易于拓展,同时又能够匹配当前团队的技术堆栈。基于如上原则,我们可以得出: 如果团队小,团队技术在 IM 上积累不够可以考虑使用 XMPP 或者 MQTT+HTTP 短连接的实现。反之可以考虑自己设计和实现私有协议,这里建议团队有计划地迁移到私有协议上。
这里特别提一下排名第二的 WebSocket ,区别于上面的聊天协议,这是一个传输通讯协议,那为什么会有这么多人在即时通讯领域运用了这一协议?除了上文说的长连接特性外,这个协议 web 原生支持,有很多第三方语言实现,可以搭配 XMPP 、 MQTT 等多种聊天协议进行使用,被广泛地应用于即时通讯领。
最大的特点在于:模式成熟,界面类似。
我们专门为社交场景开发的开源组件: ChatKit-OC , star 数, 1000+。
ChatKit-OC 在协议选择上使用的是 WebSocket 搭配私有聊天协议的方式,在数据传输上选择的是 Protobuf 搭配 JSON 的方式。
项目地址:ChatKit-OC
下文会专门介绍下技术实现细节。
一个演示如何为直播集成 IM 的开源直播 Demo :
项目地址:LiveKit-iOS
(这个库,我最近也在优化,打算做成 Lib ,支持下 CocoaPods 。希望能帮助大家快速集成直播模块。有兴趣的也欢迎参与进来提 PR )
LiveKit 相较社交场景的特点:
这些场景比聊天要简单许多,仅仅涉及到监听对象的订阅、取消订阅。 正如上文所提到的,使用 MQTT 实现最为经济。用社交类、直播类的思路来做,也可以实现,但略显冗余。
iOS 端的假在线的状态,有两种方案:
双向 ping-pong 机制:
Message 在发送后,在服务端维护一个表,一段时间内,比如 15 秒内没有收到 ack ,就认为应用处于离线状态,先将用户踢下线,然后转而进行推送。这里如果出现,重复推送,客户端要负责去重。将 Message 消息相当于服务端发送的 Ping 消息, APP 的 ack 作为 pong 。
使用 APNs 来作聊天的优缺点:
优点:
缺点:( APNs 的缺点)
APNs 不保证消息的到达率,消息会被折叠:
你可能见过这种推送消息:
这中间发生了什么?
当 APNs 向你发送了 4 条推送,但是你的设备网络状况不好,在 APNs 那里下线了,这时 APNs 到你的手机的链路上有 4 条任务堆积, APNs 的处理方式是,只保留最后一条消息推送给你,然后告知你推送数。那么其他三条消息呢?会被 APNs 丢弃。
有一些 App 的 IM 功能没有维持长连接,是完全通过推送来实现的,通常情况下,这些 App 也已经考虑到了这种丢推送的情况,这些 App 的做法都是,每次收到推送之后,然后向自己的服务器查询当前用户的未读消息。但是 APNs 也同样无法保证这四条推送能至少有一条到达你的 App 。
为什么这么设计? APNs 的存储-转发能力太弱,大量的消息存储和转发将消耗 Apple 服务器的资源,可能是出于存储成本考虑,也可能是因为 Apple 转发能力太弱。总之结果就是 APNs 从来不保证消息的达到率。并且设备上线之后也不会向服务器上传信息。
现在我们可以保证消息一定能推送到 APNs 那里,但是 APNs 不保证帮我们把消息投递给用户。
即使搭配了这样的策略:每次收到推送就拉历史记录的消息,一旦消息被 APNs 丢弃,这条消息可能会在几天之后受到了新推送后才被查询到。
让服务端负载过重:
APNs 的实现原理决定了:必须每次收到消息后,拉取历史消息。这意味着你无法控制 APP 请求服务端的频率,同一时间十万、百万的请求量都是可能的,这带来的负载以及风险,有时甚至会比轮询还要大。
参考:[《基于 HTTP2 的全新 APNs 协议》]( https://github.com/ChenYilong/iOS9AdaptationTips/blob/master/基于 HTTP2 的全新 APNs 协议 /基于 HTTP2 的全新 APNs 协议.md)
结论:如果面向的目标用户对消息的及时性并不敏感,可以采用这种方案。比如社交场景。(比如:专门为情侣间使用的 APP 。。。)
###基于 WebSocket 的 IM 系统
WebSocket 简介
WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebSocket 通信协定于 2011 年被 IETF 定为标准 RFC 6455 , WebSocket API 被 W3C 定为标准。
在 WebSocket API 中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
只从 RFC 发布的时间看来, WebSocket 要晚很多, HTTP 1.1 是 1999 年, WebSocket 则是 12 年之后了。 WebSocket 协议的开篇就说,本协议的目的是为了解决基于浏览器的程序需要拉取资源时必须发起多个 HTTP 请求和长时间的轮训的问题而创建的。可以达到支持 iOS , Android , Web 三端同步的特性。
文章较长,单独成篇。: [《技术实现细节》]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /技术实现细节.md )
注:本次投票是发布在微博 @iOS 程序犭袁 ,鉴于微博关注机制,本数据只能反映出 IM 技术在 iOS 领域的使用情况,并不能反映出整个 IT 行业的情况。
使用 ProtocolBuffer 减少 Payload
微信也同样使用的 Protobuf 协议(经过改造的)。
采用高效安全的私有协议,支持长连接的复用,稳定省电省流量
高性能,序列化、反序列化、创建综合性能高。
如何测试:
对数据分别操作 100 次, 1000 次, 10000 次和 100000 次进行了测试,
纵坐标是完成时间,单位是毫秒,
反序列化 | 序列化 | 字节长度 -------------|-------------|------------- | |
数据来源。
数据来自:项目 thrift-protobuf-compare,测试项为 Total Time ,也就是 指一个对象操作的整个时间,包括创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程。从测试结果可以看到 Protobuf 的成绩很好.
缺点:
可能会造成 APP 的包体积增大,通过 Google 提供的脚本生成的 Model ,会非常“庞大”, Model 一多,包体积也就会跟着变大。
如果 Model 过多,可能导致 APP 打包后的体积骤增,但 IM 服务所使用的 Model 非常少,比如在 ChatKit-OC 中只用到了一个 Model ,对包体积的影响微乎其微。
在使用过程中要合理地权衡包体积以及传输效率的问题,据说去哪儿网,就曾经为了减少包体积,进而减少了 Protobuf 的使用。
文章较长,单独成篇。: [《防 DNS 污染方案.md 》]( https://github.com/ChenYilong/iOSBlog/blob/master/Tips/基于 Websocket 的 IM 即时通讯技术 /防%20DNS%20 污染方案.md )
IM 服务账号密码一旦泄露,危害更加严峻。尤其是对于消息可以漫游的类型。
介绍下我们是如何做到,即使是我们的服务器被攻破,你的用户系统依然
无侵入的权限控制: 与用户的用户帐号体系完全隔离,只需要提供一个 ID 就可以通信,接入方可以对该 ID 进行 MD5 加密后再进行传输和存储,保证开发者用户数据的私密性及安全。
对关键操作,支持第三方服务器鉴权,保护你的信息安全。
参考: 《实时通信服务总览-权限和认证》
让 APP 支持单点登录,能有限减少盗号造成的安全问题。在 ChatKit-OC 中,我们就默认开启了单点登录功能,以此来提升 APP 的安全性。
当然,这样灵活的策略也同样决定了,只能在 APP 层进行心跳 ping 。
这里有必要提一下重连机制的必要性,我们知道 TCP 也有保活机制,但这个与我们在这里讨论的“心跳保活”机制是有区别的。
TCP 保活( TCP KeepAlive 机制)和心跳保活区别:
TCP 保活 | 心跳保活 -------------|------------- 在定时时间到后,一般是 7200 s ,发送相应的 KeepAlive 探针。,失败后重试 10 次,每次超时时间 75 s 。(详情请参见《 TCP/IP 详解》中第 23 章) | 通常可以设置为 3-5 分钟发出 Ping 检测连接的死活(对应于下图中的 1 ) | 检测通讯双方的存活状态(对应于下图中的 2 )
保活,究竟保的是谁?
比如:考虑一种情况,某台服务器因为某些原因导致负载超高, CPU 100%,无法响应任何业务请求,但是使用 TCP 探针则仍旧能够确定连接状态,这就是典型的连接活着但业务提供方已死的状态,对客户端而言,这时的最好选择就是断线后重新连接其他服务器,而不是一直认为当前服务器是可用状态,一直向当前服务器发送些必然会失败的请求。
大多数的移动网络(3G)并不允许一个给定 IP 地址超过两个的并发 HTTP 请求,既当你有两个针对同一个地址的连接时,再发起的第三个连接总是会超时。而 2G 网络下这个限定为 1 个。同一时间发起过多的网络请求不仅不会起到加速的效果,反而有副作用。
另一方面,由于网络连接很是费时,保持和共享某一条连接就是一个不错的选择:比如短时间内多次的 HTTP 请求。
使用 HTTP/2 就可以达到这样的目的。
HTTP/2 是 HTTP 协议发布后的首个更新,于 2015 年 2 月 17 日被批准。它采用了一系列优化技术来整体提升 HTTP 协议的传输性能,如异步连接复用、头压缩等等,可谓是当前互联网应用开发中,网络层次架构优化的首选方案之一。
HTTP/2 也以高复用著称,而且如果我们要使用 HTTP/2 ,那么在网络库的选择上必然要使用 NSURLSession 。所以 AFN2.x 也需要升级到 AFN3.x.
过短的超时容易导致连接超时的事情频频发生,甚至一直无法连接,而过长的超时则会带来等待时间过长,体验差的问题。就目前来看,对于普通的 TCP 连接 30 秒是个不错的超时值,而 Http 请求可以按照重要性和当前网络情况动态调整超时,尽量将超时控制在一个合理的数值内,以提高单位时间内网络的利用率。
图片格式优化在业界已有成熟的方案,例如 Facebook 使用的 WebP 图片格式,已经被国内众多 App 使用。
分片上传、断点续传、秒传技术、
微信是不用考虑消息同步问题,因为微信是不存储历史记录的,卸载重装消息记录就会丢失。
所以我们可以采用一个类似 E-Tag 、 Last-Modified 的本地消息缓存校验机制,具体做法就是,当我们想加载最近 10 条的聊天记录时,先将本地缓存的最近 10 条做一个 hash 值,将 hash 值发送给服务端,服务端将服务端的最近十条做一个 hash ,如果一致就返回 304 。最理想的情况是服务端一直返回 304 ,一直加载本地记录。这样做的好处:
IM 系列文章分为下面这几篇:
本文是第一篇。
Posted by 微博 @iOS 程序犭袁
原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
1
yepinf 2016-11-02 22:42:38 +08:00
赞一个。
|
2
hanangellove 2016-11-04 13:19:40 +08:00
生猛·
|
3
programdog 2016-11-04 16:12:14 +08:00
强烈赞一个
|
4
iOran 2016-11-09 23:23:09 +08:00
好文啊,居然没人赞,不合理。
|