一文理解kafka端到端的延迟

前言: 在大规模的使用kafka过程中,我们通常会遇到各种各样的问题,比如说,通常会有一些大数据集群中的Job发现总有几个task会比较慢,导致整体的任务迟迟不能完成运行,这种情况通常问题会比较复杂,想要知道具体延迟在哪里,我们需要知道在Kafka集群中哪些点可能会增加端到端的延迟。

接下来的内容翻译自confluent官网博客中的一篇文章,希望能够帮助大家理解kafka使用过程中端到端的延迟99th Percentile Latency at Scale with Apache Kafka。

文章目录

        • 理解到端的延迟(end-to-end latency)
        • Produce time
        • Publish time
        • Commit time
        • Catch-up time
        • Fetch time
        • End-to-end latency VS producer and consumer latencies
        • 控制end-to-end latency
        • 不同客户端配置的延迟测试
        • 持久性设置对延迟的影响
        • 在满足延迟目标的前提下扩展吞吐
        • 配置kafka的生产者和消费者以实现batching
        • 不增加人为延迟以提高batching效率
        • clients的数量对尾延迟(tail-latency)的影响
        • 关于增加消费者数量的说明
        • 压缩配置的影响
        • 更多的分区可能增加延迟
        • broker节点负载对延迟的影响
        • 总结

欺诈检测、支付系统和股票交易平台只是许多Apache Kafka用例中的一小部分,这些用例需要快速且可预测的数据交付。例如,在线银行交易的欺诈检测必须实时发生,以交付业务价值,而不需要为每个交易增加超过50 100毫秒的开销,以保持良好的客户体验。

在Kafka术语中,数据交付时间(data delivery time)是由端到端延迟(end-to-end latency)定义的,即消费者获取一条向Kafka生成的记录所需的时间。延迟目标表示为目标延迟和满足此目标的重要性。例如,您的延迟目标可以表示为:我希望99%的情况下从Kafka获得端到端延迟为50 ms。

这将增加可用性、持久性和吞吐量目标。实现高持久性和高吞吐量两个目标,我们需要进行一定的权衡,挑战在于在保持延迟界限的同时扩展应用程序的吞吐量,并调整Kafka集群的大小以使用可接受的Broker延迟来处理客户端和复制的请求。延迟也取决于您对硬件或云提供商的选择,所以您需要能够监视和调优您的客户端,以在您独特的环境中实现您的特定延迟目标。

注意: 通常情况下,Broker所在的网络区域其实也会对延迟造成很大影响,当然这仍然取决于您对可用性和延迟的权衡。

之前,我们有写过白皮书optimizing-your-apache-kafka-deployment,其中列出了配置Kafka部署以优化各种目标的指导原则。

这篇文章将帮助您进一步获得更好的直觉和对端到端延迟的理解,并配置和扩展您的应用程序的吞吐量,同时保持延迟的界限。

理解到端的延迟(end-to-end latency)

端到端延时是指应用逻辑调用KafkaProducer.send()生产消息到该消息被应用逻辑通过KafkaConsumer.poll()消费到之间的时间。

下图显示了一条记录在系统中的路径,从Kafkas生产者到Kafka的Broker节点,副本的复制,以及消费者最终在其主体分区日志中获取到具体的消息。

因此,端到端的延迟主要会由以下几个部分组成:

  • Produce time: 内部Kafka producer处理消息并将消息打包的时间
  • Publish time: producer发送到broker并写入到leader副本log的时间
  • Commit time: follower副本备份消息的时间
  • Catch-up time: 消费者追赶消费进度,消费到该消息位移值前所花费的时间
  • Fetch time: 从broker读取该消息的时间

一文理解kafka端到端的延迟_第1张图片

在接下来的内容中,我们将分别解释这五个延迟阶段的具体含义,特定的客户端配置或应用逻辑设计通常会极大地影响端到端延时,因此我们有必要精准定位哪个因素对延时的影响最大。

Produce time

Produce time 指的是从应用程序通过KafkaProducer.send()生产一条记录到包含该消息的生产者请求被发往leader副本所在的broker之间的时间。(因此,生产者所处的网络环境以及对应topic分区leader副本所在的broker的网络可能会影响到produce time的延迟)

