Kafka监控

五个维度来监控Kafka

  • 监控Kafka集群所在的主机
  • 监控Kafka broker JVM的表现
  • 监控Kafka Broker的性能
  • 监控Kafka客户端的性能。这里的所指的是广义的客户端——可能是指我们自己编写的生产者、消费者,也有可能是社区帮我们提供的生产者、消费者,比如说Connect的Sink/Source或Streams等
  • 监控服务器之间的交互行为

主机监控

个人认为对于主机的监控是最重要的。因为很多线上环境问题首先表现出来的症状就是主机的某些性能出现了明显的问题。此时通常是运维人员首先发现了它们然后告诉我们这台机器有什么问题,对于Kafka主机监控通常是发现问题的第一步。下面列出了常见的指标,包括CPU、内存、带宽等数据。需要注意的是CPU使用率的统计。可能大家听过这样的提法:我的Kafka Broker CPU使用率是400%,怎么回事?对于这样的问题,我们首先要搞清楚这个使用率是怎么观测出来的? 很多人拿top命令中的vss或rss字段来表征CPU使用率,但实际上它们并不是真正的CPU使用率——那只是所有CPU共同作用于Kafka进程所花的时间片的比例。举个例子,如果机器上有16个CPU,那么只要这些值没有超过或接近1600, 那么你的CPU使用率实际上是很低的。因此要正确理解这些命令中各个字段的含义。

  • 机器负载(Load)
  • CPU使用率
  • 内存使用率(free、used)
  • 磁盘IO
  • 网络IO
  • TCP连接
  • 打开文件数
  • Inode使用情况

监控JVM

Kafka本身是一个普通的Java进程,所以任何适用于JVM监控的方法对于监控Kafka都是相通的。第一步就是要先了解Kafka应用。比方说了解Kafka broker JVM的GC频率和延时都是多少,每次GC后存活对象的大小是怎样的等。了解了这些信息我们才能明确后面调优的方向。当然,我们毕竟不是特别资深的JVM专家,因此也不必过多追求繁复的JVM监控与调优。只需要关注大的方面即可。

Per-Broker监控

首先要确保Broker进程是启动状态?这听起来好像有点搞笑,但我的确遇到过这样的情况。比如当把Kafka部署在Docker上时就容易出现进程启动但服务没有成功启动的情形。正常启动下,一个Kafka服务器起来的时候,应该有两个端口,一个端口是9092常规端口,会建一个TCP链接。还有一个端口是给JMX监控用的。当然有多台broker的话,那么controller机器会为每台broker都维护一个TCP连接。在实际监控时可以有意识地验证这一点。

对于Broker的监控,我们主要是通过JMX指标来做的。用过Kafka的人知道,Kafka社区提供了特别多的JMX指标,其中很多指标用处不大。我这里列了一些比较重要的:首先是broker机器每秒出入的字节数,就是类似于我可以监控网卡的流量,一定要把这个指标监控起来,并实时与你的网卡带宽进行比较——如果发现该值非常接近于带宽的话,就证明broker负载过高,要么增加新的broker机器,要么把该broker上的负载均衡到其他机器上。

另外还有两个线程池空闲使用率要关注,最好确保它们的值都不要低于30%,否则说明Broker已经非常的繁忙。 此时需要调整线程池线程数。

接下来是监控broker服务器的日志。日志中包含了非常丰富的信息。这里所说的日志不仅是broker服务器的日志,还包括Kafka controller的日志。我们需要经常性地查看日志中是否出现了OOM错误抑或是时刻关注日志中抛出的ERROR信息。

