过去的十五年,我们怎样做 IM?
世界变了,即使同样的场景和需求,在每个时代使用的技术做的事情都可能完全不一样。
虽然用户需求越来越高,人力成本越来越高,研发一个行业水准的系统也越来越难。但要不要从头自建,依然受到很多因素的影响。
阅读本文,了解过去十五年里,IM 软件研发走过的三个阶段,了解系统选型背后的思考,了解新一代云原生 IM。
就在前几天,飞信要关停了,消息传出,很多人感慨说「又一滴时代的眼泪」,言语唏嘘。
飞信诞生于 2007 年,迄今已经运营了 15 年。由于出现时恰逢国内互联网建设初期,网络速率较低且资费昂贵,大部分即时通讯软件事实上还只能作为半留言式工具,这让与短信结合的飞信在消息投递方面的即时性无人能及,顺理成章地迎来了爆发式的增长。
只不过这种优势并没有保持多久,随着网络建设和资费下调,红利很快就开始消退,并最终在移动互联网崛起后彻底失去了机会。
巧的是,笔者也是从 2007 年开始做 IM,算来也有十五年。因此除了跟其他用户一样感慨外,也会有行业从业者的不同角度和观点。特别是最近几年一直做 IM 云服务 ,看过数以万计的 APP,这种情况也是见怪不怪。
除此之外,其实我还会经常遇到的一个问题是:
IM 不就是聊天吗,技术发展这么多年应该成熟了吧,还有什么可做的呢?
所以今天我们不谈产品,来谈一个技术问题,我们怎样实现一个即时通讯系统(IM)。
过去的十五年里,即时通讯(IM)技术大致经历了三个阶段。
第一阶段:软件和高并发服务
网络刚刚兴起的时代,开发一个软件系统要从买电脑租服务器机架开始。由于没有云服务和现成的SDK 技术,工作会往往从开源的协议和软件开始。
这也是属于 PC 互联网的时代,网络虽然不快但相对稳定,扩展性和互操作性相对重要,也因此发布于 2000 年的 XMPP 协议逐渐成为标准。
在这个以研发软件为主的时期,互联网用户开始增长,越来越多的服务开始遇到大量用户带来的并发挑战。后端技术也随之不断发展,从解决 C10K 问题(承载一万并发)到 C100K、C1000K,与之相关的线程池、IO 模型优化等技术成为重点。
为了解决并发连接,大多数服务器都要确保切换 epoll,因为在 IO 多路复用的问题上,epoll 要比 select 要高效。而当用户数超过了单机容量,就要进行会话的分片管理,你要用一致性哈希这样的技术,确保在部分节点宕机情况下整体服务的可用性。
显而易见,这个阶段软件复用率很低,每一个需要 IM 的业务基本都要从头构建自己的系统,都要自行解决这些技术难题,导致开发周期漫长且低效。现在看来简直如刀耕火种般原始,但当时确实是常态,能够从开源软件开始构建已经是高阶开发做的事情了。
这种情况一直持续到后来的移动互联网时期,直到云的出现。
第二阶段:移动互联网和云
2009 年中国正式发布 3G 牌照,标志着中国进入 3G 时代,而同年苹果发布了 iPhone 3GS 手机,开发生态也逐步完善,为随后到来的移动移动互联网爆发做好了铺垫。
3G 网络的成熟,为手机上网提供了便利,也为业务带来了新的挑战。因为手机终端的移动特性,导致用户在网络切换过程中会频繁断网重登录,以 XMPP 协议为代表的 IM 系统会因此耗费相当大的网络流量以及电量,而且还会丢失消息。
这个时期的 IM 系统,重点开始解决在移动网络下消息可靠投递的问题,以及省电省流量设计,很多公司开始使用二进制数据结构来实现自己的协议。
这个问题首先被微信解决了,这就是后来大家经常提的增量同步 XSync 协议。这种设计思想一般说是从邮箱协议启发而来,比如微软的 ActiveSYNC,但用在 IM 场景算是一个创新。
除了二进制包更小有效负荷更高外,比起传统的 XMPP 协议,开发者还会重点解决两个问题。
一个是消息投递确认机制改为显式 ACK。即把原来 XMPP 以消息离开服务端为投递成功,改为客户端收到消息后回复 ACK,服务端收到 ACK 才认为投递成功,失败的投递会触发重试,这样保证了消息投递的可靠性。
另一个是离线消息的存储与获取。原来 XMPP 协议有单独的离线存储设计,用户登录后会自动下发,而在线消息则是直接投递。前面提到,这两种情况下都会遇到的是,消息投递后如果网络切换或者不稳定,消息就会丢失。在新的显式 ACK 设计下,在线投递和离线投递合二为一,统一简化了处理。
也是部分因为这样的设计,部分因为用户体验,移动互联网后,大家不再强调在线和离线的状态,而是宣称永远在线。毕竟一个进了电梯断网的用户,下一秒就会上线,而手机一直带在身边,只要联网消息就会被取走。在线状态这个原来 IM 系统里最大的负担特性被舍弃了。
说到这里,你可能会觉得,移动互联网就是技术栈变了,好像要做的事情比之前还要多,因为协议甚至都没有现成的开源的方案可以用了。
事实并非如此,因为云的时代也到了。
大量移动 App 的爆发,带来了标准化模块化 SDK 的需求,因此为开发者服务的云服务应运而生。2014 年开始,市场上开始出现即时通讯云服务,为企业和开发者提供 IM SDK 和API 服务,我们是其中之一。
由于存在大量的机会和竞争,对于每一个移动互联网企业来讲,时间和人才变得更加宝贵。机会稍纵即逝,你要在最短的时间里跑通业务,便不可能在每个系统中都投入人力,集成 SDK 是最经济也是最明智的决策。
研发的专业化比如带来分工分化。因此,也就是从这个时期开始,研发 IM 功能的开发人员产生了分化。
一个群体是普通 App 开发者。他们的工作不再是解决 IM 系统的实现问题,而是开始集成现成的服务,使用标准的 API,将重心放在自己的业务上。
另一个群体是云服务提供方。他们开始研究客户端跨平台(iOS、安卓、Web 等)技术,解决更大规模的分布式系统问题,提供更高可用性的服务。当然在这之前,他们也要首先解决多租户问题,即大量应用使用同一个云服务,又有足够的隔离保证数据不会通不会乱。
随后对 IM 系统的更大挑战到来。2015 年,互联网直播开始流行,万人群甚至无上限聊天室等极端场景被提了出来,要知道在此之前,一般的群都是千人规模,微信群规模限制更是长期在 500 人左右。
如果要做万人群,甚至是无上限聊天室,消息的分发模型必须进行改造。因为每个用户发送消息默认都会被扩散到所有人,因此即使是万人群,每人发一条消息,系统就要承载一亿条的峰值压力,更别提百万人聊天室了。之前有个知乎提问说「把 14 亿中国人都拉到一个群里,技术上能实现吗」,算是一个很有意思的例子。
因此,服务实现时首先要将分发分批打散,分散到多台队列以及处理机上。这其中的每一步,都涉及一个更为基本的操作,即获取群成员列表,这样的压力下使得存储必须进入内存而且要分片才能解决。
客户端自然也存在群成员展示问题,毫无疑问这个列表也需要分片获取。然后你会发现,即使服务器能够下发成功,一个手机也无法同时处理一万条消息。因此,在无上限聊天室场景下,服务端在消息分发开始就需要区分消息优先级并实现消息丢弃策略。
这个阶段是云服务逐渐渗透的过程,因此我们还是会看到一些企业没有上云,这便是第三类开发者。
他们思考了自己的业务,发现业务有相对稳定的用户数,不会有用户突然爆发的情况,也没有极端的群聊需求,但同时,他们出于政策要求或者安全考虑,不放心聊天数据上云,因此他们还是会从头构建自己的 IM 系统。如前面所提,这种情况下,一般他们的 TCO 成本会远高于公有云,因为需要投入额外人力和独立的服务器,同时,他们在 IM 系统研发上投入以及服务质量也会跟专业的 IM 云服务有不少差距。
第三阶段:云原生
在云服务阶段,我们提到了三类开发者。其实在很长时间内,我们作为第二类云服务开发者,是相信未来大部分服务都是要上云的,即使不是全部的话。
然而随着越来越多场景的出现,这个观点也在逐渐改变。前面说的数据安全要求是一个原因,跟它类似的是网络安全要求。在某些业务里,IM 聊天要求内网通讯,或在特定专网内,这就使得租用一个公共的云服务变得不可行。
同时从云服务的角度看,云服务模式成立的基础在于规模效应,即通过统一服务提高资源使用效率,以低于客户自建服务成本的价格收取租用费,然后通过大量客户来获得利润。也就是说,每一个公有云服务的运行,需要有足够量的客户支撑。反之则不然。
也因此我们发现,在某些发展中国家或地区,当移动开发者和企业数量不足的时候,公有云服务实际上无法做到有效覆盖,使得在这些地区的开发者只能选择非云的当地 IDC 和技术服务。
在无法使用公有云服务的场景下,某些有足够预算的企业可以通过购买云厂商的私有部署来继续将自己的开发人员锁定在一类开发者中。这便是 IM 私有云的场景。
在过去,私有云的交付一直处在两个极端。系统集成商一般有自己的过时的相对陈旧的 IM 软件(由于客户群相同,我们暂且也称其为私有云)。它们分层抽象不足,性能差,业务灵活性不足,然而他们设计简单,交付工作量反而可控;与此同时,云厂商虽然有设计优秀的 SDK 和 API 服务,然而服务却也因承载大规模高并发而进行了各种拆分,导致私有部署复杂,交付困难,也因此定价昂贵。
这种情况一直持续到云原生技术的成熟。2015 年,随着 Kubernetes v1.0 发布, Cloud Native Computing Foundation(CNCF) 成立,并在随后几年逐渐成为容器编排的主流工具。各大云服务厂商也逐渐开始使用云原生技术来重构自己的服务,IM 服务也不例外。
借助于云原生技术,云服务厂商可以统一私有云和公有云的技术栈,在私有云或者自建服务器上进行服务部署不再是一件复杂的事情,交付实施成本得以大幅降低。
当然,要实现云原生,要解决两个重要的技术问题,也是技术难题。
首先是多云架构,也是前面提到的,必须统一公有云私有云接口、技术栈和管理控制台。做到这一点,不仅需要统一每一层的接口定义,更难的是,IM 系统多集群技术。在过去的互联网后端技术里,多集群技术,有时候会在异地多活或者单元化设计提到,是多数业务都不会涉及的工程难题。雪上加霜的是,多云架构下,多集群进一步被放大成了大量集群,且集群之间的网络环境是不稳定的公网链路。
但我们也都知道,最优的设计必然来源于对业务的深层理解。我们要实现的是 IM 系统的多集群,而不是某个通用服务的多集群。因此这里首先可以利用的业务特点是消息投递的一致性要求。并不是一般互联网服务的的最终一致性,而是消息发送的因果一致性。也就是说,IM 消息的有序,可以主要限定在某一对通话的人,发送者的消息要保证在回复者的消息前面显示即可。
因此要实现作为多集群数据核心服务的 ID 生成器,我们可以通过预先划分而不是中心服务的方式进行设计,典型的设计可参照 Snowflake 或 Ticktock,核心思想是将八个字节的 Long 型整数看成 64 位比特,约定特定位数的意义。这样就可以保证全系统跨地域生成的时候,ID 唯一且在时间上粗略有序。
第二个问题是自动化安装部署与 License 控制,在这个基础上如果有定制需求,再进一步完善版本管理和制品管理,这里也是用到云原生技术的地方。
通过容器技术,实现内置服务的持续集成和交付 CI/CD,实际上是制品的版本管理。其次借助 K8S 等编排工具,将所有服务以及模板整合成一个整体,进而将部署实施的过程变为在目标主机上安装 K8S 并拉取镜像启动服务。这样,就可以做到私有云的部署安装以及配置在极短的时间内完成。
顺便提一句, 这个时间在我们实际业务中的基准是十分钟。 超过了这个数量级的时间一般还是通过挤压运维操作时间来实现的,但要达到这个时间,有人参与的方式是不太可行的。
需要注意的是,这里面还有个前置条件是配置的服务化,这是很多系统实现时会忽略的地方。只有将配置服务化,所有的配置项可以从一个服务获取,而不是配置文件里读取,才能更好地支持动态更改与配置,也才能方便的通过编排工具来管理。
所以在这个阶段,云服务厂商将可以同时提供专业设计的 SDK 和低成本的私有云,根据现有的市场定价来看,云原生 IM 私有云的价格可以将成本降低两个数量级,即之前价格的百分之一,这样的价格下,客户使用 IM 的成本也会极大降低。
也因此根据我们的预测,这个阶段将会是第三类开发者,即不上云而坚持自建 IM 的开发者大量减少的阶段。
后记
在本质上,我是相信大多数软件技术都经历了类似的发展。只不过由于我个人经验所限,就以即时通讯(IM)技术为例。如果可以抛砖引玉,对各位所从事业务的软件开发或服务能有个启发,也算是一件开心的事。
2011 年,Marc Andreessen 在华尔街日报上发表文章说,软件正在吞噬世界。过去几年,云服务厂商一直想让客户在云和软件中选择,传统软件厂商则绞尽脑汁劝说客户不要上云,两者你来我往,势均力敌。
现在到了云原生时代,云服务终于又变回软件,客户也不再需要在「不上云」和「好产品」之间犹豫徘徊了,系统集成商和云服务厂商之间新的竞争和合作模式也开始逐渐形成。
因此,这必将也是一场软件交付的变革,让我们拭目以待吧。
关于「蓝莺 IM」
蓝莺IM 是由美信拓扑团队发布的新一代云原生 IM,专业 IM SDK,私有云也可按月付费。未来还会逐步开放这套云原生框架,成为所有 IM 相关应用的云原生通讯底座,让应用直接一步变成云原生服务,也可以重新变成快速分发和交付软件。
想了解更多关于蓝莺 IM 或者云原生服务设计的内容,可以继续阅读关于蓝莺 IM 的那些儿事:《 未来在非洲,每两台智能手机就有一台使用蓝莺IM技术 》。
蓝朋友计划的最新情报
我们正在以蓝莺 IM 开源项目为基础,打造一个专业的即时通讯技术社区,这便是我们的「蓝朋友计划」。此计划将会邀请社区技术专家一起,共同分享关于即时通讯(IM)技术相关内容,欢迎持续关注,也欢迎自荐或推荐。
另外,微博关注 @蓝莺 IM,参与文章转发,有机会获得「蓝莺 IM 礼盒」哦~
作者介绍
一乐,本名梁宇鹏,蓝莺 IM 创始人,TGO 鲲鹏会(北京)成员。原环信首席架构师、云通讯事业部总经理,也曾任新浪微博通讯技术专家,负责过微博聊天系统设计与研发,也负责当时微博平台研发的架构委员会。从开始使用 XMPP 协议,在工作中进行相关开源软件的研发,包括 C 语言的 Jabberd2、Erlang 的 EJabberd、Java 的 Openfire,到移动互联网后自己设计通讯协议并实现,已经做了 10 余年 IM,其中两款产品服务达到千万级同时在线。
- 那些 Go 语言发展历史上的重大决策
- 从趋势到挑战,一站式解读操作系统运维和可观测性
- 百万级 Topic,腾讯云的 Apache Pulsar 稳定性实践
- Apache Doris 在思必驰的应用优化实践:海量语音通话数据下,实时、离线一体的数仓架构设计实践
- 爱数正式开源认知智能开发框架 KWeaver
- 运维智能化的三大关键技术
- “抄我的还‘反捅’我一刀”,Gary Marcus 发文驳斥图灵奖得主 Yann LeCun
- 当出海成为必选项,企业如何构建全场景全生态技术底座?
- 数智底座必备能力三:快速构建创新应用
- Docker 多阶段构建实战 (multi-stage builds)
- 工作笔记之 SELECT 语句在 SAP ABAP 中的用法总结(上)
- 经久不衰的设计定律是不要让我思考的设计
- 不要指望下一个像 GPT 这样的大型语言模型会民主化
- Java 近期新闻:Helidon Níma、Spring Framework、MicroProfile、MicroStream、Kotlin 和 Piranha
- 一文入门 jQuery
- C 学习 ---__libc_open 函数的原理
- 监控系统工作原理
- 甲骨文新微服务框架 Helidon Níma:使用虚拟线程实现高性能
- 【云原生 | 从零开始学 Kubernetes】二、使用 kubeadm 搭建 K8S 集群
- Elasticsearch 聚合学习之四:结果排序