Kafka producer会将相同topic分区下的一组消息打包在一起形成一个批次(batch)以提升网络I/O性能。(在必要情况下,我们可以对生产者的batch size进行一定的调整)

默认情况下,producer会立即发送batch,这样一个batch中通常不会包含太多的消息。为了提高batch的效率,生产者通常会对linger.ms来人为设置一个较小的延迟来保证有足够多的消息记录能封装在一个batch中。一旦过了linger.ms设置的事件,或者batch size已经达到最大值(batch.size的参数值),这个batch将被认为已经完成。

如果生产者也开启了压缩(compression.type),kafka的生产者会将已完成的batch进行压缩。在batch完成之前,它的大小时根据生产者指定的压缩类型和之前观测到的压缩比率估算出来的。

如果发送给leader副本的未确认的生产者请求数量已经达到最大(max.inflight.requests.per.connection=5),则在生产者的批处理可能需要等待更长的时间。因此,broker响应生产者请求越快,生产者的等待时间也将会变得更小。

Publish time

Publish time是指内部kafka生产者发送生产者请求到一个broker节点,并且对应的消息到达leader副本日志之间的时间。当请求到达Broker节点时,负责连接的网络线程将获取该请求并将其放入请求队列中。其中一个请求处理程序线程从队列中获取请求并处理它们。(对应broker节点的num.thread 和num.io.thread两个相关参数)

因此,Publish time包含生产者请求的网络时间,broker上的排队时间,以及将消息追加到日志所消耗的时间(通常也是page cache 的访问时间)。当Broker端负载比较低,网络和日志的追加写入时间会影响publish time,随着broker负载变高,队列延迟的增加
将会更多的影响publish time。

Commit time

Commit time是指从leader副本中复制消息到全部的同步副本(all in-sync replicas)中所消耗的时间。kafka只会将已提交(committed)的消息暴露给consumer,也就是该消息必须在全部的ISR中包含。follower副本中的消息会从leader副本中并行的拉取,在一个正常的集群中,我们通常不希望副本处于不同步状态(当然有的业务场景可能会导致短暂的不同步现象)。这意味着消息被提交的时间等于ISR中最慢的follower副本所在broker去从ledaer broker节点获取记录并写入到follower副本日志的时间。

为了复制数据,follower所在的broker会想leader节点发送fetch请求,准确的来讲消费者也是使用fetch请求来获取消息。但是,官方在副本复制的fetch请求中,broker端优化了默认配置: leader副本会尽早的发送请求,只要有一个字节可用,就会发起fetch请求(由replica.fetch.min.bytes参数控制)或者当replica.fetch.wait.max.ms满足条件。Commit time主要受副本因此配置参数的影响以及集群的当前负载情况。

Catch-up time

Kafka中消息是按照其生产的顺序被消费的,除非显示的声明了一个新的offset或者有一个新的消费者从最新的offset进行消费。同一个分区下,consumer必须要消费完之前发布的消息后才能读取后面的消息。假设在提交消息时,消费者的偏移量是提交消息后面的N条消息,那么,Catch-up time就是消费者消费者N条消息的总时间.

一文理解kafka端到端的延迟_第2张图片

当我们在构建实时处理应用的时候,最好让catch-up时间为0,即一旦消息被提交,消费者可以立马读取到消息。如果消费者总是落后,端到端延迟可能会变得无限大。因此,catch-up 时间通常依赖于消费者的能力是否能够追赶上生产者的吞吐量。

Fetch time

订阅主题分区的消费者会不断轮询去从leader副本中获取更多的数据,Fetch time是从leader副本所在broker节点获取消息记录的s时间,可能需要等待足够的数据来形成对fetch请求的响应,并从KafkaConsumer.poll()的响应中返回记录。在默认的配置下,已经对于消费者的fetch延迟做了优化(fetch.min.bytes=1),即及时只有一个字节可用的时候,fetch请求也会响应数据,或者在一个短暂超时之后fetch.max.wait.ms

End-to-end latency VS producer and consumer latencies