我们还需要监控一些关键后台线程的运行状态。个人认为有两个比较重要的线程需要监控:一个Log Cleaner线程——该线程是执行数据压实操作的,如果该线程出问题了,用户通常无法感知到,然后会发现所有compact策略的topic会越来越大直到占满所有磁盘空间;另一个线程就是副本拉取线程,即follower broker使用该线程实时从leader处拉取数据。如果该线程“挂掉”了,用户通常也是不知道的,但会发现follower不再拉取数据了。因此我们一定要定期地查看这两个线程的状态,如果发现它们意外终止,则去日志中寻找对应的报错信息。

  • Broker进程是否启动?

    • Docker?
    • Established
  • JMX指标
    • BytesIn/BytesOut(接近宽带)
    • NetworkProcessorAvgIdlePercent(>30%)
    • RequestHandlerAvgIdlePercent(>30%)
    • OfflinePartitionsCount(>0)
    • ISRShrink/ISRExpand(是否频繁)
    • EventQueueTimeMs/EventQueueSize(controller only)
  • serverlog
    • ERROR****
    • OOM
  • 线程运行状态监控
    • kafka-log-cleaner-thread-***
    • ReplicaFetcherThread-***

Clients监控

客户端监控这块,我这边会分为两个,分别讨论对生产者和消费者的监控。生产者往Kafka发消息,在监控之前我们至少要了解一下客户端机器与Broker端机器之间的RTT是多少。对于那种跨数据中心或者是异地的情况来说,RTT本来就很大,如果不做特殊的调优,是不可能有太高的TPS的。目前Kafka producer是双线程的设计机制,分为用户主线程和Sender线程,当这个Sender线程挂了的时候,前端用户是不感知的,但表现为producer发送消息失败,所以用户最好监控一下这个Sender线程的状态。

Producer

  • RTT(round-trip time)
  • Sender线程运行状态
    • kafka-producer-network-thread|***
  • JMX指标
    • bufferpoll-wait-time/waiting-threads
    • request-latency

还有就是监控PRODUCE请求的处理延时。一条消息从生产者端发送到Kafka broker进行处理,之后返回给producer的总时间。整个链路中各个环节的耗时最好要做到心中有数。因为很多情况下,如果你要提升生产者的TPS,了解整个链路中的瓶颈后才能做到有的放矢。后面我会讨论如何拆解这条链路。

现在说说消费者。这里的消费者说的是新版本的消费者,也就是java consumer。

Consumer

  • RTT(round-trip time)
  • Heartbeat线程运行状态
    • kafka-coordinator-heartbeat-thread-***
  • JMX指标
    • records-lag
    • records-lead(KIP-223)
  • 分区分配
  • 分区倾斜
  • Group rebalance
    • Join rate
    • Syncrate

社区已经非常不推荐再继续使用老版本的消费者了。新版本的消费者也是双线程设计,后面有一个心跳线程,如果这个线程挂掉的话,前台线程是不知情的。所以,用户最好定期监控该心跳线程的存活情况。心跳线程定期发心跳请求给Kafka服务器,告诉Kafka,这个消费者实例还活着,以避免coordinator错误地认为此实例已“死掉”从而开启rebalance。Kafka提供了很多的JMX指标可以用于监控消费者,最重要的消费进度滞后监控,也就是所谓的consumerlag。

假设producer生产了100条消息,消费者读取了80条,那么lag就是20。显然落后的越少越好,这表明消费者非常及时,用户也可以用工具行命令来查lag,甚至写Java的API来查。与lag对应的还有一个lead指标,它表征的是消费者领先第一条消息的进度。比如最早的消费位移是1,如果消费者当前消费的消息是10,那么lead就是9。对于lead而言越大越好,否则表明此消费者可能处于停顿状态或者消费的非常慢,本质上lead和lag是一回事。

除了以上这些,我们还需要监控消费者组的分区分配情况,避免出现某个实例被分配了过多的分区,导致负载严重不平衡的情况出现。一般来说,如果组内所有消费者订阅的是相同的主题,那么通常不会出现明显的分配倾斜。一旦各个实例订阅的主题不相同且每个主题分区数参差不齐时就极易发生这种不平衡的情况。Kafka目前提供了3种策略来帮助用户完成分区分配,最新的策略是黏性分配策略,它能保证绝对的公平,大家可以去试一下。

