字节跳动新一代云原生消息队列实践

作者:火山引擎云原生计算研发工程师|雷丽媛

上文我们了解了在字节跳动内部业务快速增长的推动下,经典消息队列 Kafka 的劣势开始逐渐暴露,在弹性、规模、成本及运维方面都无法满足业务需求。因此字节消息队列团队研发了计算存储分离的云原生消息引擎 BMQ,在极速扩缩容及吞吐上都有非常好的表现。本文将继续从整体技术架构开始,介绍字节自研的云原生消息引擎的分层架构在数据存储模型、运维等角度的优势及挑战。

回顾:一文了解字节跳动消息队列演进之路

云原生消息引擎 BMQ 架构

从整体来看,BMQ 与 Kafka 架构最大的不同在于 BMQ 是存算分离的架构,相较于 Kafka 将数据存储在本地磁盘,BMQ 将数据存储在了分布式的存储系统。在 BMQ 内部,主要有四个模块:Proxy,Broker,Coordinator 和 Controller。我们依次来看一下这些模块的主要工作:

  • Proxy 负责接收所有用户的请求,对于生产请求,Proxy 会将其转发给对应的 Broker;对于消费者相关的请求,例如 commit offset,join group 等,Proxy 会将其转发给对应的 Coordinator;对于读请求 Proxy 会直接处理,并将结果返回给客户端。

  • BMQ 的 Broker 与 Kafka 的 Broker 略有不同,它主要负责写入请求的处理,其余请求交给了 Proxy 和 Coordinator 处理。

  • Coordinator 与 Kafka 版本最大的差别在于我们将其从 Broker 中独立,作为单独的进程提供服务。这样的好处是读写流量与消费者协调的资源可以完全隔离,不会互相影响。另外 Coordinator 可以独立扩缩容,以应对不同集群的情况。

  • Controller 承担组件心跳管理、负载均衡、故障检测及控制命令接入的工作。因为 BMQ 将数据放在分布式存储系统上,因此无需管理数据副本,相较于 Kafka 省去了 ISR 相关的管理。Controller 可以更加专注地关注集群整体流量均衡及故障检测。

在 BMQ 中用户所有请求都会由 Proxy 接入,因此 BMQ 的 Metadata 中的 ‘Broker’ 信息实际上填写的是 BMQ 中 Proxy 的信息,客户端根据 Metadata 请求将生产和消费等请求发送到对应的 Proxy,再由 Proxy 处理或转发。这样的架构有助于 BMQ 做更多的容错工作。例如在 Broker 重启时,Proxy 可以感知到相关错误并进行退避重试,避免将异常直接暴露给客户端;此外我们可以监控 Proxy 在访问其他组件时产生的错误,进行一些自动的故障诊断,并将故障节点自动隔离,避免对用户产生影响。

分层架构的优势

分层架构的优势是显而易见的,BMQ 作为计算层无状态,可以实现秒级的扩缩容或故障机替换。在故障场景下,例如交换机故障或机房故障,可以秒级将流量调度到健康节点恢复服务。

数据存储模型

在分层之后数据存储模型上的优势,主要体现在 BMQ 中,一个 Partition 的数据会和 Kafka 一样被切分为若干个 Segment,Kafka 中的这些 Segment 都会被存储在同一块磁盘上,而在 BMQ 中,因为数据存储在分布式存储中,每一个 Segment 也都被存储在存储池中不同的磁盘上。从上图中可以明显看出,BMQ 的存储模型很好的解决了热点问题。即使 Partition 间数据大小或访问吞吐差别很大,被切割成 Segment 后都能均匀地分散在存储池中。

接下来我们通过一个例子进一步感受池化存储的优势。

在 Kafka 的使用中,我们经常会有回溯数据的需求,以上图中的数据分布为例,例如业务有需求回溯 Partition 1 全部的数据,高吞吐的 IO 会影响磁盘的性能,在 Kafka 存储模型中与 Partition 1 Leader 同在一块磁盘的 Partition 3 Follower 就会受到影响,使得 Partition 3 处于 Under Replica 的状态。这个状态会持续到用户将 Partition 全部数据回溯完成。