下图显示了Kafka客户端观察到的延迟(通常称为生产者延迟和消费者延迟)与端到端延迟之间的关系。

生产者延迟是指KafkaProducer.send()发送和生产的消息被确认间的事件。消息的确认依赖于acks的配置,该参数可以控制消息的持久性(durability):

  • acks=0,立即确认,不等待broker的返回
  • acks=1,消息被追加到leader副本所在分区后再确认
  • acks=all,在所有的ISR(同步副本)都接收到消息时才确认

所以,生产者延迟包含produce time,publich time(如果acks >= 1),commit time(如果acks=all) 以及生产者响应从broker返回到生产者的时间。

一文理解kafka端到端的延迟_第3张图片

上图清晰的向我们展示了为何改变acks参数能够减少生产者延迟(其实是通过从生产者延迟中移除几个延迟概念来减少的publish和commit)。不过,无论我们如何配置生产者的acks参数,publish和commit 时间总是端到端延迟的一部分。

消费者延迟(Consumer latency)是指消费者发起一个fetch请求到broker节点,以及broker节点向consumer返回响应的时间。计算方法是KafkaConsumer.poll()返回的时间。Consumer的延迟主要包含了上图中的fetch time

控制end-to-end latency

如果我们思考一条消息的生命周期,控制端到端延时其实就是控制消息在系统中流转的时间总和。很多Kafka clients端和broker端参数的默认值已然对延时做了优化:比如减少人为等待时间来提高批处理的能力(通过linger.ms,fetch.min.bytes,replica.fetch.min.bytes参数来适当调优)。其他的延时可能来自于broker端上的队列等候时间,控制这种延时就要涉及控制broker的负载(CPU或吞吐量),通常情况下我们要时刻关注broker节点的各项基础监控指标。

如果我们将系统视为一个整体,那么整个端到端的延迟还要求系统中的每一个部分(生产者,broker,消费者)都能够可靠的维持应用程序逻辑所需的吞吐量。

例如,如果您的应用程序逻辑以100 MB/s发送数据,但是由于某种原因,您的Kafka消费者吞吐量在几秒钟内下降到10 MB/s,那么在此之前产生的大多数消息都需要在系统中等待更长的时间,直到消费者赶上了。此时,你需要一种高效的方式来扩展你的Kafka clients程序以提升吞吐量——高效地利用broker端资源来减少队列等候时间和偶发的网络拥塞。

理想情况下,限制延迟意味着确保所有延迟都低于目标。但实际生产环境中,由于意外故障和峰值负载,这种严格的保证是不可能的。不过,可以设计应用程序并对系统进行调优,以实现95%的延迟目标,控制所有的消息延迟在95~99%低于目标延迟时间。高百分位延迟也称为尾部延迟,因为它们是延迟频谱的尾部。

目标延时所用的百分位越大,你需要降低或容忍应用最差表现所做的努力就越多。比如,偶尔的大请求可能会阻塞全部的请求,从而增加整体的延迟,这也就是所谓的head-of-line队首阻塞。同时,大量低速率客户端可能偶尔会同时向kafka发送生产或消费请求,或全部刷新集群元数据,也会导致请求队列比平常更长,从而引发比平时更严重的尾延迟。这种行为就是所谓的micro-bursting(微型冲击,可能就是水滴石穿的意思吧)

不同客户端配置的延迟测试

在这接下来的内容中,我们使用实验结果来说明Kafka客户端配置和吞吐量扩展技术对性能的影响。我们使用kafka内置的Trogdor测试框架以及生产者和消费者的基准测试,ProduceBenchConsumeBench来进行我们的生产者和消费者实验测试。

我们所有的测试都在一个包含9个代理的Kafka集群上运行,该集群的复制因子为3,这保证了在出现最多两个同时发生的节点故障时不会丢失消息。

Kafka集群运行在AWS的r5.xlarge实例上,使用有2T的EBS(弹性块存储)。Kafka的broker节点分布在同一区域内的三个可用性区域(AZ),以获得更强的容错性,其中每个主题分区副本被放置在一个不同的AZ上,并且kafka客户端配置使用SASL认证和SSL加密,Broker之间使用PLAINTEXT进行通信。

