目录
Kafka监控与告警
Kafka中的监控和警报至关重要,原因如下:
- 主动问题检测
监控有助于实时检测Kafka集群中的问题或异常。通过根据预定义的阈值或条件设置警报,您可以在潜在问题升级并影响系统性能或可用性之前主动识别这些问题。 - 对事件的快速响应
警报使您能够在满足某些预定义条件时接收通知,例如高消费延迟、代理故障或复制分区不足。这使您能够快速响应事件,调查根本原因,并采取纠正措施,最大限度地减少停机时间和数据丢失。 - 维护服务级别协议(SLA)
监控和警报有助于确保Kafka集群在性能、可用性和可靠性方面符合定义的服务级别协议。通过监控关键指标并设置违反SLA的警报,您可以采取主动措施来维持所需的服务级别。 - 容量规划和资源优化
基于资源利用率指标的警报有助于容量规划和优化资源。通过接收有关资源限制或即将出现的容量问题的警报,您可以主动扩展集群,以处理增加的工作负载并避免性能下降。 - 安全事件响应
警报对于检测和响应Kafka中的安全事件至关重要,例如未经授权的访问尝试、异常用户行为或潜在的安全漏洞。通过设置与安全相关的警报,您可以快速识别和减轻安全威胁,以保护您的Kafka数据。
总之,Kafka监控和警报对于维护Kafka集群的运行状况、性能和安全性至关重要,能够实现主动的问题检测、快速的事件响应和遵守SLA。
1 解决方案
1.2 基础知识
Kafka提供了大量的指标,监控和告警要做的就是按照需求查询需要的指标,给针对指标指定阈值,当指标的值超出阈值时发送告警通知。所有指标都可以通过Java 管理扩展(Java management extension,JMX)接口访问。
JMX
JMX(Java Management Extensions)是Java平台上的一种管理和监控技术,它允许开发人员在运行时管理和监控Java应用程序。通过JMX,开发人员可以暴露应用程序中的各种信息和操作,例如性能指标、配置信息和操作方法,然后可以通过JMX客户端进行访问和控制。
详情参考附录:什么是JMX
监控指标代理
要在外部监控系统中使用这些指标,最简单的方法是将监控系统中负责收集指标的代理连接到Kafka上。
代理可以是:
- 运行在监控系统中的一个独立进程,并用Nagios XIcheck_jmx插件或jmxtrans连接到Kafka的JMX接口上。
- 也可以直接在Kafka中运行一个JMX代理,然后通过HTTP连接(比如Jolokia或MX4J)获取指标。
查看KafkaJMX远程端口
JMX端口是broker配置信息的一部分,保存在ZooKeeper中,如果监控系统要直接连到Kafka的JMX端口,那么可以先从ZooKeeper获取端口信息。出于安全方面的考虑,Kafka默认禁用了远程JMX。这是因为JMX不仅可以作为查看应用程序状态的窗口,它还允许执行代码。强烈建议使用嵌入应用程序中的JMX指标代理。
zkCli.sh get /brokers/ids/0
可以看到返回的结果,jmx端口是-1,也就是没有开启远程JMX连接。
{
"features": {},
"listener_security_protocol_map": {
"PLAINTEXT": "PLAINTEXT"
},
"endpoints": ["PLAINTEXT://172.26.143.96:9092"],
"jmx_port": -1,
"port": 9092,
"host": "172.26.143.96",
"version": 5,
"timestamp": "1716079501534"
}
如果要启用它,则必须保护好端口。
使用如下命令启动Kafka可以开启远程JMX。
JMX_PORT=19092 $KAFKA_CLUSTER_HOME/kafka-1/bin/kafka-server-start.sh $KAFKA_CLUSTER_HOME/kafka-1/config/server.properties
可以看到JMX端口已经赋值了:
{
"features": {},
"listener_security_protocol_map": {
"PLAINTEXT": "PLAINTEXT"
},
"endpoints": ["PLAINTEXT://172.26.143.96:9092"],
"jmx_port": 19092,
"port": 9092,
"host": "172.26.143.96",
"version": 5,
"timestamp": "1716099839130"
}
如果你所仕的组织没有监控Java应用程序的经验,那么可以考愿使用第三方提供的监控服务。很多第三方供应商的服务包已经包含了监控代理、指标收集点、存储、图形可视化和告警。它们还能进一步帮你搭建符合要求的监控代理。
1.3 真实案例
下面是一个典型的Kafka监控与告警解决方案,利用了第三方开源的工具,笔者公司也使用了相似的方案。
使用的核心系统有:
Kafka Exporter:
Kafka Exporter 是一个用于监控 Apache Kafka 集群的工具,它可以将 Kafka 集群的指标导出到 Prometheus 监控系统中。通过使用 Kafka Exporter,用户可以实时监控 Kafka 集群的健康状况、消息流量等指标。
Kafka Exporter 的主要功能包括:
- 导出 Kafka 集群的指标到 Prometheus 格式
- 支持多个 Kafka 集群的监控
- 提供丰富的指标,包括消息生产与消费速率、分区偏移量、副本状态等
官网链接:https://github.com/danielqsj/kafka_exporter/blob/master/README.md
Promethus
Prometheus 是一个开源的系统监控和警报工具包。它最初由SoundCloud开发,并于2012年在Cloud Native Computing Foundation下发布。Prometheus可以收集、存储和查询各种指标,并提供强大的查询语言和灵活的警报机制。Prometheus主要用于对基础设施的监控,几乎所有东西都可以通过Prometheus进行监控。
官网链接:https://prometheus.io/docs/introduction/overview/
来自官网的架构图:
Promethus Alert Manager
Prometheus Alertmanager是一个用于处理和路由警报的组件,它与Prometheus紧密集成。Alertmanager可以接收来自Prometheus的警报,并根据配置的规则对这些警报进行处理、路由和通知。
Alertmanager具有以下主要功能:
- 警报路由:Alertmanager可以根据配置的路由规则将警报路由到不同的接收端,如Email、Slack、PagerDuty等。
- 抑制重复警报:Alertmanager可以抑制重复的警报,以避免发送多个相同的警报通知。
- 静默期:Alertmanager支持设置静默期,以在某些情况下暂时屏蔽警报的发送。
- 集成第三方服务:Alertmanager可以与各种第三方服务集成,以实现更灵活的警报通知机制。
官网链接:https://prometheus.io/docs/alerting/latest/alertmanager/
Grafana
Grafana 是一个流行的开源数据可视化工具,它提供了丰富的图表和面板,可以帮助用户轻松地创建、编辑和共享仪表盘,展示各种数据源的实时监控和分析结果。Grafana 支持多种数据源,包括 Prometheus、InfluxDB、Elasticsearch、MySQL 等,用户可以将这些数据源连接到 Grafana 中,并通过配置灵活的查询语言来展示数据。
Grafana 的主要特点包括:
- 可视化丰富:Grafana 提供了多种图表和面板类型,包括折线图、柱状图、热力图等,用户可以根据需求自定义展示数据的方式。
- 多数据源支持:Grafana 支持多种数据源,用户可以轻松地连接不同类型的数据源,并在同一个仪表盘中展示多个数据源的数据。
- 仪表盘共享:用户可以创建自定义的仪表盘,并将其共享给团队成员或其他用户,以便实时监控和共享数据分析结果。
- 警报功能:Grafana 还提供了警报功能,用户可以设置警报规则,并在触发条件时发送通知。
官网链接:https://grafana.com/grafana/dashboards
1.3 实际操作部署监控和告警系统
下面就具体的部署步骤来进行简单的演示。
1.2.1 部署Kafka Exporter
安装包下载地址:https://github.com/danielqsj/kafka_exporter/releases
依次运行命令:
# 下载安装包
wget https://github.com/danielqsj/kafka_exporter/releases/download/v1.7.0/kafka_exporter-1.7.0.linux-amd64.tar.gz
# 解压缩
sudo tar -xzvf kafka_exporter-1.7.0.linux-amd64.tar.gz -C /usr/local/bin
# 赋权限
sudo chmod -R 777 /usr/local/bin/kafka_exporter-1.7.0.linux-amd64
ls -l /usr/local/bin/kafka_exporter-1.7.0.linux-amd64
# 启动
/usr/local/bin/kafka_exporter-1.7.0.linux-amd64/kafka_exporter --kafka.server=172.26.143.96:9091 --kafka.server=172.26.143.96:9092 --kafka.server=172.26.143.96:9093
如果能成功访问地址:http://172.26.143.96:9308/metrics,看到Kafka的度量指标,说明安装成功。
1.2.2 部署Prometheus
官网下载地址:https://prometheus.io/download/#prometheus
依次运行命令:
wget https://github.com/prometheus/prometheus/releases/download/v2.52.0/prometheus-2.52.0.linux-amd64.tar.gz
sudo tar -xzvf prometheus-2.52.0.linux-amd64.tar.gz -C /usr/local/bin
sudo chmod -R 777 /usr/local/bin/prometheus-2.52.0.linux-amd64
ls -l /usr/local/bin/prometheus-2.52.0.linux-amd64
编辑Prometheus的配置文件:
vim /usr/local/bin/prometheus-2.52.0.linux-amd64/prometheus.yml
修改监控指标来源为Kafka_export的地址:“172.26.143.96:9308”
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ["172.26.143.96:9308"]
最后启动Prometheus:
/usr/local/bin/prometheus-2.52.0.linux-amd64/prometheus --config.file=/usr/local/bin/prometheus-2.52.0.linux-amd64/prometheus.yml
如果能成功访问地址:http://172.26.143.96:9090/targets,说明部署成功
1.2.3 部署AlertManger
官网下载地址:https://prometheus.io/download/#alertmanager
依次运行命令:
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
sudo tar -xzvf alertmanager-0.27.0.linux-amd64.tar.gz -C /usr/local/bin
sudo chmod -R 777 /usr/local/bin/alertmanager-0.27.0.linux-amd64
ls -l /usr/local/bin/alertmanager-0.27.0.linux-amd64
集群模式启动
/usr/local/bin/alertmanager-0.27.0.linux-amd64/alertmanager --config.file=/usr/local/bin/alertmanager-0.27.0.linux-amd64/alertmanager.yml --web.listen-address=172.26.143.96:9095 --cluster.listen-address=:9096
单机模式启动:
/usr/local/bin/alertmanager-0.27.0.linux-amd64/alertmanager --config.file=/usr/local/bin/alertmanager-0.27.0.linux-amd64/alertmanager.yml --web.listen-address=172.26.143.96:9095 --cluster.listen-address=\
如果能成功访问地址:http://172.26.143.96:9095/#/alerts,说明部署成功。
1.2.4 添加告警规则
在Prometheus的安装目录下创建该井规则文件:
/usr/local/bin/prometheus-2.52.0.linux-amd64/kafka_alert_rules.yml
groups:
- name: kafka-alerts
rules:
- alert: KafkaBrokerDown
expr: kafka_brokers < 4
for: 1m
labels:
severity: error
annotations:
summary: "The number of brokers {{$value}} of cluser {{$labels.env}} is less than 4."
description: "Brokers's number is less than 4."
然后修改配置文件并重启Prometheus:
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- 172.26.143.96:9095
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
- "kafka_alert_rules.yml"
1.2.5 修改告警接收配置
修改AlertManager配置文件,将webhook地址配置成要接受告警的服务地址。
vim /usr/local/bin/alertmanager-0.27.0.linux-amd64/alertmanager.yml
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
receiver: 'web.hook'
receivers:
- name: 'web.hook'
webhook_configs:
- url: 'http://127.0.0.1:5001/kafka/alerts'
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'dev', 'instance']
1.2.6 部署Grafana
官网下载地址:https://grafana.com/grafana/download?pg=graf&plcmt=deploy-box-1
依次运行命令:
wget https://dl.grafana.com/enterprise/release/grafana-enterprise-11.0.0.linux-amd64.tar.gz
sudo tar -zxvf grafana-enterprise-11.0.0.linux-amd64.tar.gz -C /usr/local/bin
sudo chmod -R 777 /usr/local/bin/grafana-v11.0.0
ls -l /usr/local/bin/grafana-v11.0.0
复制配置文件并启动:
cp /usr/local/bin/grafana-v11.0.0/conf/sample.ini /usr/local/bin/grafana-v11.0.0/conf/grafana.ini
/usr/local/bin/grafana-v11.0.0/bin/grafana server -homepath /usr/local/bin/grafana-v11.0.0 -config /usr/local/bin/grafana-v11.0.0/conf/grafana.ini
官网链接https://grafana.com/docs/grafana/latest/setup-grafana/start-restart-grafana/
如果能成功访问地址:http://172.26.143.96:3000,说明部署成功。
接下来还需要配置Prometheus的dashboard:
- 在左侧菜单选择添加数据源
填入Prometheus的地址:http://172.26.143.96:9095。
官网链接:https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/ - 导入dashboard
可以导入现成的dashboard模板,就不用自己创建了。我导入了编号7589的模板。 - 访问dashbaord链接
http://172.26.143.96:3000/d/jwPKIsniz/kafka-exporter-overview?orgId=1
2 监控指标详解
1.1 按指标的来源分类
并不是所有的指标都来自Kafka,我们能够获取到的指标通常可以分为5类。
2.1.1 应用程序指标
这些是可以通过JMX接口获取到的Kafka 指标。
2.1.2 日志
来自Kafka的另一种监控数据。因为日志是文本或结构化数据,不仅仅是数字,所以需要进行额外的处理。
2.1.3 基础设施指标
这些指标来自部署在Kafka前面的系统,这些系统也处在请求路径中,并在你的可控范
围之内,比如负载均衡器就属于这一类。
2.1.4 外部工具指标
这些数据来自Kafka以外的工具,类似于客户端,但在你的直接控制之下,它们的用途
与普通的Kafka客户端不一样,比如Kafka健康检查就属于这一类。
2.1.5 客户端指标
这些指标来自与集群相连的Kafka客户端。
2.2 按指标的目的分类
2.2.1 用于告警
用于告警的指标在短时间内(通常不会比对问题做出响应所需的时间长很多)非常有用。可以以小时或天为单位,将这些指标发送给能够对已知问题做出响应的自动化工具,或者没有自动化工具,则可以发送给运维人员。
2.2.2 用于调试
用于调试问题的数据需要保留较长的时间,因为你可能会频繁诊断已经存在一段时间的问题或对比较复杂的问题进行更深入的探究。这些数据在被收集到之后需要保留几天或几周,它们通常比较客观,或者是来自Kafka应用程序本身。
需要注意的是,并不一定要将这些数据收集到监控系统中。如果主要目的是调试问题,那么在调试问题时能够获取它们就可以了。不需要持续不断地收集数以万计的指标,从而让监控系统不堪重负。
2.2.3 历史数据
你可能还需要第三种类型的数据,也就是应用程序的历史数据。通常,历史
数据被用于容量管理,它们一般会包含资源的使用信息,比如计算资源、存
储和网络。这些指标需要保存很长一段时间,一般以年为单位。你可能还需
要收集额外的元数据,比如一个broker是何时加入或被移出集群的,这些元
数据将作为指标的上下文信息。
2.3 按使用者分类
2.3.1 自动化系统
如果指标被用在自动化系统中,那么它们就应该非常具体。我们可以收集大量的指标,并且每个指标都只描述一些小细节,这没有问题,因为这就是计算机存在的原因:处理大量的数据。指标越具体,就越容易对其进行自动化,因为越具体就越没有太多的解释空间。
2.3.2 人
如果指标是给人类看的,那么太多的指标会让人类不堪重负。在基于指标定义告警时,这一点尤为重要。人们很容易陷入“告警疲劳”,因为告警太多,很难知道问题究竟有多严重。我们也很难为每个指标定义合理的阈值并持续更新它们。如果告警过多或经常误报,我们就会怀疑告警是否正确地反映了应用程序的状态。
2.4 应用程序健康检查
需要用简单的方式来监控应用的整体健康状况:
- 使用外部进程来报告 broker的运行状态(健康检测)。
例如有一个定时任务每分钟向broker写入消息并成功读取。 - 在broker停止发送指标时发出告警(有时也叫作过时指标)。
虽然第二种方式也是可行的,但有时候很难区分是broker出了问题还是监控系统本身出了问题。
2.5 服务级别目标
对基础设施服务(如Kafka)来说,**服务级别目标(service level objective,SLO)**是一个非常重要的监控领域。服务供应商可以基于这些指标向客户宣称他们能够获得怎样的基础设施服务级别。
服务级别定义
在讨论Kafka的SLO之前,需要先统一一下涉及的术语。你可能经常听到工程师、经理、高管等错误地使用“服务级别”这个术语,导致人们对他们正在谈论的东西困惑重重。
服务级别指标(service level indicator,SLI) 是一种用于描述服务可靠性的指标。因为它与用户体验紧密相关,所以通常来说越客观越好。在一个处理请求的系统(如Kafka)中,这个指标通常用正常事件数量与总事件数量之间的比率来表示,比如Web服务器在处理请求时返回2xx、3xx或4xx响应的比例。
服务级别目标(SLO)也叫作服务级别阈值(service level threshold,SLT) 它将SLI与目标值组合在一起。服务级别目标的一种常见表示方法是使用数字9(99.9%就是39),尽管这不是必需的。SLO还需要包含一个时间窗口,通常以天为单位。例如,7天内99%发送给Web服务器的请求必须返回一个2xx、3xx或4xx响应。
服务级别协议(service level agreement,SLA) 是服务供应商和客户之间的一种契约。它通常会包含几个SLO,以及如何度量和报告它们、客户如何向服务供应商寻求支持、服务供应商如果不按照SLA执行将受到怎样的惩罚。例如,之前的SLO所对应的SLA可能是这样的:如果服务供应商在SLO服务期内暂停运营,那么就需要退还客户为此期间支付的所有费用。
哪些指标是好的SLI
通常,需要使用外部工具或系统来收集适用于SLI的指标。SLO可以用来描述你服务的田户的满意度,我们无法主观地得出这些指标。客户并不关心你是否认为你的服务运行正滑他们的体验才是关键。也就是说,基础设施指标没什么问题,外部工具指标也不错,但客户端指标可能是大多数SLI的最佳指标。
在请求响应和数据存储系统中常见的SLI:
- 可用性
- 延迟
- 质量
- 安全性
- 吞吐量
将SLO用于告警
SLO应该为你提供主要的告警。因为SLO是从客户的角度描述问题,而这些问题应该是首先要关注的。一般来说,如果一个问题不会对你的客户造成影响,就不会重要到要在以里把你叫醒的程度。SLO还会告诉你一些你不知道该如何检测的问题,因为你以前可能从未遇到过。
问题在于,我们很难直接将SLO作为告警。SLO的时间范围通常较长(比如一周),因为
我们希望提供给高层或客户的SLO是可重复查看的。此外,在发出SLO告警时,可能已经太迟了,因为你已经在SLO之外采取了行动。
有些人用派生值来设置预警,但在告警中使用SLO的最佳方式是观察SLO的燃烧率。
2.6 Broker的指标
broker提供了很多指标。它们大部分是底层的度量信息,是Kafka开发者为诊断特定问题或预计在未来调试问题时需要用到这些信息而添加的。broker的每一项功能都有相应的指标,其中一些最为常见的指标为Kafka的日常运行提供了必要的信息。
2.6.1 如何诊断集群问题
Kafka集群一般会出现3类问题。
- 单个broker的问题
这些问题会通过异常的集群指标易现出来,通常与缓便或发生故障的存储设备或者计算资源限制有关。要检测这些问题,需要根据操作系统的指标监控每个服务器的可用性和存储设备的运行状态。
加果问题不在操作系统或硬件层面,那么很可能是Kafka集群负载不均衡导致的。尽管Kafka尽量保持集样数据能够均匀地分布在所有的broker上,但并不能保证访问这些数据的客户端是均匀分布的。Kafka也无法检测出诸如热分区之类的问题。强烈建议使用外部工具来保持集群均衡,比如Cruise Control,它会持续监控集群,并在集群不均衡时对分区进行再均衡。它还提供了很多其他的管理功能,比如添加和删除broker。 - 集群过载
集群过载也是一个很容易检测的问题。如果集群是均衡的,并且多个broker的请求延迟一直在增加或者请求处理器空闲率较低,那么说明集群broker处理流量的能力达到了极限。你可能会发现一个客户端改变了它的请求模式,正在给你的集群制造麻烦。但即使发生了这种情况,你可能也无法对客户端做些什么。你能做的要么是降低集群负载,要么是增加broker。 - 控制器的问题
集群控制器的问题较难诊断,它们通常属于Kafka本身的bug。这些问题表现为broker元数据不同步、broker看起来正常但副本是离线的,以及主题操作问题(比如创建主题的操作没能被正常执行)。如果一个集群问题让你抓狂,让你不禁觉得“这真的很奇怪”,那么很有可能是因为控制器做了一些不可预知的事情。监控控制器的方法并不多,不过,只要监控好活动控制器的计数和控制器队列大小,当出现问题时,它们也是很有用的。
2.6.2 非同步分区问题
非同步分区是最为常见的监控指标:
指标名称 | JMX MBean | 取值范围 |
---|---|---|
非同步分区数 | kafka.server: type=ReplicaManager,name=UnderReplicatedPartitionis | 非负整数 |
如果集群中多个broker的非同步分区数量一直很稳定(保持不变),则说明集群中的某
个broker已经离线了。整个集群的非同步分区数量等于离线broker的分区数量,而离线broker不会生成任何指标。在这种情况下,需要检查这个broker出了什么问题,并解决问题。通常可能是硬件问题,也可能是操作系统问题或Java的问题。
2.6.2.1 集群负载不均衡
需要用到以下几个指标:
- 分区数量
- 首领分区数量
- 所有主题的消息流入速率
- 所有主题的字节流入速率
- 所有主题的字节流出速率
在一个均衡的集群中,这些指标的数值在整个集群范围内是均衡的。要解决这个问题,需要将负载较重的broker分区移动到负载较轻的broker上,这可以使用kafka-reassign-partitions.sh来实现。
2.6.2.2 请求数量超过了broker的处理能力
Kafka集群的另一个性能问题是请求数量超过了broker的处理能力。有很多潜在的瓶颈会地慢整个集群:CPU、磁盘IO和网络吞吐量是其中最为常见的。磁盘使用率不在其列,因内broker会一直运行,直到磁盘被填满然后“挂掉”。为了诊断这类问题,可以监控以下这些操作系统级别的指标。
- CPU使用
- 网络输入吞吐量
- 网络输出吞吐量
- 磁盘平均等待时间
- 磁盘使用百分比
上述任何一种资源出现过度消耗都会表现为分区不同步。
2.6.2.3 主机级别的问题
如果性能问题不是出现在集群级别,而是出现在一两个broker中,就要检查一下broker所在的主机,看看是什么导致它与集群中的其他broker不一样。主机级别的问题可以分为以下几类。
· 硬件故障
· 网络
· 进程冲突
· 本地配置不一致
2.6.3 Broker指标
活跃控制器数量
指标名称 | JMX MBean | 取值范围 |
---|---|---|
活跃控制器数量 | kafka.controller: type=KafkaController,name=ActiveControllerCount | 0或1 |
如果集群中没有控制器,就无法对状态变化(比如创建主题或分区或者broker故障)做出正确的响应。这个时候,要仔细检查为什么控制器线程出现了异常。例如,ZooKeeper集群的网络分区就会导致这个问题。在解决了这些底层问题之后,最好重启所有的broker,以便重置控制器线程的状态。
控制器队列大小
指标名称 | JMX MBean | 取值范围 |
---|---|---|
控制器队列大小 | kafka.controller:type=ControllerEventManager,name=EventQueueSize | 非负整数 |
控制器队列大小指标会告诉我们控制器当前有多少个等待处理的请求,它的值可以是0或正整数。因为新请求不断在涌入,管理操作(比如创建分区、移动分区和首领变更)也不断在发生,所以这个指标的值会频繁波动。这个指标会出现可预料的峰值,但如果持续增长,或保持在一个较高的位置不下降,则说明控制器可能被卡住了。这可能导致管理任务无法被正常执行。要解决这个问题,需要关闭当前的控制器,并将控制器角色交给另一个broker。但是,当控制器被卡住时,我们无法通过正常的方式关闭它。
请求处理器空闲率
指标名称 | JMX MBean | 取值范围 |
---|---|---|
请求处理器空闲率 | kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent | 从0到1的浮点数(包括1在内) |
Kafka 使用两个线程池来处理客户端请求:网络线程和请求处理线程(也叫IO线程)。网络线程负责通过网络读入数据和写出数据。这里不涉及太多的处理工作,所以不用太过担心这些线程会被耗尽。请求处理线程负责处理来自客户端的请求,包括从磁盘读取消息和向磁盘写入消息。因此,broker负载的增长对这个线程池有很大的影响。
这个指标值越低,说明系统负载越高。如果空闲百分比低于20%,那么说明存在潜在的问题;如果低于10%,则说明出现了性能问题。
除了集群的规模太小,还有其他两个方面的原因会导致这个线程池被重度使用。
- 线程池里设有足够的线程。一般来说,请求处理线程的数量应该等于系统的处理器核数(包括多线程处理器)。
- 线程做了不该做的事。
主题流入字节
指标名称 | JMX MBean | 取值范围 |
---|---|---|
每秒流入字节 | kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec | 速率为双精度浮点数,计数为整数 |
主题流入字节速率使用字节/秒来表示,用于度量broker从生产者客户端接收到的消息流量。可以基于这个指标决定何时对集群进行扩展或开展其他与流量增长相关的工作。这个指标也可用于评估一个broker是否比集群中的其他broker接收了更多的流量,如果出现了这种情况,则需要对分区进行再均衡。
所有的速率指标都有7个属性,使用哪些属性取决于你想获得哪方面的信息。有些属性是离散的事件计数,有些是基于时间段的事件平均数。务必在合适的地方使用合适的指标,否则你将得到一个错误的broker视图。
属性名称 | 解释 | |
---|---|---|
1 | EventType | 速率属性的度量单位,在这里是“字节”。 |
2 | RateUnit | 速率的时间段,在这里是“秒”。这两个属性表明,速率是通过字节/秒来表示的,不管它的值是基于多长时间段计算出来的平均值。速率还有其他4个不同粒度的属性。 |
3 | OneMinuteRate | 前1分钟的平均值。 |
4 | FiveMinuteRate | 前5分钟的平均值。 |
5 | FifteenMinuteRate | 前15分钟的平均值。 |
6 | MeanRate | 从 broker启动到现在的平均值。 |
7 | Count | 除了速率属性,速率指标还有一个Count属性,它在broker启动之后就一直保持增长。Count表示从broker启动以来接收的流量的字节总数。将这个属性用在一个支持计数指标的监控系统中,就可以获得完整的度量视图(我们看到的不仅仅是平均速率)。 |
主题流出字节
指标名称 | JMX MBean | 取值范围 |
---|---|---|
每秒流出字节 | kafka.server: type=BrokerTopicMetrics,name=BytesOutPerSec | 速率为双精度浮点数,计数为整数 |
主题流出字节速率是另一个与流量增长有关的指标,与流入字节速率类似。流出字节速率也就是消费者从broker读取消息的速率。流出速率的扩展方式与流入速率不一样。这是因为Kafka支持多消费者客户端。很多Kafka集群的流出速率可以达到流入速率的6倍!这就是为什么要单独对流出速率进行观察和分析。表
主题流入消息
指标名称 | JMX MBean | 取值范围 |
---|---|---|
每秒流入消息 | kafka.server: type=BrokerTopicMetrics, name=MessagesInPerSec | 速率为双精度浮点数,计数为整数 |
前面介绍的字节速率使用字节来表示broker流量,而消息速率则使用每秒生成消息条数(不考虑消息的大小)来表示流量。这也是一个很有用的生产者流量增长指标。它还可以与字节速率结合在一起使用,用于计算消息的平均大小。与字节速率一样,这个指标也能反映出集群流量是否均衡。
为什么没有消息流出速率?
这是因为在消息被读取时,broker会将整个消息批次发送给消费者,并不会展开计算每个批次包含了多少条消息。所以,broker也就不知道发送了多少条消息。broker提供了一个每秒获取请求次数指标,不过它是一种请求速率,不是消息计数。
分区数量
指标名称 | JMX MBean | 取值范围 |
---|---|---|
分区数量 | kafka.server: type=ReplicaManager,name=PartitionCount | 非负整数 |
broker的分区数重就是分配给broker的分区总数,一般不会经常发生变化。它包括broker的每一个分区副本,不管是首领副本还是跟随者副本。如果一个集群启用了自动创建士顾的功能,那么监控这个指标就会很有趣,因为主题的创建不受集群管理员的控制。
首领数量
指标名称 | JMX MBean | 取值范围 |
---|---|---|
首领数量 | kafka.server:type=ReplicaManager,name=LeaderCount | 非负整数 |
首领数量指标表示一个broker的首领分区数量。与broker的其他指标一样,这个指标也应该在整个集群的broker之间保持均衡。我们需要定期检查这个指标,并适时地发出告警,因为即使在副本的数量和大小看起来都很均衡的时候,它仍然能够显示出集群的不均衡。broker有可能会因为各种原因释放掉一个分区的领导权(比如ZooKeeper会话过期),但在会话恢复之后并不会自动收回领导权(除非启用了自动首领再均衡)。在这些情况下,这个指标会显示较少的首领分区数,或通常为零。这时候需要进行一次首选副本选举,重新均衡集群的首领。
可以将这个指标与分区数量指标结合在一起,计算出以该broker作为首领的分区的百分比。一个均衡的集群,如果它的复制系数是2,那么所有的broker都应该差不多是50%分区的首领。如果复制系数是3,则这个百分比应该降到33%。
离线分区数量
指标名称 | JMX MBean | 取值范围 |
---|---|---|
离线分区数量 | kafka.controller:type=KafkaController,name=OfflinePartitonsCount | 非负整数 |
与非同步分区数量一样,离线分区数量也是一个关键的监控指标。只有集样控制器会提供这个指标(其他broker提供的值为零),它会告诉我们集群中有多少个分区是没有首领的。
以下两个原因会导致分区没有首领:
- 包含这个分区副本的所有broker都关闭了。
- 由于消息数量不匹配,没有同步副本能够获得领导权(并禁用了不彻底首领选举)。
在生产环境中,离线分区会影响生产者客户端,导致消息丢失或应用程序回压。这属于“站点停机”问题,需要立即解决。
请求指标
Kafka协议,它有多种请求,每种请求都有相应的指标。
AddOffsetsToTxn | AddPartitionsToTxn | AlterConfigs |
AlterPartitionReassignments | AlterReplicaLogDirs | ApiVersions |
ControlledShutdown | CreateAcls | CreateDelegationToken |
CreatePartitions | CreateTopics | DeleteAcls |
DeleteGroups | DeleteRecords | DeleteTopics |
DescribeAcls | DescribeConfigs | DescribeDelegationToken |
DescribeGroups | DescribeLogDirs | ElectLeaders |
EndTxn | ExpireDelegationToken | Fetch |
FetchConsumer | FetchFollower | FindCoordinator |
Heartbeat | IncrementalAlterConfigs | InitProducerId |
JoinGroup | LeaderAndIsr | LeaveGroup |
ListGroups | ListOffsets | ListPartitionReassignments |
Metadata | OffsetCommit | OffsetDelete |
OffsetFetch | OffsetsForLeaderEpoch | Produce |
RenewDelegationToken | SaslAuthenticate | SaslHandshake |
StopReplica | SyncGroup | TxnOffsetCommit |
UpdateMetadata | WriteTxnMarkers |
每一种请求类型都有8个指标,它们分别提供了不同处理阶段的细节。
名字 | JMX MBean |
---|---|
总时间 | kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Fetch |
请求队列时间 | kafka.network:type=RequestMetrics,name=RequestQueueTimeMs,request=Fetch |
本地时间 | kafka.network:type=RequestMetrics,name=LocalTimeMs,request=Fetch |
远程时间 | kafka.network:type=RequestMetrics,name=RemoteTimeMs,request=Fetch |
节流时间 | kafka.network:type=RequestMetrics,name=ThrottleTimeMs,request=Fetch |
响应队列时间 | kafka.network:type=RequestMetrics,name=ResponseQueueTimeMs,request=Fetch |
响应发送时间 | kafka.network:type=RequestMetrics,name=ResponseSendTimeMs,request=Fetch |
每秒请求数 | kafka.network:type=RequestMetrics,name=RequestsPerSec,request=Fetch |
什么是百分位
2.6.4 主题指标和分区指标
除了用于描述broker一般行为的指标,还有很多主题级别的指标和分区级别的指标。在较大的集群中,这样的指标很多,一般来说不太可能将它们全部收集到一个指标系统中,但在调试与客户端相关的问题时,它们会很有用。
个体主题指标
名字 | JMX MBean |
---|---|
字节流入速率 | kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec,topic=TOPICNAME |
字节流出速率 | kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec,topic=TOPICNAME |
失败的获取速率 | kafka.server:type=BrokerTopicMetrics,name=FailedFetchRequestsPerSec,topic=TOPICNAME |
失败的生产速率 | kafka.server:type=BrokerTopicMetrics,name=FailedProduceRequestsPerSec,topic=TOPICNAME |
消息流入速率 | kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec,topic=TOPICNAME |
获取请求速率 | kafka.server:type=BrokerTopicMetrics,name=TotalFetchRequestsPerSec,topic=TOPICNAME |
生产请求速率 | kafka.server:type=BrokerTopicMetrics,name=TotalProduceRequestsPerSec,topic=TOPICNAME |
个体主题指标与前面介绍的broker指标非常相似。事实上,它们之间唯一的区别在于,个体主题指标指定了特定的主题名称,也就是说,这些指标属于某个特定的主题。因为个体主题指标的数量可能非常巨大(取决于集群主题的数量),所以我们极有可能不会监控它们或为它们设宜警。它们通常会被提供给客户端,客户端将用它们来评估和调试自己对Kafka的使用。
个体分区指标
个体分区指标不如个体主题指标有用。另外,它们的数量更加庞大,因为几百个主题可能句含数千个分区。不过,在某些情况下,它们还是有一定用处的,特别是用于表示分区当前在磁盘上保留的数据量的分区大小指标。如果把它们组合在一起,则可以知道主题保留了多少数据。如果同一主题的两个分区大小存在差异,则说明消息分布不均衡。日志片段数量指标表示分区保存在磁盘上的日志片段文件的数量。它可以与分区大小指标结合起来使用,用于跟踪磁盘资源的使用情况。
名称 | JMX MBean |
---|---|
分区大小 | kafka.log:type=Log,name=Size,topic=TOPICNAME,partition=0 |
日志片段数量 | kafka. log:type=Log,name=NumLogSegments, topic=TOPICNAME,partition=0 |
日志结束偏移量 | kafka.log:type=Log,name=LogEndOffset,topic=TOPICNAME,partition=0 |
日志起始偏移量 | kafka.log:type=Log,name=LogStartOffset,topic=TOPICNAME,partition=0 |
2.6.5 Java虚拟机监控
除了broker的指标,还需要监控服务器的指标和Java虚拟机(JVM)的指标。JVM频繁
发生垃圾回收会影响broker的性能,这个时候应该收到告警。JVM的指标还能告诉我们为什么broker下游组件的指标会发生变化。
垃圾回收
任JM方面,最需要监控的是垃圾回收(GC)状态。需要监控哪些MBean取决于你使用
H Java运行时(JRE)和垃圾回收器配置。如果你使用的是Oracle Java 1.8和G1垃圾回收器,G1垃圾回收指标:
名称 | JMX MBean |
---|---|
完全垃圾回收周期 | java. lang: type=GarbageCollector,name=G1 Old Generation |
年轻代垃圾回收周期 | java. lang: type=GarbageCollector,name=G1 Young Generation |
需要注意的是,在垃圾回收语义中,“Old”和“Full”代表的是一个意思。对于上述的两
个指标,我们需要收集CollectionCount和CollectionTime这两个属性。CollectionCount表示从JVM启动开始算起的垃圾回收次数,CollectionTime表示从JVM启动开始算起的垃圾回收时间(以毫秒为单位)。因为这些属性都是计数值,所以它们可以告诉我们单位时间内发生垃圾回收的绝对次数和时间。还可以用它们计算出平均每次垃圾回收花费的时间,尽管这在日常运维中没有多大用处。
这些指标还有一个LastGcInfo属性。这是由5个属性组成的组合值,为我们提供了最后一次垃圾回收的信息。5个属性中最重要的一个属性是duration(以毫秒为单位),表示最后一次垃圾回收花费了多长时间。其他几个属性(GcThreadCount、id、startTime和endTime)虽然也会提供一些信息,但用处不大。不过需要注意的是,我们无法通过这些属性查看到每一次垃圾回收的信息,因为年轻代垃圾回收发生得非常频繁。
Java操作系统监控
名称 | JMX MBean |
---|---|
操作系统信息 | java.lang:type=OperatingSystem |
JVM通过这个MBean提供了有关操作系统的一些信息,但这些信息很有限,并不能告诉我们操作系统的所有情况。有两个比较有用但在操作系统层面难以收集到的属性是MaxFileDescriptorCount 和OpenFileDescriptorCount。
MaxFileDescriptorCount 告诉我们JVM最多能打开多少个文件描述符(FD),
OpenFileDescriptorCount 告诉我们目前已经打开了多少个文件描述符。每个日片段和网络连接都需要一个文件描述符,所以它们的数量增长得很快。如果网络连接不能被正常关闭,那么broker很快就会把文件描述符耗尽。
2.6.6 操作系统监控
JVM并不能告诉我们所有与操作系统相关的信息。因此,我们不仅要收集broker的指标,也需要收集操作系统的指标。大多数监控系统会提供代理,这些代理将收集有关操作系统的信息。我们需要收集的信息包括CPU的使用情况、内存的使用情况、磁盘的使用情况、磁盘IO和网络的使用情况。
CPU
在CPU方面,至少需要监控系统平均负载。系统负载是一个数字,表示处理器的相对使用率。另外,按照使用类型收集到的CPU使用百分比信息也很有用。根据收集方法和操作系统的不同,我们可以得到如下这些CPU百分比细分(使用了缩写)中的一部分或全部。
us | 用户空间使用的时间。 |
sy | 内核空间使用的时间。 |
nt | 低优先级进程使用的时间。 |
id | 空闲时间。 |
wa | 等待(磁盘)时间。 |
hi | 处理硬件中断的时间。 |
si | 处理软件中断的时间。 |
st | 等待监视器的时间。 |
什么是系统负载?
系统负载是对CPU使用情况的度量。平均负载是指等待处理器执行的可执行进程数。Linux系统中还有一些处于不可中断睡眠状态的进程,比如磁盘等待。负载用3个数值来表示,分别是前1分钟的平均数、前5分钟的平均数和前15分钟的平均数。
- 在单CPU系统中,数值1表示系统负载达到了100%。此时总是会存在一个等待执行的进程。
- 在多CPU系统中,如果负载达到100%,那么它的数值就等于CPU核数。如果系统的CPU的核数是24,那么负载达到100%时它的数值就是24。
跟踪broker的CPU使用情况是很有必要的,因为它们在处理请求时占用了大量的CPU
时间。内存使用情况的跟踪就显得没那么重要了,因为运行Kafka并不需要太大的内存。broker会使用堆外的一小部分内存来压缩数据,剩下的大部分会被用作缓存。尽管如此,还是要跟踪内存的使用情况,确保broker的内存不会受到其他应用程序的影响。可以通过监控总内存空间和可用交换内存空间来确保内存交换空间没有被其他应用程序占用。
磁盘
对Kafka来说,磁盘是最重要的子系统。所有的消息都保存在磁盘上,所以Kafka的性能很大程度上依赖于磁盘的性能。我们需要监控磁盘空间和索引节点(索引节点是Unix文件系统中文件和目录的元数据对象),确保磁盘空间不会被用光。对保存数据的分区来说更是如此。还需要监控磁盘IO的统计数据,它们可以告诉我们磁盘是否被有效利用。至少,我门要监控磁盘的每秒读写速度、平均读写队列大小、平均等待时间和磁盘使用百分比。
网络
最后,还需要监控broker的网络使用情况。简单地说,就是流入和流出的网络流量,一般用每秒多少位来表示。需要注意的是,在没有消费者的情况下,流入一个位就有复制系数个位流出,如果再算上消费者,那么流出流量很容易比流入流量高出一个数量级。在设置告警阈值时要切记这一点。
2.6.7 日志
没有日志的监控是不完整的。与其他应用程序一样,broker也会将日志写到磁盘上。为了能够从日志中获得有用的信息,需要配置恰当的日志级别。可以通过记录INFO级别的日志捕捉到很多有关broker运行状态的重要信息。为了获得一系列清晰的日志文件,需要将不同的日志分开。
有两个日志记录器,它们会分别将日志写到单独的磁盘文件中。一个是kafka.controller,我们可以将它的级别设置为INFO。这个日志记录了集群控制器的信息。在任何时候,集群中都只有一个控制器,所以只有一个broker会使用这个日志。日志中包含了主题的创建和修改操作信息、broker的状态变更信息,以及集群活动的信息,比如首选副本选举和分区移动。另一个是kafka.server.ClientQuotaManager,我们也可以将它的级别设置为INFO。这个日志用于记录与生产和消费配额活动相关的信息。因为这些信息很有用,所以最好不要把它们记录在broker的主日志文件中。
日志压实线程的运行状态也是有用的信息。不过,Kafka并没有为这些线程提供单独的指标。一个分区压实失败可能导致整个压实线程崩溃,而且这是静默发生的。可以启用kafka.log.LogCleaner、kafka.log.Cleaner 和kafka.log.LogCleanerManager这些日志,并将它们的级别设置为DEBUG,这样就可以输出这些线程的运行状态信息。这些日志包含了每个被压实的分区的大小和消息条数。正常情况下,这些日志的数量不会很大,所以默认启用这些日志并不会给我们造成什么麻烦。
在调试问题时,还有一些日志也很有用,比如kafka.request.logger,我们可以将它的级
别设置为DEBUG或TRACE。这个日志包含了发送给broker的每一个请求的详细信息。如果级别被设置为DEBUG,那么它将包含连接端点、请求时间和概要信息。如果级别被设置为TRACE,那么它将包含主题和分区的信息,以及除消息体之外的所有与请求相关的信息。不管被设置成什么级别,这个日志都会产生大量的数据,所以如果不是出于调试的目的,则不建议启用这个日志。
2.7 客户端监控
所有的应用程序都需要被监控。Kafka客户端,不管是生产者还是消费者,都有特定的指标需要监控。本节将主要介绍如何监控官方的Java客户端,其他第三方客户端应该也提供了自己的指标。
2.7.1 生产者指标
Kafka生产者客户端的指标经过精简只提供了少量的JMX MBean。相反,旧版本客户端(不再受支持)提供了大量的MBean,其中有很多指标包含了更多的细节(包括大量的百分位和各种移动平均数)。它们有更大的覆盖面,却让异常情况跟踪变得更加困难。所有的生产者指标在MBean名字里都有对应的客户端ID。
名称 | JMX MBean |
---|---|
生产者整体 | kafka.producer:type=producer-metrics,client-id=CLIENTID |
个体 broker | kafka.producer:type=producer-node-metrics,client-id=CLIENTID,node-td=node-BROKERID |
个体主题 | kafka.producer:type=producer-topic-metrics,cltent-id=CLIENTID,topic=TOPICNAME |
上面列出的每一个MBean都提供了多个属性用于描述生产者的状态。下面列出了用外最大的几个属性。
生产者整体指标
生产者整体指标的属性描述了生产者各个方面的信息,从消息批次的大小到内存缓冲区的使用情况。虽然这些指标在调试时都有一定用处,但只有少数几个会经常用到,而在这几.个指标当中,也只有一部分需要监控和告警。下面将讨论一些平均数指标(以-avg结尾),它们都有相应的最大值(以-max结尾),不过这些最大值的用处很有限。
-
record-error-rate是一个很有必要对其设置告警的属性。这个属性的值一般情况下都是零,如果大于零,则说明生产者正在丢弃无法发送的消息。生产者配置了重试次数和回退策略,如果重试次数达到了上限,那么消息就会被丢弃。也可以跟踪另外一个属性record-retry-rate,不过这个属性不如record-error-rate重要,因为重试是很正常的行为。
-
也可以对request-latency-avg属性设置告警。这个属性是指生产者向broker发送请求时平均花费的时间。应该为这个指标设置一个基线,并设定告警阈值。请求延迟的增加说明生产者请求速率变慢,有可能是网络出了问题,也有可能是broker出了问题。不管是哪一种原因导致的,都是性能问题,并将导致生产者应用程序出现回压和其他问题。
-
除了这些关键指标,还需要知道生产者发送了多大的消息量。有3个属性为我们提供了3个不同的视图:outgoing-byte-rate表示每秒发送的消息字节数,record-send-rate表示每秒发送的消息数量,request-rate表示每秒生产者发送给broker的请求数。一个请求可以包含一个或多个批次,一个批次可以包含一条或多条消息,一条消息由多个字节组成。把这些指标都显示在仪表盘上会很有用。
-
有一些指标描述了消息、请求和批次的大小(都以字节为单位)。request-size-avg表示生严者发送给broker的请求的平均大小。batch-size-avg表示单个消息批次的平均大小(根据定义,批次包含了属于同一个分区的多条消息)。record-size-avg表示单条消息的平均大小。对单个主题生产者来说,这些属性提供了有关消息发送的有用信息。对多主题生产者(如MirrorMaker)来说,这些属性就没有那么有用了。除了这3个指标,还有另外一个指标records-per-request-avg,它表示单个生产请求包含的平均消息条数。
-
最后一个值得推荐的生产指标是record-queue-time-avg,它表示消息在发送给Kafka之前任生产者客户端的平均等待时间(以毫秒为单位)。应用程序在使用生产者客户端发送消息(调用send方法)之后,生产者会等待以下任一情况发生。
- 有足够多的消息填满批次(根据配置的batch,size)。
- 距离上一批次发送已经有足够长的时间(根据配置的linger,ms)。
这两种情况都会促使生产者客户端关闭当前批次,并把它发送给broker。对于较为繁忙的一般会发生第二种情况。record-ueue-time-avg指标会告诉我们发送消息用了多长时间,因此,如果希望通过配置上面的两个参数来降低应用程序延迟,那么这个指标会很有用。
个体broker指标和个体主题指标
除了生产者整体指标,还有一些MBean为每个broker连接和每个主题提供了一系列属性。这些指标在调试问题时很有用,但在平常不需要对它们进行常规的监控。这些MBean属性的名字与整体指标的名字一样,表示的含义也一样(只是它们是应用在个体broker或个体主题上)。
request-latency-avg是最为有用的个体broker指标,因为它比较稳定(在消息批次比较稳定的情况下),而且能够显示与某个broker之间的连接是否有问题。其他属性,比如outgoing-byte-rate和request-latency-avg,它们会因为broker所包含分区的不同而有所不同。也就是说,这些指标会随时间发生变化,完全取决于集群的状态。
个体主题指标比个体broker指标要有趣一些,但它们只有在生产者向多个主题生成消息时才有用。当然,也只有在生产者不会向过多主题生成消息的情况下才有必要对这些指标进行常规的监控。例如,MirrorMaker有可能向成百上千个主题生成消息。我们无法逐个检查这些指标,也几乎不可能为它们设置合理的告警阈值。与个体broker指标一样,个体主题指标一般用于诊断问题。例如,可以根据record-send-rate和 record-error-rate这两个属性将丢弃的消息隔离到特定的主题上(或者作为跨所有主题的可用消息)。另外还有一个指标byte-rate,它表示主题整体的消息速率(以字节/秒表示)。
2.7.2 消费者指标
与生产者客户端类似,消费者客户端将大量的指标属性整合到少数的几个MBean里,去掉了延迟百分位和速率移动平均数指标。因为消费者读取消息的逻辑比生产者发送消息的逻辑复杂,所以消费者的指标会更多。
名称 | JMX MBean |
---|---|
消费者整体 | kafka.consumer:type=consumer-metrics,client-id=CLIENTID |
获取请求管理器 | kafka.consumer:type=consumer-fetch-manager-metrics,client-id=CLIENTID |
个体主题 | kafka.consumer:type=consumer-fetch-manager-metrics,client-id=CLIENTID,topic=TOPICNAME |
个体 broker | kafka.consumer:type=consumer-node-metrics,client-id=CLIENTID,node-id=node-BROKERID |
协调器 | kafka.consumer:type=consumer-coordinator-metrics,client-id=CLIENTID |
获取请求管理器指标
在消费者客户端,消费者整体指标MBean的作用并不是很大,因为有用的指标都在获取请求管理器MBean里。消费者整体指标MBean包含了与底层网络相关的指标,而获取请求管理器MBean则包含了与字节、请求和消息速率相关的指标。与生产者客户端不同,我们可以查看消费者客户端提供的指标,但对它们设置告警并没有太大意义。
对于获取请求管理器,需要监控并设置告警的一个指标是fetch-latency-avg。与生产者客户端的request-ldtency-avg类似,这个指标表示消费者向broker发送请求所需要的时间。为这个指标设直告警的问题在于,请求延迟是通过消费者fetch.min.bytes和fetch.max.wait.ms这两个参数来控制的。一个低吞吐量的主题会出现不稳定的延迟,有时候broker的响应很快(有可用消息),有时候却无法在fetch.max.wait.ms指定的时间内完成响应(没有可用消息)。只有当主题有相对稳定和足够的消息流量时,这个指标才更有用。
要想知道消费者客户端正在处理多少消息流量,可以查看bytes-consumed-rate或 records-consumed-rate这两个指标,最好是两个都查看。它们分别表示客户端每秒读取的消息字节数和每秒读取的消息条数。有些用户为它们设置了最小值告警阈值,当消费者工作负载不足时,他们就会收到告警。不过需要注意的是,Kafka会试图解开消费者客户端和生产者客户端之间的耦合,但消费者读取消息的速率经常会依赖于生产者是否在正常运行,所以监控消费者的这些指标实际上是对生产者做了某些假设,这样可能会导致误报。
获取请求管理器也提供了一些指标,有助于我们理解字节、消息和请求之间的关系。
fetch-rate表示消费者每秒发出的请求数量,fetch-size-avg表示这些请求的平均大小
(字节),records-per-request-avg表示每个请求的平均消息条数。需要注意的是,消费者并没有提供与生产者record-size-avg相对应的指标,所以我们无法知道消息的平均大小。如果这个对你来说很重要,那么可以用其他指标来推算,或者在应用程序接收到消息之后获取消息的大小。
个体broker指标和个体主题指标
与生产者客户端类似,消费者客户端也为每个broker连接和主题提供了很多指标,我们可以用它们诊断问题,但可能不需要进行常规的监控。与获取请求管理器一样,个体brokerJ request-latency-avg指标的用处也是有限的,具体取决于主题的消息流量。incoming-yte-rate和 request-rate分别表示从个体broker每秒读取的消息字节数和每秒请求数。我们可以用它们诊断消费者与broker之间的连接问题。
如果读取的是多个主题,那么消费者客户端提供的个体主题指标就会很有用,否则它
们就与获取请求管理器的指标一样,我们也就没有必要去收集它们。如果客户端(如
OrMaker)读取了大量主题,那么查看这些指标就会变得很困难。如果你真的打算收集“兰指标,那么可以考虑收集最重要的3个:bytes-consumed-rate、records-consumed-rate和和fetch-size-avg。bytes-consumed-rate表示从某个主题每秒读取的消息字节数,records-consumed-rate表示每秒读取的消息条数,fetch-size-avg表示每个请求的平均大小。
消费者协调器指标
多个消费者客户端组成了消费者群组,群组中会发生一些需要协调的活动,比如新成员加入,或者通过向broker发送心跳来维持群组的成员关系。消费者协调器负责处理这些协调工作,并提供了一组指标。与其他指标一样,协调器指标也提供了很多
数字,但只有一小部分需要进行常规的监控。
消费者群组在执行同步操作时可能会导致消费者出现停顿。此时,群组中的消费者正在协商哪些分区应该由哪些消费者读取。停顿的时间长短取决于分区的数量。协调器提供了sync-time-avg指标,表示同步活动发生的平均时长(以毫秒为单位)。sync-rate属性也很有用,表示消费者群组每秒发生同步的次数。在一个稳定的消费者群组中,这个数字大多数时候是零。
Kafka会将消费者提交的偏移量作为读取进度的检查点。消费者既可以基于固定的时间间隔自动提交偏移量,也可以在代码中手动提交偏移量。提交偏移量也是一种生成消息的请求(它们有自己的请求类型),提交的偏移量会作为消息被发送到一个特定的主题上。协调器提供的commit-latency-avg属性表示提交偏移量所需的平均时长。我们也需要监控这个指标,就像监控生产者请求延迟一样。还要为它设定一个预期的基线和合理的告警阈值。
协调器指标的最后一个属性是assigned-partitions,表示分配给消费者客户端(群组中的单个消费者)的分区数量。这个属性之所以有用,是因为我们可以通过比较整个群组各个消费者分配到的分区数量来判断群组的负载是否均衡。可以使用这个属性来识别因协调器分区分配算法所导致的负载不均衡问题。
2.7.3 配额
Kafka可以对客户端请求进行节流,防止某个客户端拖垮整个集群。对消费者客户端和生产者客户端来说,这都是可配的,并用每秒允许单个客户端从单个broker读取多少字节或写入多少字节来表示。它有一个broker级别的默认值,客户端可以动态覆盖。当broker发现客户端的流量已经超出配额时,它会暂缓向客户端返回响应,直到客户端流量降到配额以下。
broker并不会将客户端被节流的错误码放在响应消息里。也就是说,对应用程序来说如果不监控这些指标,就不会知道发生了节流。
名称 | JMX MBean |
---|---|
消费者 | kafka.consumer:type=consumer-fetch-manager-metrics,client-id=CLIENTID的属性fetch-throttle-time-avg |
生产者 | kafka.producer:type=producer-metrics,client-id=CLIENTID的属性 produce-throttle-time-avg |
在默认情况下,broker不会开启配额限制。但不管有没有开启,监控这些指标总是安全无害的。况且,配额限制有可能在未来某个时刻被启用,从一开始就监控这些指标要比在后期添加更容易。
2.8 滞后监控
对消费者来说,取需要被监控的指标是滞后消息数量,也就是分区生成的最后一条消息和费者读取的敢后一条消息之间的差值。前面讲过,对于消费者滞后,使用外部监控工具使用客户端提供的指标要好得多。虽然消费者提供了滞后指标,但该指标存在一个问题,即它只表示单个分区(也就是具有最大滞后的那个分区)的滞后,所以不能准确地告长我们消费者的整体滞后情况。另外,它需要消费者做一些额外的操作,因为这个指标是由消费者对每个发出的获取请求进行计算得出的。如果消费者发生崩溃或离线,那么这个指标要么不准确,要么不可用。
监控消费者滞后最好的办法是使用外部工具,它们既能观察broker的分区状态(通过跟踪最近生成的消息的偏移量),也能观察消费者的状态(通过跟踪消费者群组提交的最新偏移量)。这种监控方式提供了一种不依赖消费者状态的客观视图。我们需要监控消费组读取的每一个分区。对于大型的消费者,比如MirrorMaker,这意味着需要监控成千上万个分区。
使用命令行工具获取消费者群组的信息,包括提交的偏移量和滞后的消息数量。如果直接监控由命令行工具提供的信息,那么也存在一些问题。首先,需要为每个分区定义一个合理的滞后阈值。每小时接收100条消息的主题和每秒接收10万条消息的主题的滞后阈值显然不同。其次,需要将滞后指标导入监控系统,并为它们设置告警。如果一个消费者群组读取了1500个主题,这些主题的分区数量超过了10万个,那么
这将是一项令人望而生畏的任务。
可以使用Burrow 来降低这项任务的复杂性。Burrow是一个开源的应用程序,最初由
LinkedIn开发。它会收集集群消费者群组的滞后信息,并为每个群组计算出一个独立的状态,告诉我们群组是否运行正常、是否出现了滞后、速度是否变慢或者是否已经停止工作。它不需要通过监控消费者群组的处理进度来获得阈值,不过我们仍然可以从中获得滞后的消息绝对数量。LinkedIn工程博客“Burrow:Kafka Consumer Monitoring Reinvented”介绍了Burrow的工作原理。Burrow既可用于监控一个集群中的消费者,也可用于监控多个集群,而且可以很容易被集成到已有的监控和告警系统中。
如果没有其他选择,则可以考虑查看消费者客户端的records-lag-max指标,它提供了消费者消费状态的部分视图。不管怎样,仍然建议使用像Burrow这样的外部监控系统。
2.9 端到端监控
我们推荐的另一种外部监控解决方案是端到端监控,它为Kafka集群的健康状态提供了一种客户端视图。消费者客户端和生产者客户端的一些指标能够说明集群可能出现了问题,但这里有猜想的成分,因为延迟的增加有可能是由客户端、网络或Kafka本身引起的。另外,你原本的任务可能只是管理Kafka集群,但现在也需要监控客户端。
你需要知道下面这两个问题的答案:
- 可以向Kafka集群写入消息吗?
- 可以从Kafka集群读取消息吗?
理想情况下,你可能希望对每一个主题都执行这些操作。但是,在大多数情况下,向每一个主题都注入人为制造的流量是不合理的。所以,可以考虑将问题提升到broker级别,而这正是Xinfra Monitor(之前叫作Kafka Monitor)要做的事情。这个工具最初由LinkedIn的Kafka团队开发并开源。它会持续地向一个横跨集群所有broker的主题生成消息,并读取这些消息。然后会监控每个broker生产请求和读取请求的可用性,以及生产消息和读取消息之间的整体延迟。这种从外部验证Kafka集群运行状态的监控方式是非常有价值的,因为与监控消费者滞后一样,broker本身无法告知客户端集群是否运行正常。
附录
1 JMX
1.1 什么是JMX
JMX,全称 Java Management Extensions,是在 J2SE 5.0 版本中引入的一个功能。提供了一种在运行时动态管理资源的框架,主要用于企业应用程序中实现可配置或动态获取应用程序的状态。JMX 提供了一种简单、标准的监控和管理资源的方式,对于如何定义一个资源给出了明确的模式。
1.2 JMX的架构
资源探测(Instrumentation)
资源探测的核心是用于资源管理的 Managed bean,简称 MBean。MBean 表示在 Java 虚拟机中运行的资源,例如应用程序或 Java EE 技术服务(事务监视器、JDBC 驱动程序等)。MBean 可用来收集重点关注的统计信息,比如性能、资源使用以及问题等,也可以用于获取和设置应用程序配置或属性(推/拉模式),也还可以用于故障通知或者状态变更(推送)等。
代理(Agent)
代理充当管理资源和应用程序之间的中介。代理层提供对来自管理应用程序的管理资源的访问。JMX 代理可以在嵌入在管理资源的机器中的 JVM 中运行,也可以位于远程位置。代理不需要知道它公开的资源以及使用公开 MBean 的管理器应用程序。它充当处理 MBean 的服务,并允许通过通过 Connector 或 Adaptor 公开的协议来操作 MBean。
代理层的职责之一是将应用程序与管理资源分离。应用程序不会直接引用管理的资源,而是通过 JMX 代理的对象名称引用调用管理操作。代理层的核心组件是 MBean Server,作为 MBean 的注册中心,并允许应用程序发现已注册 MBean 的管理接口。除此之外,代理层提供了四种代理服务,使管理 MBean 更加容易:计时器服务、监控服务、关系服务以及动态 MBean 加载服务。
想要 MBean Server 可以管理 MBean 资源,首先要把资源注册到 MBean Server 上,任何符合 JMX 的 MBean 资源都可以进行注册。
远程管理(Remote Management)
远程管理是 JMX 架构的最外层,该层负责使 JMX 代理对外部世界可用。代理层并没有实现远程访问方法,所以在远程管理层会提供一个远程通信接口对外提供服务。提供对外服务需要通过使用一个或多个 JMX Connector 或者 Adaptor 来实现。Connector 和 Adaptor 允许应用程序通过特定协议访问远程 JMX 代理,这样来自不同 JVM 的应用程序就可以调用 MBean Server 上的管理操作、获取或设置管理属性、实例化和注册新的 MBean,以及注册和接收来自管理资源的通知。
Connector 是将代理 API 暴露给其他分布式技术,例如 Java RMI,而 Adaptor 则是通过 HTTP 或者 SNMP 等不同的协议提供对 MBean 的可见性。事实上,一个代理可以使用许多不同的技术。Connector 和 Adaptor 在 JMX 环境中提供相同的功能。
1.2 示例代码
定义资源探测类
首先定义一个MBean的接口,这个接口的目的是监控JVM内存使用百分比。
package com.qupeng.demo.jmxnative;
import javax.management.MXBean;
@MXBean
// Interface's name must be ended with MBean or add the @MXBean annotation
public interface MonitoringMBean {
String getHeapMemUsageRatio();
int getHeapMemUsageRatioPrecision();
void setHeapMemUsageRatioPrecision(int precision);
void updateHeapMemUsageRatioPrecision(int precision);
}
注意:在定义MBean时,要么以MBean结尾,要么添加注解@MXBean,两者选其一。
将探测类注册到JMX代理中
实现这个类:
@ManagedResource(
objectName = "com.qupeng.demo.jmxspringboot: type=monitoring, name=Monitor",
description = "JMX Bean for monitoring",
log = true,
logFile = "jmx.log",
currencyTimeLimit = 15,
persistPolicy = "OnUpdate",
persistPeriod = 200,
persistLocation = "foo",
persistName = "bar")
public class Monitoring implements MonitoringMBean {
private int heapMemUsageRatioPrecision = 2;
private String description;
@Override
public String getHeapMemUsageRatio() {
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
System.out.println(heapMemoryUsage.getUsed());
System.out.println(heapMemoryUsage.getMax());
return String.format("%."+ heapMemUsageRatioPrecision +"f", (double)heapMemoryUsage.getUsed() * 100 / heapMemoryUsage.getMax());
}
public int getHeapMemUsageRatioPrecision() {
return this.heapMemUsageRatioPrecision;
}
@Override
public void setHeapMemUsageRatioPrecision(int precision) {
this.heapMemUsageRatioPrecision = precision;
}
@Override
public void updateHeapMemUsageRatioPrecision(int precision) {
this.heapMemUsageRatioPrecision = precision;
}
}
@ManagedResource这个注解实际上就是将MBean注册到JMXServer中,ObjectName就是MBean实例的唯一标识。
ObjectName 由 domain:key 格式构成:
- domain:可以是任意字符串,但根据 MBean 命名约定,一般使用 Java 包名(避免命名冲突)
- key:以逗号分隔的’key=value’键值对列表
我们一般会定义两个 key:
- type=MXBean 接口的实现类的类名
- name=自定义的名字
在这里,使用的是:com.qupeng.demo.jmxspringboot: type=monitoring, name=Monitor。
接下来实例化这个类:
@Bean
public Monitoring monitoring() {
return new Monitoring();
}
提供远程代理
监控数据准备好了,但是监控系统一般都是部署在远程服务器上的,所以需要还需要提供远程调用服务。
@Bean
public Registry registry() throws Exception {
System.out.println(2);
return LocateRegistry.createRegistry(9999);
}
@Bean
public JMXConnectorServer serverConnector(Registry registry) throws IOException, JMException {
System.out.println(3);
ConnectorServerFactoryBean factory = new ConnectorServerFactoryBean();
factory.setServiceUrl("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
factory.afterPropertiesSet();
return factory.getObject();
}
这个例子注册了RMI接口。
客户端
我们实现一个简单的SpringBoot程序作为监控系统,调用RMI接口获取内存使用比例。
public class JMXSpringBootRMIClient {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(JMXSpringBootRMIClient.class, args);
MonitoringMBean monitoringMBean = applicationContext.getBean(MonitoringMBean.class);
System.out.println(monitoringMBean.getHeapMemUsageRatio());
}
@Bean
public MBeanServerConnection clientConnector() throws IOException {
MBeanServerConnectionFactoryBean factoryBean = new MBeanServerConnectionFactoryBean();
factoryBean.setServiceUrl("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
factoryBean.setConnectOnStartup(false);
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
@Bean
public MonitoringMBean monitoringMBean(MBeanServerConnection clientConnector) throws IOException, MalformedObjectNameException {
MBeanProxyFactoryBean factoryBean = new MBeanProxyFactoryBean();
factoryBean.setObjectName(new ObjectName("com.qupeng.demo.jmxspringboot: type=monitoring, name=Monitor"));
factoryBean.setProxyInterface(MonitoringMBean.class);
factoryBean.setServer(clientConnector);
factoryBean.afterPropertiesSet();
MonitoringMBean proxy = (MonitoringMBean) factoryBean.getObject();
System.out.println(proxy.getHeapMemUsageRatio());
System.out.println(9);
return proxy;
}
}