而在 BMQ 的存储模型中,Partition 1 的数据分散在不同磁盘上,热点会随着用户的回溯进程转移,不会持续影响同一块磁盘。且对于回溯访问的磁盘,仅有已经存储在该磁盘的其他 Segment 刚好被用户消费时,或有新的 Segment 要写入该磁盘的时候会受影响。此外我们也可以通过一些策略避免写入有热点访问的磁盘来降低热点访问对新写入的影响。总结来看,Kafka 存储模型下,热点访问对同磁盘其他访问的影响大、持续长、且优化空间不大;而 BMQ 的池化存储模型中,热点影响范围小、持续时间短并且可以通过一些策略优化进一步降低影响。

运维及故障影响

从运维角度来看,BMQ 的存储模型也有非常大的优势。无论重启、替换、扩容还是缩容,Kafka 都需要数据拷贝。以扩容为例,新扩容的 Broker 需要作为 Partition 的 follower,将数据从 leader 所在 Broker 拷贝至本地,全部拷贝完成后新 Broker 才可以晋升为 leader 提供服务。而矛盾的地方在于,当业务流量上涨急需扩容时,Broker 已经没有多余的带宽来支持拷贝数据了。而 BMQ 所依赖的分布式存储系统则没有这个问题,同样以扩容为例,新扩充进来的存储节点可以立即提供读写服务,无需做额外的数据拷贝,不会对原有存储造成额外压力。而在替换和缩容场景,分布式存储依然需要一些数据拷贝来补齐副本,但对业务影响会小很多。因为数据存储是分散的,因此拷贝的 IO 也会分散在多台存储上。

从故障影响角度分析,以两副本的配置为例,在 Kafka 场景下,任意两台 Broker 宕机都会造成某个 Partition 无法读写,且数据全部丢失。在 BMQ 的存储模型下,任意两台存储节点的异常都不会影响新写入的数据,因为只要存活的存储节点可以支持写入流量,新写入的数据就可以选择剩余健康的存储节点写入。对于已经存入的数据,两台存储节点宕机会导致同时存在这两台机器上的 Segment 无法读取,若这个 Segment 是最近写入的尚未被消费的,则会影响这部分数据的消费,但若这个 Segment 刚好是一个历史数据,没有消费者需要,那就不会对业务产生实际影响。

分层架构的挑战

上面我们讨论了分层架构带来的优势,下面要来分析下挑战以及 BMQ 的解决方案。分层存储之后 BMQ 访问数据的代价增加了,访问存储在分布式系统上的数据延时会比直接读取本地磁盘稍高,并且我们也需要考虑对分布式存储系统元信息及存储节点的压力情况。下面我们来分别看一下 BMQ 在生产和消费这两条链路上是如何克服这些困难的。

生产

首先介绍一下 BMQ 数据写入的流程。上文介绍过 Broker 主要负责数据写入的节点,由 Controller 负责将 Partition 分配到各个 Broker 上。因为 Kafka 协议中 Partition 内部的数据是有序的,因此每个 Partition 只会在唯一一个 Broker 上调度。Controller 调度的时候也会综合考虑 Broker 的负载及 Partition 的流量等因素,最终做到 Broker 之间的负载均衡。

如上图所示,当一个 Partition 被调度到 Broker 上之后,便开始了它的生命周期。首先 Partition 会进行 Recover,即从上一个 Checkpoint 恢复数据,并将最终结果保存,这样做是避免因意外宕机导致用户已经写入成功的数据丢失。之后 Partition 便会创建一个新的 Segment 开始写入数据,期间会写入索引等信息。当文件长度到达配置长度,或者文件写入持续到达配置时间后会被关闭,存储相关元信息,并开启一个新的 Segment 写入。依次循环,直到 Controller 将 Partition 从这个 Broker 调度走,或发生异常 Partition 退出。