主题:需要注意的是,分布式集群中节点如果在不同可用区也可能导致延迟的增加,当然这要在延迟和容错性角度进行权衡,也需要考虑到云厂商的可用区之间本身的延迟。

我们的实验使用了以下非默认客户端配置和其他规范:

参数项 参数值
副本数 3
topic的分区数 108
security.protocol SASL_SSL
sasl.mechanism PLAIN
acks all
linger.ms 5
compression.type lz4
Producer record size value=521bytes/key=4bytes
Trogdor record value generator uniformRandom
Trogdor record key generator sequential
Number of Trogdor agents 9

这个测试场景会产生额外的延迟: 多可用区可能增加commit时间,由于是跨可用区的副本。无论是clients端还是broker端,SSL加密也是有开销的。同时由于SSL无法利用Zero Copy特性进行数据传输,因为consumer获取消息时也会增加额外的开销。

虽然这些因素都会影响延迟,但是通常情况下企业内部可能还是需要这种架构上的考虑,因此采用该部署结构进行测试。

持久性设置对延迟的影响

当将延迟目标与其他需求叠加在一起时,首先考虑持久性需求是很有用的。由于数据的重要性,通常需要一定程度的持久性。

优化持久性会增加端到端延迟,因为这会增加延迟的复制开销(提交时间),并向Broker添加复制负载,从而增加排队延迟。

Replication factor

Replication factor 是Kafka持久化保证的核心,它定义了Kafka集群上保存的topic副本数。Replication factor = N 表示我们最多能够容忍N-1台broker宕机而不必数据丢失。N=1能够令端到端延时最小化,但却是最低的持久化保证。

增加副本数会增加备份开销并给broker额外增加负载。如果clients端带宽在broker端均匀分布,那么每个broker都会使用N * w写带宽r + (N - 1) * w读带宽,其中w是clients端在broker上的写入带宽占用,r是读带宽占用。

由此,降低N 对端到端延时影响的最佳方法就是确保每个broker上的负载是均匀的。这会降低commit time,因为commit time是由最慢的那个follower副本决定的。

如果你的Kafka broker使用了过多的磁盘带宽或CPU,follower就会开始出现追不上leader的情况从而推高了commit time。(其实还需要注意的是,当最小的ISR默认为副本的数量个数时,在出现follower和leader不同步时恰巧leader节点宕机,会导致topic本身不可用)

我们建议为副本同步消息流量设置成使用不同的listener来减少与正常clients流量的干扰。你也可以在follower broker上增加I/O并行度,并增加副本拉取线程数量number.replica.fetchers来改善备份性能。

Acks

纵然我们配置了多个副本,producer还是必须通过acks参数来配置可靠性水平。设置acks=all能够提供最强的可靠性保证,但同时也会增加broker应答PRODUCE请求的时间,就像我们之前讨论的那样。

Broker端应答的速度变慢通常会降低单个producer的吞吐量,进而增加producer的等待时间。这是因为producer端会限制未应答请求的数量(max.inflight.requests.per.connection)。

举个例子,在我们的环境中acks=1,我们启动了9个producer(同时也跑了9个consumer),吞吐量达到了195MB/秒。当acks切换成all时,吞吐量下降到161MB/秒。设置更高级别的acks通常要求我们扩展producer程序才能维持之前的吞吐量水平以及最小化producer内部的等待时间。

Min.insync.replicas

min.insync.replicas是一个重要的持久化参数,因为它定义了broker端ISR副本中最少要有多少个副本写入消息才算PRODUCE请求成功。这个参数会影响可用性,但是不会影响端到端的延时。因此,选择一个小一点的值并不能减少commit time并减少延迟。

在满足延迟目标的前提下扩展吞吐

延迟和吞吐的权衡

优化Kafka clients端吞吐量意味着优化batching的效果。Kafka producer内部会执行一类batching,即收集多条消息到一个batch中。

每个batch被统一压缩然后作为一个整体被写入日志或从日志中读取。这说明消息备份也是以batch为单位进行的。