最后就是要监控rebalance的时间——目前来看,组内超多实例的rebalance性能很差,可能都是小时级别的。而且比较悲剧的是当前无较好的解决方案。所以,如果你的Consumer特别特别多的话,一定会有这个问题,你监控一下两个步骤所用的时间,看看是否满足需求,如果不能满足的话,看看能不能把消费者去除,尽量减少消费者数量。

Inter-Broker监控

最后一个维度就是监控Broker之间的表现,主要是指副本拉取。Follower副本实时拉取leader处的数据,我们自然希望这个拉取过程越快越好。Kafka提供了一个特别重要的JMX指标,叫做备份不足的分区数,比如说我规定了这条消息,应该在三个Broker上面保存,假设只有一个或者两个Broker上保存该消息,那么这条消息所在的分区就被称为“备份不足”的分区。这种情况是特别关注的,因为有可能造成数据的丢失。《Kafka权威指南》一书中是这样说的:如果你只能监控一个Kafka JMX指标,那么就监控这个好了,确保在你的Kafka集群中该值是永远是0。一旦出现大于0的情形赶紧处理。

  • No.1 JMX metric

    • under replicated partitions
  • under minIsr partitions
  • active controller count
  • lag between follower and leader

还有一个比较重要的指标是表征controller个数的。整个集群中应该确保只能有一台机器的指标是1,其他全应该是0,如果你发现有一台机器是2或者是3,一定是出现脑裂了,此时应该去检查下是否出现了网络分区。Kafka本身是不能对抗脑裂的,完全依靠Zookeeper来做,但是如果真正出现网络分区的话,也是没有办法处理的,不如赶快fail fast掉。

系统调优

Kafka监控的一个主要的目的就是调优Kafka集群。这里罗列了一些常见的操作系统级的调优。

首先是保证页缓存的大小——至少要设置页缓存为一个日志段的大小。我们知道Kafka大量使用页缓存,只要保证页缓存足够大,那么消费者读取消息时就有大概率保证它能够直接命中页缓存中的数据而无需从底层磁盘中读取。故只要保证页缓存要满足一个日志段的大小。

第二是调优文件打开数。很多人对这个资源有点畏手畏脚。实际上这是一个很廉价的资源,设置一个比较大的初始值通常都是没有什么问题的。

第三是调优vm.max_map_count参数。主要适用于Kafka broker上的主题数超多的情况。Kafka日志段的索引文件是用映射文件的机制来做的,故如果有超多日志段的话,这种索引文件数必然是很多的,极易打爆这个资源限制,所以对于这种情况一般要适当调大这个参数。

第四是swap的设置。很多文章说把这个值设为0,就是完全禁止swap,我个人不建议这样,因为当你设置成为0的时候,一旦你的内存耗尽了,Linux会自动开启OOM killer然后随机找一个进程杀掉。这并不是我们希望的处理结果。相反,我建议设置该值为一个比较接近零的较小值,这样当我的内存快要耗尽的时候会尝试开启一小部分swap,虽然会导致broker变得非常慢,但至少给了用户发现问题并处理之的机会。

第五JVM堆大小。首先鉴于目前Kafka新版本已经不支持Java7了,而Java 8本身不更新了,甚至Java9其实都不做了,直接做Java10了,所以我建议Kafka至少搭配Java8来搭建。至于堆的大小,个人认为6-10G足矣。如果出现了堆溢出,就提jira给社区,让他们看到底是怎样的问题。因为这种情况下即使用户调大heap size,也只是延缓OOM而已,不太可能从根本上解决问题。

  • 页缓存

    • 至少要容纳一个日志段的大小
  • ulimit -n
    • 设置大一点没问题!
  • vm.max_map_count(超多topic时候需要设置)
  • 设置vm.swappiness接近0,但不要为0
  • JVM heap size
    • G1
    • 6GB~8GB
  • 多块专属磁盘
  • JBOD vs RAID

最后,建议使用专属的多块磁盘来搭建Kafka集群。自1.1版本起Kafka正式支持JBOD,因此没必要在底层再使用一套RAID了。

kafka调优的四个层面