我们可以看到在状态集中有一个 Failover 节点,这个节点是 BMQ 降低分布式存储延时毛刺的关键。每一次写入 BMQ 会先将数据放入一个 Inflight Buffer 中,之后通过异步调用分布式存储的 Flush 接口持久化数据。若 Flush 在预期时间内返回成功,那么 Inflight Buffer 数据中的数据会被清除,同时返回给用户写入成功的回应。但若因为网络或者慢节点问题导致写入超时,那么 Broker 会直接创建一个新的 Segment 文件,将 Inflight Buffer 中的数据直接写入新的文件,并在后台异步将之前的 Segment 文件关闭。对于异步关闭的这个文件,元信息只会包含成功返回的数据长度,最后超时的部分则不会被记录,这样即使超时数据最终确实写入了分布式存储,也不会被用户读取造成数据重复,这一整个过程就是我们说的 Failover。

为什么通过切一个文件就能解决这个问题呢?这也与存储模型有关。Kafka 因为一个 Partition 数据均被存储在一块磁盘上,那么若是因为磁盘异常引起的延时抖动,无论如何切换文件都是不能解决的。但是在 BMQ 中,每个 Segment 都是一个文件,而每个文件的多个副本都会随机地分布在整个存储池中。那么若存储池中有少数慢节点,随机切换一个节点大概率可以绕过故障的节点。因此,在慢节点问题及偶发的磁盘热点问题上,BMQ 可以更加灵活地规避,降低这些问题对用户的影响。

当然,BMQ 的分层架构对于底层的分布式存储系统也提出了较高的要求。火山引擎上分布式存储系统由 C++ 实现,是一个高性能的分布式文件系统。能够提供 5w QPS 写入及 15w QPS 读取的元信息访问能力;写入访问延时 p99 约在 10 毫秒左右,读取延时 p99 为亚毫秒级别;并且单集群可以承载 50 亿文件。同时在数据写入方面对写入延时也做了很多优化,包括慢节点的检测和规避、利用 NVMe 加速的多介质存储功能等。

消费

当一个消费请求到达 Kafka Broker,Broker 会查看当前是否有足够多的已写入数据返回给消费者,如果条件满足则会读取数据并返回。这个流程非常简单清晰,但这个流程不能直接照搬到 BMQ,因为 BMQ 底层是分布式存储系统,如果对于每个请求都直接从存储层读取数据,那么对于分布式存储系统的元信息和数据节点都是极大地压力,并且延时也会变得非常高。因此直接处理消费请求的 BMQ Proxy 针对读流程设计了多个缓存机制

第一个缓存系统非常直观,我们称之为 Message Cache。顾名思义,这个缓存存储的是消息数据。Message Cache 会将每个 Partition 末尾的一部分数据从远端读取回来,并缓存在内存中,以供消费者读取。若这个 Partition 有多个消费组,那么理想情况下,他们只会产生一次分布式文件系统的实际数据读取,其余请求均会从 Proxy 内存中直接获取数据。不同于 Kafka 依赖于 Page Cache,BMQ 的 Message Cache 有丰富的淘汰策略以应对不同的生产消费场景,使得缓存命中率更高

当然,不是所有的请求都能够完美的命中 Message Cache,一些消费者会因为消费资源不足或业务需求消费一些较老的数据,而这部分数据无法被 Message Cache 覆盖。如果在这种请求发生时 Proxy 直接读取分布式存储系统则会对其造成一次元数据的访问,当请求变多时分布式存储系统的元数据节点将不堪重负。因此 Proxy 设计来 File Cache 来应对这种情况。Proxy 会缓存某个 Segment 的文件句柄,即这个 Segment 所对应文件的文件句柄。因为 Kafka 的消费场景下,用户大多数情况都是顺序消费,因此一个消费请求这一次所访问的文件很大概率是上一次请求访问过的文件。线上实践效果来看,File Cache 可以帮助我们减少 70% 对后端存储的元信息访问请求

在 BMQ 拥有优越的消费性能上也需要强大的分布式存储系统的加持。除了上一节提到的高性能的元数据节点,也需要存储系统支持读取的慢节点检测,即如果当前读取的节点延时较高,Client 端会自动切换另外一个节点读取。再加上 NVMe 分层存储的加速,BMQ 可以以较低延时达到非常理想的消费吞吐。

总结与展望