Batching会减少每条消息的成本,因为它将这些成本摊还到clients端和broker端。通常来说,batch越大这种开销降低的效果就越高,减少的网络和磁盘I/O就越多。

另一类batching就是在单个网络请求/响应中收集多个batch以减少网络数据传输量。这能降低clients端和broker端的请求处理开销。这类batching能够提升吞吐量和降低延时,因为batch越大,网络传输I/O量越小,CPU和磁盘使用率越低,故最终能够优化吞吐量。另外batch越大还能减低端到端延时,因为每条消息的成本降低了,使得系统处理相同数量消息的总时间变少了。

这里的延时-吞吐量权衡是指通过人为增加等待时间来提升打包消息的能力。但过了某个程度,人为等待时间的增加可能会抵消或覆盖你从打包机制获得的延时收益。因此你的延时目标有可能会限制你能实施打包化的水平,进而减少所能达到的吞吐量并增加延时。如果拉低了本能达到的吞吐量或端到端延时水平,你可以通过扩展集群来换取或“购买”更多的吞吐量或处理能力。

配置kafka的生产者和消费者以实现batching

对于producer而言,batching由两个参数进行控制: batch.size(16KB)linger.ms(0),前者控制batch的大小,后者限制延迟量。如果使用场景中,应用会频繁的像kafka集群发送数据,及时设置了linger.ms=0,整个batch也会被尽快填满。如果应用生产数据的频率较低,可以通过增加linger.ms来增加batch。

对于consumer而言,可以调整fetch.min.bytes(1)来限制每个消费者在每个fetch响应中接收的数据量,该参数指定了broker应该在一个fetch响应中返回的最小数据,以及fetch.max.wait.ms(500)来设置等待数据的超时时间。在fetch响应中的数据越多,就会有更少的fetch请求。

在生产者端的batching也会间接影响produce和fetch的请求数量,因为batch定义了数据能够被获取的最小数据量。

值得注意的是,默认情况下,Kafka producer和consumer设置的是无人为等待时间,这么做的目的是为了降低延时。但是,即使你的目标就是了使延时最小化,我们依然推荐你设置一个不为0的linger.ms值,比如5~10ms。当然,这么做是有前提的:

  • 如果你扩展了你的producer程序,平均下来使得每个producer实例的发送速率变得很低,那么你的batch只会包含很少的几条消息。如果你整体的吞吐量已然很高了,那么你可能会直接把你的Kafka集群压挂,导致超高的队列等候时间从而推高延时。此时,设置一个较小的linger.ms值确实能够改善延时。
  • 如果你在意尾延时,那么增加linger.ms可能会降低请求速率以及同时到达broker端的瞬时冲击流量。这种冲击越大,请求在尾部的延时就越高。这些瞬时冲击流量决定了你的尾延时水平。

下面这个实验说明了以上两种场景。我们启动了90个producer,向一个有108个分区的topic发送消息。生产端整体的吞吐量峰值在90MB/秒。我们跑了3次测试,每一次对应一种不同的producer配置。

一文理解kafka端到端的延迟_第4张图片

因为在给定的总吞吐下,我们有相对大量的生产者,因此linger.ms = 0导致在生产者端机会没有batch操作。将linger.ms从0调整到5可以增加batching能力: 向kafka发起的生产者请求从2800降低到了1100。这减少了50%和99%的生产者延迟。

增加batch.size不会直接影响生产者的等待时间,因为生产者在填满batch的时间不会超过linger.ms的限制。在我们的实验中,增加batch.size从16KB到128KB没有增加bacth的效果,因为每个生产者的吞吐量非常低。正如预期的那样,生产者延迟在两种配置之间没有变化。

总之,如果您的目标是最小化延迟,我们建议保留默认的客户端批处理配置,并尽可能增加linger.ms。如果你在意尾延时,最好调优下打包水平来减少请求发送率以及大请求冲击的概率。

不增加人为延迟以提高batching效率

batching效果不好的另一个原因是producer发送消息给大量分区。如果消息不是发往同一个分区的,它们就无法聚集在一个batch下。因此,通常最好设计成让每个producer都只向有限的几个分区发送消息。