Kafka调优通常可以从4个维度展开,分别是吞吐量、延迟、持久性和可用性。在具体展开这些方面之前,我想先建议用户保证客户端与服务器端版本一致。如果版本不一致,就会出现向下转化的问题。举个例子,服务器端保存高版本的消息,当低版本消费者请求数据时,服务器端就要做转化,先把高版本消息转成低版本再发送给消费者。这件事情本身就非常非常低效。很多文章都讨论过Kafka速度快的原因,其中就谈到了零拷贝技术——即数据不需要在页缓存和堆缓存中来回拷贝。

简单来说producer把生产的消息放到页缓存上,如果两边版本一致,可以直接把此消息推给Consumer,或者Consumer直接拉取,这个过程是不需要把消息再放到堆缓存。但是你要做向下转化或者版本不一致的话,就要额外把数据再堆上,然后再放回到Consumer上,速度特别慢。

Kafka调优 – 吞吐量

调优吞吐量就是我们想用更短的时间做更多的事情。这里列出了客户端需要调整的参数。前面说过了producer是把消息放在缓存区,后端Sender线程从缓存区拿出来发到broker。这里面涉及到一个打包的过程,它是批处理的操作,不是一条一条发送的。因此这个包的大小就和TPS息息相关。通常情况下调大这个值都会让TPS提升,但是也不会无限制的增加。不过调高此值的劣处在于消息延迟的增加。除了调整batch.size,设置压缩也可以提升TPS,它能够减少网络传输IO。当前Lz4的压缩效果是最好的,如果客户端机器CPU资源很充足那么建议开启压缩。

  • Producer

    • batch.size:设置一个较大的值
    • linger.ms:100或更大的值
    • compression.type=lz4:启动压缩,降低网络IO
    • acks=1
    • retries=0
    • buffer.memory:适当增加缓冲池大小,容乃更多batch
  • Consumer
    • fetch.min.bytes:增加单个request处理的最小字节数

对于消费者端而言,调优TPS并没有太好的办法,能够想到的就是调整fetch.min.bytes。适当地增加该参数的值能够提升consumer端的TPS。对于Broker端而言,通常的瓶颈在于副本拉取消息时间过长,因此可以适当地增加num.replica.fetcher值,利用多个线程同时拉取数据,可以加快这一进程。

Kafka调优 – 延时

所谓的延时就是指消息被处理的时间。某些情况下我们自然是希望越快越好。针对这方面的调优,consumer端能做的不多,简单保持fetch.min.bytes默认值即可,这样可以保证consumer能够立即返回读取到的数据。讲到这里,可能有人会有这样的疑问:TPS和延时不是一回事吗?假设发一条消息延时是2ms,TPS自然就是500了,因为一秒只能发500消息,其实这两者关系并不是简单的。因为我发一条消息2毫秒,但是如果把消息缓存起来统一发,TPS会提升很多。假设发一条消息依然是2ms,但是我先等8毫秒,在这8毫秒之内可能能收集到一万条消息,然后我再发。相当于你在10毫秒内发了一万条消息,大家可以算一下TPS是多少。事实上,Kafka producer在设计上就是这样的实现原理。

Kafka调优 – 消息持久性

消息持久化本质上就是消息不丢失。Kafka对消息不丢失的承诺是有条件的。以前碰到很多人说我给Kafka发消息,发送失败,消息丢失了,怎么办?严格来说Kafka不认为这种情况属于消息丢失,因为此时消息没有放到Kafka里面。Kafka只对已经提交的消息做有条件的不丢失保障。

如果要调优持久性,对于producer而言,首先要设置重试以防止因为网络出现瞬时抖动造成消息发送失败。一旦开启了重试,还需要防止乱序的问题。比如说我发送消息1与2,消息2发送成功,消息1发送失败重试,这样消息1就在消息2之后进入Kafka,也就是造成乱序了。如果用户不允许出现这样的情况,那么还需要显式地设置max.in.flight.requests.per.connection为1。

  • Producer

    • retries:设置为1或更大的值
    • acks=all
    • max.in.flight.requests.per.connection=1:防止乱序
  • Broker
    • default.replication.factor >= 3
    • auto.create.topics.enable=false
    • min.insync.replicas=2
    • unclean.leader.election.enable=false
    • 设置log.flush.interval.messages,log.flush.interval.ms
  • Consumer
    • auto.commit.enble=false