总体来看,分层架构给 BMQ 带来了极大的性能收益及可运维性的提升,同时也给我们带了来很多的挑战。BMQ 也通过不停的探索和优化,成功克服了这些困难,很好的支撑了业务的发展。在线上实践中,目前我们承接了 TB/s 级别的入流及数十 TB/s 级别的的峰值吞吐,其中最大 Topic 峰值达到数百 GB/s 入流和 TB/s 级别的出流吞吐。

当然,我们也在思考如何在各种场景下持续优化云原生消息引擎能力为用户带来更加极致的使用体验。对于一些特定的场景,探索将 Proxy 和 Broker 融合,在降低部署成本的同时提供更加极致的读写延时体验。未来,我们也将持续优化自动检测能力,使它更智能、更准确判断故障的同时更快地隔离异常节点,缩短影响时间,持续为 BMQ 的稳定性保驾护航。此外我们也在探索更加极致的弹性能力,在保障租户吞吐能力的同时,可以根据流量潮汐自动扩缩容实例,实现极致地降本增效。后续,我们还会介绍更多技术能力,敬请期待。


火山引擎云原生消息引擎 BMQ 基于云原生全托管服务,支持灵活动态的扩缩容和流批一体化计算,能够有效地处理大数据量级的实时流数据,帮助用户构建数据处理的“中枢神经系统”,广泛应用于日志收集、数据聚合、离线数据分析等业务场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/661544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RISC-V指令集之RV32I

RISC-V指令集之RV32I 1 RV32I的寄存器2 RV32I的指令2.1 算术运算指令2.2 逻辑运算指令2.3 移位运算指令2.4 内存读写指令2.5 分支与跳转指令 本文属于《 RISC-V指令集基础系列教程》之一,欢迎查看其它文章。 1 RV32I的寄存器 如下图,列出了RV32I 寄存器…

力扣hot100 数据流的中位数 大小根堆

Problem: 295. 数据流的中位数 文章目录 思路复杂度💖 Code 思路 👨‍🏫 参考 大根堆维护较小值(堆顶即中位数),小根堆维护较大值(堆顶可能是中位数之一)维护小堆长度较长&#x…

idea搭建spring5.3.x源码环境

1.写在前面的话 碰到了不少想阅读或者学习spring源码的同学,但是第一步搭建这个源码阅读环境就能难倒了一大批人。下面我就以spring5.3.x这个源码分支,来具体演示一下搭建过程。 2. 下载源码 下载源码这一步,说实话,由于某些原…

grafana安装DevOpsProdigy KubeGraf 1.5.2

安装DevOpsProdigy KubeGraf需要安装kube-state-metrics 官方地址:https://github.com/kubernetes/kube-state-metrics/tree/release-2.10/examples/standard 查看k8s版本和kube-state-metrics对应版本: [rootmaster1 kube-state-metrics]# ll 总用量 …

并查集(高阶数据结构)

目录 一、并查集的原理 二、并查集的实现 2.1 并查集的初始化 2.2 查找元素所在的集合 2.3 判断两个元素是否在同一个集合 2.4 合并两个元素所在的集合 2.5 获取并查集中集合的个数 2.6 并查集的路径压缩 2.7 元素的编号问题 三、并查集题目 3.1 省份的数量 3.2 等…

R-kknn包-类别插值可视化绘制

前面的推文我们介绍了使用scikit-learn结合分类散点数据,构建机器学习分类模型并将模型结果可视化展示,具体链接如下: 机器学习和可视化还能一起这样用?Python教你全搞定。今天这篇推文,我们就使用R语言的kknn包进行类…

【2023地理设计组一等奖】基于机器学习的地下水仿真与时空分析

作品介绍 1 设计思想 1.1 作品背景 华北平原是我国最重要的粮棉产地之一,然而近年来农业的低效用水以及过度压采正逐步加剧其地下水资源的紧张性,为经济可持续发展带来重大风险。而地下水动态变化与人为干预、全球气候波动呈现出高度相关性,因此,地下水的仿真模拟对保障粮…

JMeter 下载、安装、启动