另外,可以考虑升级到Kafka 2.4 producer。这个版本引入了一个全新的Sticky分区器。该分区器能够改善non-keyed topic的打包效果,同时还无需引入人为等待。

clients的数量对尾延迟(tail-latency)的影响

即使整体的生产和消费的吞吐量保持不变,通常也是Clients数越多,broker上负载越大。这是因为clients数量多会导致更多的METADATA请求发到Kafka,继而要维护更多的连接,故给broker带来更大的开销。

相对于50%或平均延时的影响,Clients数量增加对尾延时的影响更大。

每个producer最多发送max.inflight.requests.per.connection个PRODUCE请求给单个broker,而每个consumer一次最多只会给一个broker发送FETCH请求。Clients越多,同一时刻发送到broker的PRODUCE和FETCH请求也就越多,这就增加了形成请求瞬时冲击的概率,进而推高了尾延时。

Consumer数量通常由topic分区数量以及期望consumer没有较大lag的目标共同决定。但是,我们却很容易为了扩展吞吐量而引入大量的producer。

基于吞吐量的考量增加producer实例数可能有相反的效果,因为producer会导致更少的消息被打包,毕竟每个producer处理了更少的消息,因而发送速率会变慢。同时producer还必须等待更长的时间来积累相同数量的消息进到batch里面。

在我们的实验中,我们将producer的数量从90增加到900,发现吞吐量没有他打变化:90MB/秒。

我们使用batch.size=16KB,linger.ms=5,acks=all的生产者配置,实验结果如下:

一文理解kafka端到端的延迟_第5张图片

结果显示增加producer数量(90->900)增加了60%的中位数延时值,而99%延时值几乎增加了3倍。

延时的增加是因为producer端打包效果变差导致的。

尾延时的增加是因为更大的请求瞬时冲击,这会拉升broker端延时,同时producer端会等待更长的时间来接收应答。

在900个producer的测试中,broker完全被PRODUCE请求压垮了。用于处理请求的时间几乎占到了broker端CPU使用率的100%。另外由于我们使用了SSL,它也会进一步引入请求级的开销。

如果你通过添加producer来提升吞吐量,那么可以考虑增加单个proudcer的吞吐量,即改善batching的效果。不管怎样,你最终可能会有很多producer实例。比如,大公司收集设备上的统计指标,而设备数可能有成千上万。此时,你可以考虑使用一个Broker收集来自多个clieints的请求,然后把它们转换成更高效的PRODUCE请求再发给Kafka。你也可以增加broker数来降低单个broker上的请求负载。

关于增加消费者数量的说明

当扩展消费者时需要注意,在同一个消费者组的消费者会提交offset信息和心跳到broker节点上(controller节点)。如果按时间间隔执行偏移提交(auto.commit.interval.ms),则消费者组中的更多消费者者会增加偏移提交率。偏移量提交本质上是向内部__consumer_offsets产生请求,因此增加consumer数量会导致broker上的请求负载增加,特别是auto.commit.interval.ms值很小的时候。

压缩配置的影响

默认情况下,Kafka producer不做压缩。compression.type参数可以决定要不要做压缩。

压缩会在producer端引入额外的开销来压缩消息,在broker端做校验时解压缩从而引入额外的开销,另外在consumer端解压缩也是开销。

注意:通常情况下broker端的压缩参数需要设置成producer,以避免压缩方式冲突导致数据无法正常消费,这样broker只需要直接将压缩后的日志写入

虽然压缩会增加CPU开销,但它还是可能减少端到端延时的,因为它能显著地降低处理数据所需的带宽占用,进而减少broker端的负载。压缩是在batch级别上完成的,故打包效果越好,压缩效果也就越好。

更多的分区可能增加延迟

一个主题的分区是kafka中的并行单元。发送到不同分区的消息可以由生产者并行发送,由不同的Broker并行写入,并可以由不同的消费者并行读取。因此,更多的分区通常会导致更高的吞吐量,不过单从吞吐量的角度来看,我们能够从每个Broker有10个分区的kafka集群,就已经能够达到最大的吞吐量了。您可能需要更多的主题分区来支持您的应用程序逻辑。