如上列出的其他参数都是很常规的参数,比如unclean.leader.election.enable参数,最好还是将其设置成false,即不允许“脏”副本被选举为leader。

Kafka调优 – 可用性

最后是可用性,与刚才的持久性是相反的,我允许消息丢失,只要保证系统高可用性即可。因此我需要把consumer心跳超时设置为一个比较小的值,如果给定时间内消费者没有处理完消息,该实例可能就被踢出消费者组。我想要其他消费者更快地知道这个决定,因此调小这个参数的值。

定位性能瓶颈

下面就是性能瓶颈,严格来说这不是调优,这是解决性能问题。对于生产者来说,如果要定位发送消息的瓶颈很慢,我们需要拆解发送过程中的各个步骤。就像这张图表示的那样,消息的发送共有6步。第一步就是生产者把消息放到Broker,第二、三步就是Broker把消息拿到之后,写到本地磁盘上,第四步是follower broker从Leader拉取消息,第五步是创建response;第六步是发送回去,告诉我已经处理完了。

这六步当中你需要确定瓶颈在哪?怎么确定?——通过不同的JMX指标。比如说步骤1是慢的,可能你经常碰到超时,你如果在日志里面经常碰到request timeout,就表示1是很慢的,此时要适当增加超时的时间。如果2、3慢的情况下,则可能体现在磁盘IO非常高,导致往磁盘上写数据非常慢。倘若是步骤4慢的话,查看名为remote-time的JMX指标,此时可以增加fetcher线程的数量。如果5慢的话,表现为response在队列导致待的时间过长,这时可以增加网络线程池的大小。6与1是一样的,如果你发现1、6经常出问题的话,查一下你的网络。所以,就这样来分解整个的耗时。这是到底哪一步的瓶颈在哪,需要看看什么样的指标,做怎样的调优。

Java Consumer的调优

最后说一下Consumer的调优。目前消费者有两种使用方式,一种是同一个线程里面就直接处理,另一种是我采用单独的线程,consumer线程只是做获取消息,消息真正的处理逻辑放到单独的线程池中做。这两种方式有不同的使用场景:第一种方法实现较简单,因为你的消息处理逻辑直接写在一个线程里面就可以了,但是它的缺陷在于TPS可能不会很高,特别是当你的客户端的机器非常强的时候,你用单线程处理的时候是很慢的,因为你没有充分利用线程上的CPU资源。第二种方法的优势是能够充分利用底层服务器的硬件资源,TPS可以做的很高,但是处理提交位移将会很难。

最后说一下参数,也是网上问的最多的,这几个参数到底是做什么的。第一个参数,就是控制consumer单次处理消息的最大时间。比如说设定的是600s,那么consumer给你10分钟来处理。如果10分钟内consumer无法处理完成,那么coordinator就会认为此consumer已死,从而开启rebalance。

Coordinator是用来管理消费者组的协调者,协调者如何在有效的时间内,把消费者实例挂掉的消息传递给其他消费者,就靠心跳请求,因此可以设置heartbeat.interval.ms为一个较小的值,比如5s。

原文链接

