概述
本文由 StreamNative 组织翻译自《Apache BookKeeper Internals — Part 4 — Back Pressure》,作者 Jack Vanlightly,Apache BookKeeper Committer。
译者信息
张文锋,热爱分享,拥抱开源,社区昵称 zeroGozhang,现为星环信息科技金融大数据架构师。
本系列博客基于为 Apache Pulsar 配置的 Apache BookKeeper 4.14 编写。
在这篇文章中,我们将介绍 BookKeeper 服务器节点采用的所有背压机制,来保护自己免受读取和写入过载的影响。背压机制在任何数据系统中都是必要的,它不仅可以防止资源耗尽,同时也能让系统更优雅平稳地处理过载情况。当 Bookie 过载时,理想情况下,我们希望它能继续为其可以处理的内容提供高吞吐量,并拒绝其他请求。
一些组件会对它们内存中的数据结构加以限制,以防止 Bookie 耗尽内存。例如,针对 Journal 队列会对 entry 数量进行限制,写入缓存则存在写入字节数的限制。事实上,写入缓存都会预先分配,因此无论 Bookie 的负载如何,它的内存使用量都是固定的。
在默认情况下,其他组件均无限制,例如挂起的 Netty 任务。此外,即使是在考虑到内部缓冲区的所有不同限制后,当它们组合到一起也有可能超过 Bookie 的内存限制。
下面让我们来看看各种背压和内存限制机制。
图 1:各种背压机制的位置
1 - 正在进行的写入总数
我们可以通过配置 maxAddsInProgressLimit
参数控制正在进行的写入总数。当正在进行的写入请求总数达到此限制时,系统将阻塞住那些处理新写入请求的 Netty 线程,直到进行中的写入数再次降至该限制以下。当 Netty 停止从网络缓冲区读取数据时,数据会积压在 TCP 缓冲区中,从而触发 TCP 自带的背压机制,通知上游发送端并暂停发送,将 TCP 背压应用到客户端。
这能有效避免线程池任务队列、Journal 队列等因积压了太多的请求而导致的 OutOfMemory 错误。默认情况下没有限制。
2 - 进行中的读取总数
我们也可以通过配置 maxReadsInProgressLimit
参数,限制正在进行的读取总数。这会触发相同的 Netty 线程阻塞式行为。
3 - 每个写入线程池线程挂起的写入请求数
我们还可以限制写入线程池任务队列的大小(配置 maxPendingAddRequestsPerThread,默认值为 10000),以减少挂起的写入请求数量。一旦任务队列已满,客户端将收到 TOO_MANY_REQUESTS
的响应,导致客户端选择另一个 Bookie 进行写入。
4 - 每个读取线程池线程挂起的读取请求数
读取线程池任务队列的大小可以通过与写入线程池相同的方式进行限制(配置 maxPendingReadRequestsPerThread
)。一旦任务队列已满,读取请求会开始以与写入请求相同的方式被拒绝,受影响的客户端将选择另一个 Bookie 进行读取。
5 - Journal 队列
Journal 从阻塞队列中获取任务。当 Journal 逐渐无法跟上队列的填充速度导致队列大小达到其最大值时,写入线程将会被阻塞,在队列上有空间之前无法执行更多的工作。
接下来,写入线程任务队列将被填满,写入请求将开始被拒绝。
6 - DbLedgerStorage 拒绝写入
如果活跃的写入缓存已满,而交换出来的写入缓存还未可用(因为它仍在刷盘),则 Bookie 无法处理更多的写入操作。只有当一个空的写入缓存变得可用时,它才能接受更多的写入操作。因此,当在等待一段时间后仍没有写入缓存可用时(默认情况下为 10 秒,由 dbStorage_maxThrottleTimeMs
定义),写入请求将被拒绝。
在 BookKeeper 4.14 版本中,这会导致返回给客户端的是通用的错误代码,而不是 TOO_MANY_REQUESTS
代码。这个问题将会在未来的版本中得到修复。
7 - Netty 不可写通道
为了防止在通过 Netty 通道向客户端发送响应时出现内存不足的异常情况,可以通过 waitTimeoutOnResponseBackpressureMs
参数配置回退机制。如果由于通道缓冲区已满,使得 Netty 通道变得不可写入,则写入响应可能会延迟至对应的等待时间。如果通道仍然不可写入,则系统不会发送响应,并且会发出通道错误的指标。如果没有配置这个回退机制,尽管其状态为不可写,响应仍将会发送到通道;如果沿通道发送的字节过多,可能会导致内存不足的错误。
此外,Netty 的队列任务数量还可以通过使用 Java 属性来限制:io.netty.eventLoop.maxPendingTasks
。这个参数也可以防止 Netty 线程接受过多的任务。
总结
接收端施加的背压将会沿着链路向上传播,直至到达源头。对于 BookKeeper 来说,背压通常从 Journal 和 DbLedgerStorage 组件开始,然后沿着链路向上到达线程池(它们的任务队列)、Netty,最后到达客户端。
但对于 Apache Pulsar 集群来说,还不止这些。Pulsar Broker 也有自己的内部背压机制,最终会触发退回给 Pulsar 生产者和消费者。
本文为此系列的最后一篇文章。接下来,我们将发布一系列新的博客,内容有关于 BookKeeper 所产生的一些指标,以及如何使用我们在本系列中尝试构建的思维模式来解释它们。
推荐阅读
1. 深入解析Apache BookKeeper 系列:第一篇 — 架构原理
2. 深入解析 BookKeeper 多副本协议(一)
3. 深入解析 Apache BookKeeper 系列:第二篇 — 写操作原理
4. Apache BookKeeper 洞察(一) — 外部共识和动态成员
5. 深入解析 Apache BookKeeper 系列:第三篇——读取原理
▼ 关注「Apache Pulsar」,获取干货与动态 ▼
????????回复 BookKeeper,加入 Pulsar Storage 小组????????
点击「阅读原文」,领取 Pulsar vs Kafka 最新测评报告 PDF!
最后
以上就是虚拟酸奶为你收集整理的深入解析 Apache BookKeeper 系列:第四篇—背压译者信息总结推荐阅读的全部内容,希望文章能够帮你解决深入解析 Apache BookKeeper 系列:第四篇—背压译者信息总结推荐阅读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复