但是,太多的分区可能导致更多的端到端的延迟。每个主题的分区越多,对生产者的批处理就越少。每个Broker的主题分区越多,每个follow副本获取请求的开销就越大。每个fetch请求必须去枚举自己感兴趣的分区,并且每个leader副本必须去检查状态,同时从请求的每个分区中去fetch数据,这通常会导致较小的磁盘I/O。因此,越多的分区,可能会导致更长的commit time和更高的cpu使用,最终导致较长的排队延迟。

提交时间的增加和更高的CPU负载会导致所有共享同一个Kafka集群的客户端端到端延迟的增加,即使是那些只生产和使用少量主题分区的客户端来说,也是如此。

我们使用两个topic来做此次测试。一个Topic有9个生产者生产5MB/s的数据,然后有一个对应9个消费者的消费者组。这个实验持续了几天,我们将这个主题中的分区数量从108个逐步增加到7200个(每个Broker8000个),每个步骤运行一个小时。第二个主题在整个实验运行期间有9个分区和9个生产者,每个生产者向一个分区和一个对应的消费者组(每个分区一个)生产消息,该主题每秒生产一个512bytes的数据。

下图显示了分区数量对客户端访问9分区主题的99%的端到端延迟的影响,随着每个broker上分区数的增加,clients的端到端延时大致呈线性增加趋势。分区数的增加会推高broker上的CPU负载同时拖慢所有clients的备份,即使是对那些只与固定分区数量交互的clients而言,也会抬高端到端延迟。

一文理解kafka端到端的延迟_第6张图片

为了减少延时,最好还是限制每个broker上的分区数,方法是减少总的分区数或扩展集群。你还可以通过增加fetcher线程数量的方式来改善commit time。

broker节点负载对延迟的影响

我们已经讨论了Broker上的负载导致增加排队延迟,从而增加了端到端的延迟,很容易看出为什么请求速率的增加会加剧排队延迟,因为更多的请求会导致更大的队列。

broker节点上高资源利用率(磁盘或cpu)可能导致更高的队列的延迟,并且延迟的增长会随着资源利用率的增长呈指数级增长。这是一个可以有排队理论解释的已知属性: Kingman公式证明等待某种资源的时间正比于资源繁忙程度/资源空闲程度(% of time resource is busy)/(% of time resource is idle).

一文理解kafka端到端的延迟_第7张图片

由于延迟随资源利用率呈指数增长,如果broker中的任何资源的利用率接近100%,您可能会看到很高的延迟。通过减少每个Broker的资源使用(比如减少每个broker的链接数,请求以及分区数)或扩展集群来整体降低每个broker节点的资源使用率,在这种情况可以显著降低延迟。保持负载在broker之间平均通常情况下是非常有用的,同时也可以均匀地或基于负载分布分区副本也能降低尾部延迟。

因此,通常情况下,负责kafka集群的SRE团队需要自动检测Broker节点上的高资源利用率(磁盘和CPU),然后重新平衡集群上的分区,以便更均匀地在Broker之间重新分配负载,或者在需要时扩展集群。而如果使用云厂商提供的kafka服务,则可以适当避免这类事情的发生,因为云服务会去做相关的事情。

总结

我们已经演示了,在为吞吐量扩展客户机和分区时的边界延迟要求时可以通过限制每个broker的连接数、分区数和请求速率来限制每个broker的使用。

边界尾延迟需要额外注意减少来自客户机的任何突发(连接和请求)或应用程序行为中的差异。

均匀加载的broker节点对于最小化尾部延迟也很重要,因为它受到最慢broker的影响。

如下是一些相关扩展的文章,可能有助于我们控制整体延迟:

  • Optimizing Your Apache Kafka Deployment提供配置Kafka部署的指导方针,以优化各种目标:吞吐量、延迟、持久性和可用性(公众号回复:kafka可获取资料)
  • How to Choose the Number of Topics/Partitions in a Kafka Cluster?(2016) 和follow-updates有一些kafka 2.0版本的优化
  • Improving batching with the new sticky partitioner
  • KIP-345: 引入静态成员协议来减少消费者的再平衡

公众号

你可能感兴趣的:(运维,SRE,kafka)