Kafka监控与调优的更多相关文章

  1. DataPipeline |ApacheKafka实战作者胡夕:Apache Kafka监控与调优

    https://baijiahao.baidu.com/s?id=1610644333184173190&wfr=spider&for=pc DataPipeline |ApacheK ...

  2. DataPipeline |《Apache Kafka实战》作者胡夕:Apache Kafka监控与调优

    胡夕 <Apache Kafka实战>作者,北航计算机硕士毕业,现任某互金公司计算平台总监,曾就职于IBM.搜狗.微博等公司.国内活跃的Kafka代码贡献者. 前言 虽然目前Apache ...

  3. 使用 Elastic Stack 来监控和调优 Golang 应用程序

    Golang 因为其语法简单,上手快且方便部署正被越来越多的开发者所青睐,一个 Golang 程序开发好了之后,势必要关心其运行情况,今天在这里就给大家介绍一下如果使用 Elastic Stack 来 ...

  4. Java系列笔记(4) - JVM监控与调优

    目录 参数设置收集器搭配启动内存分配监控工具和方法调优方法调优实例     光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分析原因并解决之.通过学习,我觉得JVM ...

  5. JVM监控与调优

    目录 参数设置收集器搭配启动内存分配监控工具和方法调优方法调优实例     转:http://www.cnblogs.com/zhguang/p/java-jvm-gc.html光说不练假把式,学习J ...

  6. 实例详解 DB2 排序监控和调优

    实例详解 DB2 排序监控和调优http://automationqa.com/forum.php?mod=viewthread&tid=2882&fromuid=2

  7. [java] JVM监控与调优

    原文出处:http://www.cnblogs.com/zhguang/p/java-jvm-gc.html   光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分 ...

  8. Tomcat性能调优-JVM监控与调优

    参数设置 在Java虚拟机的参数中,有3种表示方法用"ps -ef |grep "java"命令,可以得到当前Java进程的所有启动参数和配置参数: 标准参数(-),所有 ...

  9. JVM监控和调优常用命令工具总结

    JVM监控和调优 在Java应用和服务出现莫名的卡顿.CPU飙升等问题时总是要分析一下对应进程的JVM状态以定位问题和解决问题并作出相应的优化,在这过程中Java自带的一些状态监控命令和图形化工具就非 ...

随机推荐

  1. javascript 字面量

    https://www.cnblogs.com/radius/p/6874165.html

  2. Visual Staudio 2015 打开指定文件,定位到指定文件目录下

    Visual Staudio 2015 项目定位文件位置 每次使用的Visual Staudio 2015 写代码的时候总是打开了.cs文件或xaml文件时, 还要手动去找该 文件位置,每次都要翻好大 ...

  3. 1、Docker 架构详解

    本文来自clouldman ,有增删. Docker 的核心组件包括: Docker 客户端 - Client Docker 服务器 - Docker daemon Docker 镜像 - Image ...

  4. 沉淀再出发:Bean,JavaBean,POJO,VO,PO,EJB等名词的异同

    沉淀再出发:Bean,JavaBean,POJO,VO,PO,EJB等名词的异同 一.前言 想必大家都有这样的困惑,接触的东西越多却越来越混乱了,这个时候就要进行对比和深入的探讨了,抓住每一个概念背后 ...

  5. Python 处理脚本的命令行参数-getopt

    # -*- coding:utf-8 -*- import sys def test(): """ 参数列表:sys.argv 参数个数:len(sys.argv) 脚本 ...

  6. [T-ARA][지난 달력][旧挂历]

    歌词来源:http://music.163.com/#/song?id=29343994 作曲 : Rocoberry [作曲 : Rocoberry] [作曲 : Rocoberry] 作词 : R ...

  7. windows系统查看端口占用程序方法

    开始---->运行---->cmd,或者是window+R组合键,调出命令窗口   输入命令:netstat -ano,列出所有端口的情况.在列表中我们观察被占用的端口,比如是49157, ...

  8. 「BZOJ3226」[Sdoi2008]校门外的区间

    题目 首先是开闭区间的处理,我们把\(1.5\)这种数加进来,用\([1.5,6]\)来表示\((2,6]\) 根据离散数学的基本知识,尝试把五个操作转化成人话 把\([x,y]\)变成\(1\) 把 ...

  9. seek()和tell()在文件里转移

    Seek()方法允许在输入和输出流移动到任意的位置,seek()有好几种形式.包含:seekp() 方法和seekg()方法,p是put的意思,g是get的意思:其中输入流里用seekg()函数,输出 ...

  10. QTP基本方法4------手动写入信息到测试结果报告中

    可以使用写代码的方式添加结果信息到测试结果报告中. 结构:reporter.ReportEvent result,object,details,path result:状态:4种状态:micPass. ...