【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的高可靠消息服务设计实现
前提回顾
在深入研究了 “【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的 API 网关设计实现” 设计实现后,我们意识到,尽管 API 网关为服务商提供了高效的数据获取手段,但实时数据的获取仍然是一个亟待解决的问题。
目前,轮询作为一种常见的解决方案,其效率却不尽如人意,且易导致机器资源的过度消耗。轮询的实时性受限于应用所设定的轮询间隔时间,这意味着数据的更新频率无法超越这一设限。因此,为了实现更高效、更实时的数据获取,服务商急需探索并应用更为先进和高效的数据获取策略。
消息服务
针对上述应用场景,开放平台推出了一项创新的消息服务技术。该技术的主要目标是构建一个实时、可靠且异步的双向数据交换通道,从而显著提升 API 的调用效率,使数据流通更加迅速和高效。目前,该系统已具备处理上亿级消息量的能力,同时能够轻松应对从十万到百万级的流量波动。
逻辑架构
消息系统在逻辑架构上主要由三个子系统构成:路由系统、存储系统以及推送系统。这三个子系统协同工作,确保消息数据的稳定传输。
路由系统:路由系统负责接收和处理所有传入的消息,并将其正确地路由到目标系统或接收者。它可能包括负载均衡和消息分发机制,以保证消息的高效处理和传递。
存储系统:存储系统负责消息的持久化存储和管理。它可以使用数据库或分布式存储系统来存储消息,并提供对消息的检索、查询和删除等功能。存储系统通常还与路由系统和推送系统进行数据交互,以确保消息的完整性和一致性。
推送系统:推送系统用于将消息及时地推送给目标接收者。它可以通过不同的通信协议(如 HTTP、WebSocket、TCP/IP 等)将消息传递给终端设备或服务。推送系统可能还包括推送队列、消息处理和推送策略等功能,以确保消息的可靠传递和接收。
运作流程
消息数据首先会被存储在系统中,然后再进行推送,这样的设计保证了每条消息至少能够被成功推送一次。
此外,写入操作与推送操作是相互分离的,发送方在发送消息后无需同步等待接收方的应答。这种异步通信方式使得客户端的任何异常都不会对发送方系统的稳定性造成影响。
消息路由系统
消息路由系统经过精心设计,采用了模块化的管道化处理流程,从而具备了出色的扩展性。该系统能够实时监听上游服务发生的各类事件,如新增数据、数据更新、状态改变等。
针对不同类型的业务,路由系统会执行相应的消息过滤、鉴权、格式转换、存储及日志记录等操作。
消息处理的全过程中,系统会详细记录每个处理环节的状态信息,并通过日志采集器将这些信息输出给日志审计系统。我们可以处理并分析这些日志数据,同时记录消息的完整轨迹,确保每一条消息都能够被准确追踪和回溯。
数据存储系统
存储系统作为整个架构的关键组件,主要负责削峰填谷,确保系统在高并发场景下依然能够稳定运行。
该系统采用 BitCask 存储结构,结合内存映射文件技术,使得磁盘写入操作完全按照顺序进行,从而极大提升了写入速度。在数据读取方面,存储系统则运用了 FileRegion 零拷贝技术,有效减少了内存拷贝的开销,进一步提升了数据读取的效率。
BitCask 结构
BitCask 是一个基于哈希表结构的键值存储系统,它采用 Append-only 的方式进行数据写入,即所有的写操作只追加不修改老的数据。BitCask 的数据文件以日志型只增不减的方式写入,每个文件有一定的大小限制,当文件增加到相应大小时,会产生一个新文件,而老的文件则只读不写。这种设计使得 BitCask 具有优秀的写入性能。
异地存储容灾
为了确保数据的安全性和可用性,存储系统被部署在多个机房中,这样的部署策略不仅提高了系统的容灾能力,还能在一定程度上实现数据的备份和冗余。这样的设计使得存储系统既能够满足高性能的需求,又能够确保数据的安全可靠。
推送系统
技术选项:基于 Disruptor 构建了一个高效的事件驱动模型,并采用 Netty 作为网络层框架,旨在构建能够处理海量连接的模型。
控制流量:系统能够根据连接吞吐量进行自适应调整,有效减轻慢连接对系统造成的压力。同时,利用 WebSocket 技术构建了长连接通道,进一步降低了通信延迟。
优化性能,采用了对象池技术,显著降低了系统的垃圾回收(GC)频率,提高了整体运行效率。
从消息的触发到拉取、发送和确认,整个流程均实现了完全异步处理,确保了系统的高性能表现。
这些改进措施共同提升了系统的稳定性和可扩展性,使其能够更好地应对各种复杂场景。
数据消费模式
在消息系统中,消费模式通常包括服务端推送和客户端拉取两种。考虑到系统主要服务于公网环境,选择了推送模式,其优势如下:
高实时性:从消息生成到实际推送至客户端,整体平均延迟仅为 100 毫秒,且最大延迟不会超过 200 毫秒。这种快速的响应能力确保了消息的时效性和准确性。
减轻服务器压力:与拉取模式相比,推送模式每次都有实际数据发送,避免了因空轮询而产生的资源浪费。这有助于提升服务器的整体性能和稳定性。
使用便捷性:在拉取模式下,客户端需要负责维护消费队列的位置,并处理多客户端并发消费时的复杂问题。而在推送模式下,这些繁琐的任务全部由服务器承担,客户端仅需启动 SDK 并监听消息即可。这种简化的操作流程降低了使用门槛,使得用户能够更轻松地使用系统。
推、拉模式的切换
系统同样支持客户端拉取模式。在这种模式下,推送系统会智能地将客户端的拉取请求转换为推送请求,并直接返回响应。推送服务器会根据客户端的请求,主动将相关数据推送给客户端。
通过这种方式,实现了拉取操作的异步化,从而有效降低了客户端的网络消耗。只有当客户端有新数据产生时,服务器才会返回数据;否则,不会返回任何数据,避免了不必要的网络传输。这样的设计不仅提升了系统的效率,也优化了客户端的用户体验。
实现低延时推送
在分布式消息系统中,推送模式的实时性至关重要,其核心指标便是推送延时。考虑到系统中各个长连接可能分布于不同的推送机器上,当新消息产生时,如何确保这些连接所在的机器能够迅速感知到这一事件变得尤为关键。
为了实现这一目标,设计了一套高效的事件通知机制。
在本系统中,所有的推送机器都通过彼此之间的连接构成了一个紧密的通知网络。当其中任意一台机器感知到新消息产生的事件时,它会迅速确定这条消息所归属的长连接所在的推送机器,并将消息快速传递给那台机器。随后,负责该长连接的推送机器会立即将数据推送给相应的客户端。
同时,路由系统在接收到每一条消息时,都会及时通知下游的推送系统。这种上下游系统之间的紧密协作确保了消息能够在产生后的第一时间被准确地推送给目标客户端,实现了消息的高效、实时传递。
过这种方式,各个推送机器能够在消息产生后迅速感知到相关事件,并及时进行消息推送,从而确保了推送模式的实时性和高效性。同时,这种机制也有效降低了网络传输的负担,因为只有在真正需要推送消息时,才会进行数据的传输。
快速确认消息
针对消息推送事务数据的特性,即大部分数据在几秒内完成一次读写操作后即失去意义,使用传统数据库进行存储显然并不合理。这类似于在数据库中插入一条几乎不会被读取的数据,不仅造成了资源的浪费,还可能导致数据库成为系统的瓶颈。
三层存储结构
针对消息推送的高频和短生命周期特性,本系统精心设计了存储子系统,采用 HeapMemory、DirectMemory 和 FileSystem 三级存储结构。为了确保存储系统的高效运行,我们对内存使用进行了精细化的管理。
HeapMemory
HeapMemory 主要用于存储最近 10 秒内的发送记录,以便快速访问和处理。而其余的数据则会被异步写入内存映射文件,并最终持久化到磁盘中。
在 HeapMemory 中,我们基于时间维度将其划分为三个 HashMap,通过时钟滴答机制实现无锁切换,从而确保数据的高效读写。
DirectMemory
在 DirectMemory 中,我们结合消息队列和时间维度,将数据组织成多个链表环。最新数据总是被写入到指针头链表,而末端指针则指向已经超时的事务所在链表。这种设计不仅有效隔离了各个队列之间的相互影响,还便于我们快速扫描和处理超时事务。
通过这种优化的存储模式,我们发现 95%的消息事务都能在 HeapMemory 内完成,仅有 5%的消息需要进入 DirectMemory 进行处理。至于涉及磁盘读写的消息事务更是寥寥无几。因此,绝大部分消息事务的处理都能在内存中高效完成,从而大幅节省了服务器资源。这也为我们的系统带来了更高的吞吐量和更低的延迟,提升了用户的整体体验。
总结和展望
到目前为止,我们已对消息队列高性能架构的基本设计实现和功能分布进行了全面而深入的介绍与分析。
版权声明: 本文为 InfoQ 作者【洛神灬殇】的原创文章。
原文链接:【http://xie.infoq.cn/article/af62998ada42e054b09eb0547】。文章转载请联系作者。
评论