JMeter安装部署依赖Java环境,所以首先得安装JDK。 JDK下载JDK环境变量配置 ① 新建系统环境变量JAVA_HOME ② 编辑系统变量Path ③ 新建系统变量CLASSPATH变量 JMeter下载安装 Apache JMeter - Apache JMeter™ JMeter安装部署依赖Java环境,所以首…

sql注入之GETSHELL

2024.2.1 GETSHELL 利用SQL注入获取MYSQL数据库权限的要求: 文件读写基本要求: 是root用户最高权限 知道网站的绝对路径 文件读写注入的原理: 利用文件的读写权限进行注入,它可以写入一句话木马,也可以读取系统文件的敏感信息 文件读写…

Qt设计师中(没有现成的控件):如何添加QToolBar工具栏

1、在QtCreator设计师界面中,在MainWindow上右键,有“添加工具栏”菜单项 2、但只有在MainWindow上右键才有,在其它控件上方点击则没有,那么怎么在对话框上添加呢? 可以添加一个QWidget,然后手动在ui文件里把class改为QToolBar就…

canvas设置全局透明度globalAlpha(图文示例)

查看专栏目录 canvas实例应用100专栏,提供canvas的基础知识,高级动画,相关应用扩展等信息。canvas作为html的一部分,是图像图标地图可视化的一个重要的基础,学好了canvas,在其他的一些应用上将会起到非常重…

睿尔曼超轻量仿人机械臂—外置按钮盒使用说明

睿尔曼RM系列机械臂的控制方式有很多种,包括:示教器、JSON、API等。在此为大家介绍外置按钮盒的使用方法。 按钮盒接线安装 按钮盒外观如下图所示,有:急停、暂停、开始、继续。四个功能按钮。用户可通过这四个按钮来实现对机械臂运…

环状热力图R语言画法

环状热力图(Circular Heatmap)是一种以环状布局展示数据的可视化方法。它结合了热力图和极坐标系统,能够有效地显示数据的关系、模式和趋势。 环状热力图通常用于可视化二维数据矩阵,其中行和列代表不同的类别或变量,…

K8S-NFS-StorageClass

工作流程 K8s中部署NFS-StorageClass K8s的StorageClass提供了为集群动态创建PV的能力。 1.部署NFS服务 2.选择NFS的Provinisoner驱动 K8S中没有内置的NFS的制备器,而定义StorageClass的时候需要指定制备器(Pervisioner),所以需要&#xf…

OpenHarmony—开发及引用静态共享包(API 9)

HAR(Harmony Archive)是静态共享包,可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP,不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。 接下来&a…

springboot146基于Spring Boot的可盈保险合同管理系统的设计与实现

可盈保险合同管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本可盈保险合同管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时…

富文本编辑器CKEditor4简单使用-02(常用插件安装及使用)

富文本编辑器CKEditor4简单使用-02(常用插件安装及使用) 1. CKEditor4 入门2. 下载并安装常用插件——Language插件2.1 下载插件2.2 下载并安装 Language 插件2.2.1 下载 Language 插件2.2.2 下载 Menu Button插件以及后续的各依赖插件2.2.3 安装下载的L…

YOLOv5改进 | Conv篇 | 结合Dual思想利用HetConv创新一种全新轻量化结构CSPHet(参数量下降20W,)

一、本文介绍 本文给大家带来的改进机制是我结合Dual的思想利用HetConv提出一种全新的结构CSPHet,我们将其用于替换我们的C3结构,可以将参数降低越20W,GFLOPs降低至4.1GFLOPs,同时本文的结构比我提出的另一个CSPPC精度更高,但是轻量化效果要差一点同时本文结构为我独家创…

ElasticSearch-IK分词器(elasticsearch插件)安装配置和ElasticSearch的Rest命令测试

四、IK分词器(elasticsearch插件) IK分词器:中文分词器 分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一一个匹配操作&…

ElasticSearch概述及Window和Linux环境下安装

一、ElasticSearch 1、ElasticSearch概述 ES(Elasticsearch)是一个基于开放源代码的分布式搜索引擎,用于快速和灵活地搜索和分析大量数据。它是构建在Apache Lucene之上的,通过提供一个简单而强大的RESTful API来实现全文搜索、…