摘要:Facebook 的实时聊天架构每日可处理数十亿条消息。
作者 | shivang
译者 | 弯月,责编 | 郭芮
在这篇文章中,我将讨论Facebook的实时聊天架构,该架构每天可以处理数十亿条的消息。
Facebook的实时聊天架构后台使用了哪些技术?技术栈包括哪些?系统采用了怎样的架构?像Facebook一般拥有如此大量用户的平台在推出和扩展实时聊天功能时,主要面临的难题有哪些?
下面就让我们一起来看看吧。
引言
这一切都始于一场黑客马拉松,当时一群Facebook工程师写了一个聊天软件的原型,并在他们的团队面前进行了展示。虽然只有最基本的功能,但是浮动的聊天框可以在网页上拖动,即便页面重新加载和移动到其他页面,该聊天框依然会显示。
Facebook的工程师采纳了这个原型,然后将其发展成了全面的实时聊天功能,后来这就成了Facebook服务生态系统中使用最频繁的功能之一。
该聊天软件每天要处理全世界发送的数十亿条消息。社交平台的工程团队对其进行了良好地扩展,其响应时间不足100毫秒。
该功能不断得到改进,其唯一的目的就在于为用户提供一流的通信服务。
为什么要从头开始编写和维护聊天功能?为什么不集成第三方聊天服务?
除了基本的聊天功能外,Facebook的聊天模块还集成了社交图。用户可以轻松地获取他们的朋友列表,以及其他相关信息,比如他们正在玩的游戏和浏览的内容。
通常,平台上用户可用的所有信息也可以在聊天模块上访问。
与集成第三方代码相比,从头编写聊天功能更容易、更清晰、更安全且可以提供更多控制。
实时聊天架构与技术栈
Facebook聊天软件的整个系统由几个松散耦合的模块组成,这些模块彼此协同工作,例如Web层、用户界面、聊天记录、用户上线模块和频道集群。
用户界面
用户界面自然是用Java编写的,也利用PHP做了服务器端渲染。
客户端和服务器之间通过Ajax建立长期的持久连接。
该框架没有采用Flash的原因主要有两个:首先,使用Flash就会要求用户在浏览器中安装插件,这种用户体验不佳;其次,从安全角度来看,Flash不是首选。
消息获取流程采用了基于HTTP模型的PULL与PUSH相结合。
最初,客户端需要发送PULL请求以获取消息的第一个快照,这一步会同时执行订阅操作(这是基于PUSH的方法)。
在用户订阅了更新之后,只要有新的更新,Facebook后台就会将更新推送到客户端。
后台Web层
Web层由PHP提供,处理Web请求本身。负责用户身份认证、朋友的隐私设置、聊天记录、朋友更新以及其他平台功能业务逻辑。
用户在线状态模块
该模块提供用户朋友的在线信息。该模块采用C++编写,是系统中最常用的模块。
该模块聚合了内存中用户的在线信息,并在请求时将信息发送给客户端。
频道服务器
频道服务器负责消息队列和传递。该功能采用Erlang编写。
Erlang是一种并发式函数编程语言,可用于编写即时消息、金融科技、在线电话等实时的可扩展和高可用性系统。
Erlang的运行时系统内置并发、分发和容错的支持。
频道服务器利用了Mochi Web库。这是一个用于构建轻量级HTTP服务器的Erlang库。用户发送的消息会送到频道服务器的队列中。每条消息都有一个序列号,可以保证两个或多个用户之间的同步通信。
聊天记录
聊天记录模块可以记录聊天的元数据以及其他信息。该模块采用C++编写,用于记录UI页面加载之间的信息。
服务的可扩展性和部署
Facebook所有数据中心都持有用户上线与聊天记录数据的副本,而频道服务器数据仅存储在一个专用的数据中心,以确保消息的强一致性。
如上图所示,所有后台模块都采用松散耦合,各个模块之间通过Thrift互相通信。
Thrift是一种通信协议,可用于在异构技术上运行的服务之间的通信。
它是Facebook内部开发的服务通信的序列化和RPC框架,有助于运行在C++、Erlang、PHP、Java上的系统作为一个团队协同工作。
资源密集度最高的操作
整个系统中资源密集度最高的操作不是发送数十亿条消息,而是让用户掌握朋友的在线状态。
这个功能非常重要,因为只有当一个人看到朋友在线才会开始对话。
为了实现这一点,一种选择是向用户发送朋友上线的通知。但考虑到Facebook平台的用户数量,这种实现方式没有可扩展性。
在最差的情况下,该操作的复杂度为O(用户的平均朋友数 * 流量高峰期的用户数 * 用户离线和重新上线的频率)个消息/秒。
在高峰时段,Facebook网站上的并发用户数量高达数百万。保证所有用户的上线信息最新,这在技术上是不可行的。
此外,即便是没有聊天的用户通过后台的异步轮询获得朋友的活动状态也会给服务器带来巨大压力。
为了实现可扩展的用户在线状态后台,频道服务器的集群会保留用户的在线状态,并通过定期批量更新将其发送到状态服务器。
这种方法的优点在于,只需一个查询就可以获取用户朋友的整个列表。
考虑到模块之间巨大的信息交换量,频道服务器会在将信息传输到状态服务器之前进行压缩。
随着用户连接数越来越多,负载均衡器的数量也会增加。在此之后,基础架构管理并发用户连接的能力得到了显著提高。这是一个瓶颈,会在高峰时段导致聊天服务发生中断。
消息与存储的同步
如前所述,为了管理同步通信,每条消息都有一个序列号。
除此之外,Facebook还创建了一个Messenger同步协议,将非媒体数据使用量减少了40%。这减少了网络拥塞,并摆脱了由此引起的错误。
Facebook的工程团队编写了一项名为Iris的服务,该服务可以将消息更新组织在有序队列中。
该队列具有不同的指针,可帮助跟踪用户已读的消息以及尚未处理的消息更新。
最近的消息由Iris的内存发送,旧的会话从普通的存储中获取。Iris建立在MySQL和闪存之上。
最初,消息存储采用了Hbase,但后来又迁移到了MyRocks。这是一个由Facebook开发的开源数据库项目,以RocksDB为MySQL存储引擎。