Kafka JMX监控实战:jmxtrans+InfluxDB+Grafana搭建可视化链路 1. 先聊聊这套Kafka监控方案是怎么来的做过Kafka运维的应该都懂那种感觉集群跑着跑着消费者突然不消费了或者消息积压越来越严重你盯着命令行敲metrics半天却看不出个所以然。Kafka的很多内部状态——从Broker线程池利用率、请求队列堆积到分区副本的ISR收缩情况——都藏在JMXJava Management Extensions里像Broker自身的健康信号一样不主动去拉数据就只能等它出事了才被动排查。这套jmxtrans influxdb grafana的组合解决的就是一个很直接的问题怎么把Kafka暴露出来的JMX指标变成一条连续的、可视化的实时监控曲线。jmxtrans负责“抓取”它是个用Java写的通用JMX采集器按JSON格式的配置规则去指定的JMX端口上拉指标influxdb负责“存储”把时序数据按时间落库grafana负责“展示”把数据从influxdb里捞出来画成面板。我最初参考这个方案的时候其实也犹豫过现在很多团队都用开源的Kafka Exporter配Prometheus再加上Grafana的官方模板一套下来比jmxtrans直观很多。但这个组合在JVM进程场景下有一个特有优势——它不挑采集对象。只要是能开JMX端口的Java应用你都能用同一套配置模板去抓不只是Kafka。而且jmxtrans对老版本Java应用兼容性极好在很多存量系统还在用旧JDK版本的环境里想不动应用代码就接入监控jmxtrans几乎是唯一能低风险落地的方案。适合谁来参考这套东西我觉得主要是两类人一是用Java技术栈、Kafka集群规模中等几个到几十个节点且不想引入太多额外组件的团队二是对Prometheus这套相对较新的链路有其他成本顾虑、想把监控建立在已有时间序列数据库上的工程师。对刚入门监控这块的运维新人来说跟着这个组合走一遍也能把“JMX采集原理时序存储可视化面板”这三块基础知识给摸透了。2. 三个组件的角色划分与数据链路这套体系里每个组件都只干一件事边界很清晰。2.1 Kafka侧的JMX不止是“开个端口”这么简单Kafka从很早的版本开始就在bin目录里提供了kafka-server-start.sh这个启动脚本。多数人在没配置JMX端口的情况下直接启动脚本会自动设置一个默认的JMX监听端口比如9999但这只对单机本地用跨节点采集就不行了还得显式把它绑到内网网卡上否则只能采集到localhost的指标。我之前遇到过一个问题服务器上有多个网卡Kafka启动时JMX只听在了默认的localhost上结果jmxtrans部署在另一台机器上死活连不上。后来检查才发现是JMX_PORT是设了但java.rmi.server.hostname没指到实际的业务IPRMI注册表把地址广播成了内部混乱的那个网卡IP。这个细节很坑我给当时踩坑的记录写进了配置里后面会细说。关于JMX端口和指标范围Kafka的JMX其实包含三类东西MBean服务本身的基础信息比如操作系统层面的一些属性Kafka核心组件暴露的业务指标包括Broker的请求处理耗时、网络处理器线程数、请求队列大小、响应队列大小各类topic的分区情况、消息速率、日志落盘字节数、ISR状态等。这些指标都是实时生成的没有历史概念所以一定要有外部存储来做时间序列化这也正是influxdb存在的意义。2.2 jmxtrans是这套方案里最灵活的“采集适配层”jmxtrans作为一个独立的JVM进程运行通过查询目标应用的JMX端口按规则采集MBean属性值把结果转换成JSON等格式发到输出端。它有两个比较关键的设计一是每个采集规则都对应一个JSON配置块叫做Query二是输出端被抽象成Writer可以对接很多后端比如Graphite、StatsD、Logstash当然也包括InfluxDB通过HTTP接口。在Kafka场景下jmxtrans的配置核心就三块servers定义要采集的Kafka节点地址、JMX端口、认证信息queries定义要拉取的MBean对象、属性、采集频率outputWriters定义数据往哪里送、按什么格式送。初看jmxtrans可能会有个误区以为它配置很复杂实际上它的复杂度全部集中在这三块JSON的合理组织上。只要理解了MBean的层次结构写起配置来就跟填表一样机械。比如Kafka Broker的请求处理器线程数MBean路径是kafka.network:typeRequestChannel,nameRequestQueueSize那在jmxtrans里就写这个object name和属性名。Kafka 0.10之前和之后的指标命名有些差别但大框架一致。这个后面讲配置的时候我会给一个可以直接改的完整示例。2.3 influxdb在时序数据处理上的取舍influxdb用起来很舒服的一点是它不用预先建表数据写进来就自动按measurement建好存储字段类型自动推断。对监控这种“持续写入按时间范围聚合查询”的场景它的数据模型是按measurement tags fields timestamp来组织的跟JMX指标天生合拍。举个例子Kafka采集上来的一个指标可能是这样的measurementkafka_request_queue_sizetagshostbroker-01、metric_typebrokerfieldsvalue15timestamp写入时刻这样在Grafana里就能用SELECT mean(value) FROM kafka_request_queue_size WHERE hostbroker-01 AND time now() - 6h GROUP BY time(1m)画出趋势图。这种设计让监控面板的查询逻辑非常朴素不需要复杂的维表关联——这也是时序库区别于普通MySQL方案的地方。2.4 grafana只做一件事把时间序列画成人能看的图Grafana本身不采集数据也没有存储能力。它就是数据源的可视化前端。但它对influxdb的支持历史相当久数据源配置里直接选InfluxDB填入HTTP地址、库名、账号密码剩下的就是写InfluxQL查询语句对应到面板的每条Query上。很多人第一次用Grafana会觉得功能多到不知道从哪里下手。我的建议是先把数据源配通然后建一个Dashboard在Panel的Query编辑器里跑通一条最简单的查询看到曲线出来了再逐步往上堆指标。一次别贪多一个面板一个指标地加最后自然就成了一个结构清晰的监控大屏。3. 核心配置文件详解jmxtrans那套JSON我帮你们拆开揉碎刚开始用jmxtrans的人一半时间耗在让采集器“跑起来”另一半耗在写JSON上。很多人直接拿网上的模板改改完发现要么采集器起不来要么Kafka端口连不上要么指标落库了但数值是0或者NaN。我把自己一直在用的模板拿过来拆一下你们可以直接照抄改参数。3.1 设置influxdb输出端口的JSON片段outputWriters里配置输出到influxdb的部分决定数据以什么协议写入InfluxDB。用HTTP接口是最省事的做法jmxtrans会组装好行协议line protocol直接POST过去。{ class: com.googlecode.jmxtrans.model.output.InfluxDbWriterFactory, url: http://192.168.1.100:8086, database: kafka_metrics, username: jmxwriter, password: jmxpass, tags: { cluster_name: prod-kafka } }这里有几个容易踩的坑url不要加尾斜杠有些版本会拼出双重斜杠导致influxdb返回404database对应的库必须提前创建好jmxtrans没有权限自动建库tags一定要打不加tags的话多台Broker的数据写入同一measurement后就没法区分来源到了Grafana画图就会乱成一团。实测下来加上cluster_name和host这对tag后面做多集群筛选特别顺手。3.2 servers和queries把Kafka节点的JMX指标拉回来servers定义了连接哪个节点的哪个JMX端口{ servers: [ { host: 192.168.1.11, port: 9999, queries: [ { obj: kafka.server:typeBrokerTopicMetrics,nameBytesInPerSec, attr: [OneMinuteRate], resultAlias: bytes_in_rate }, { obj: kafka.network:typeRequestChannel,nameRequestQueueSize, attr: [Value], resultAlias: request_queue_size }, { obj: kafka.network:typeRequestChannel,nameResponseQueueSize, attr: [Value], resultAlias: response_queue_size } ] }, { host: 192.168.1.12, port: 9999, queries: [ { obj: kafka.server:typeBrokerTopicMetrics,nameBytesInPerSec, attr: [OneMinuteRate], resultAlias: bytes_in_rate } ] } ] }这段配置看起来简单但有三个细节值得注意。第一个是obj里的通配符匹配。Kafka有很多指标是带topic名的例如BrokerTopicMetrics这个object下面每个topic都有独立实例。如果在obj里写死单个topic那配置就只能在指定场景下用如果写成kafka.server:typeBrokerTopicMetrics,name*那就能把所有topic都拉回来。但是注意这样做的代价是jmxtrans会尝试遍历所有匹配的MBean在topic数量很多比如上千个时采集量会非常大jmxtrans线程容易撑满。后来我的策略是核心汇总指标用Broker维度的name不用通配需要单独看topic的就挑几个关键topic单独列出来控制采集规模。第二个是attr数组。Kafka的速率指标通常有OneMinuteRate、FiveMinuteRate、MeanRate等多个属性。多数监控场景下看OneMinuteRate就够用它是滑动1分钟平均速率能平滑掉毛刺又不会钝到看不出变化。如果一次把三个取回来数据量直接翻三倍但在Grafana面板上你大概率只用其中一个。第三个是resultAlias。这个名字会自动拼进InfluxDB的measurement或其他标识里。建议定义成跟自己业务含义相近的英文名别用MBean里的驼峰原样名不然Grafana面板里一列指标都是BytesInPerSec、BytesOutPerSec看着脑壳疼。3.3 Kafka启动脚本里必要的JMX参数前面对配置文件做好了如果Kafka进程本身没把JMX暴露出来一切都是白搭。我在kafka-server-start.sh或者bin/kafka-server-start.sh顶部追加过类似这样一段export JMX_PORT9999 export RMI_HOSTNAME192.168.1.11 export KAFKA_JMX_OPTS-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticatefalse -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.local.onlyfalse -Djava.rmi.server.hostname${RMI_HOSTNAME}强调一下RMI_HOSTNAME必须填能实际路由到这个Kafka节点的业务IP不要填localhost。因为JMX的RMI注册表会把主机名写进connection信息里如果注册时拿到的是内网第二个网卡的地址或者hostname解析出的IP跟实际监听网卡不一致远端采集器连的时候就会报类似Connection refused to host: 192.168.1.x的错误。当时我在新机房部署时踩过这个坑排查一整天就为了一个环境变量。另外把这套配置直接加到启动脚本里之后监控是起来了但安全上要考虑一下。因为设了authenticatefalse和sslfalse等同于所有能访问这个端口的人都能拉取到JVM的MBean信息。这在隔离的内网做测试和内部监控是可行的但如果是多租户环境或者公网场景就必须改成带认证和SSL的模式否则会有信息泄漏风险。我一般做生产环境部署时会把JMX端口只开放给采集器所在的网段用安全组或防火墙做一层白名单。4. 部署与启动流程从裸环境到第一条监控曲线4.1 第一步准备好influxdb数据库和账号influxdb安装包装好之后首先要确认HTTP API能用。用命令行或者直接访问http://influxdb-ip:8086/ping能看到返回200就算正常。然后建库建账号# 进入influxdb命令行 influx # 创建数据库 CREATE DATABASE kafka_metrics # 创建用户并授权写权限 CREATE USER jmxwriter WITH PASSWORD jmxpass GRANT ALL ON kafka_metrics TO jmxwriter这里我特地把写权限单独给了一个用户而Grafana的数据源用另一个只读账号。避免以后如果Grafana面板不小心被改了查询语句也不会误删或者误写数据。这种权限最小化的操作在真实生产环境里很加分。4.2 第二步用jmxtrans启动采集jmxtrans的启动方式有两种一种是直接跑Java jar包另一种是解压完整包后用自带shell脚本启动。我倾向于使用完整包方式因为日志、配置、pid管理都齐全。启动之前先确认Java版本。jmxtrans是Java程序一般用JDK 8及以上启动没问题但如果你在非常老的JDK 6/7上跑就得用对应版本的jmxtrans。这个兼容性问题一旦踩到表现就是启动报ClassNotFoundException或者UnsupportedClassVersionError解决方式就是换一个版本千万别去硬改代码。启动命令大致是cd /usr/local/jmxtrans nohup java -jar -Xms512m -Xmx512m jmxtrans-all.jar -f /etc/jmxtrans/kafka-broker.json /var/log/jmxtrans/startup.log 21 -f参数指向刚才写好的JSON配置。这里有个小建议jmxtrans默认日志默认输出到stdout但实际运行中排查问题的时候最好在logback或log4j配置里打开info级别的日志让它把每次采集的指标数量和上报结果打出来。特别是第一次调试时能看到类似下面这样的日志就代表采集链路已经通了[INFO] Result: [Result{... bytes_in_rate15230.0 ...}]如果这个日志一直没有任何指标输出说明要么连不上JMX端口要么MBean路径写错了。jmxtrans在启动后是个常驻进程它按JSON里queries里定义的freq字段单位是秒默认60秒周期性地跑每个query。这个频率对Kafka监控足够了不需要刻意调成1秒频繁拉取对Kafka的JMX也是一种压力尤其MBean数量多时拉取间隔太短反而会拖垮Broker。4.3 第三步给Grafana接上influxdb数据源Grafana启动起来之后在Configuration → Data Sources → Add data source里选InfluxDB填三样核心信息URLhttp://192.168.1.100:8086Databasekafka_metricsUser / Password刚刚建好的只读账号和密码保存并测试看到 “Successfully connected” 就说明数据源没问题。接着新建Dashboard添加一个Panel查询语句先写最简单的SELECT mean(value) FROM kafka_request_queue_size WHERE $timeFilter GROUP BY time($__interval)这里$timeFilter和$__interval是Grafana的内置变量前者自动把时间范围限定到面板右上角选的区间后者根据时间范围和面板宽度自动决定聚合粒度。查询出来如果能画出线来整条链路就通了。4.4 第四步常用Kafka核心指标对照表很多新手真正到这一步时会困惑——链路通了但我到底该看哪些指标这里我把平时必看的一套Kafka JMX核心指标整理成了表格可以直接对着配指标类别MBean Object Name常用属性关注点Broker消息速率kafka.server:typeBrokerTopicMetrics,nameBytesInPerSecOneMinuteRate流入流量突增可能触发磁盘写入瓶颈请求队列积压kafka.network:typeRequestChannel,nameRequestQueueSizeValue持续大于cpu线程数说明处理器忙不过来响应队列积压kafka.network:typeRequestChannel,nameResponseQueueSizeValue持续增大可能伴随响应超时请求处理耗时kafka.network:typeProcessor,nameTotalProcessorCountValue监控处理器线程配额活跃连接数kafka.network:typeProcessor,nameIdlePercentValue长期趋近0说明线程资源被耗尽ISR收缩kafka.server:typeReplicaManager,nameIsrShrinksPerSecOneMinuteRate非零持续出现需要检查磁盘/网络稳定性分区总数kafka.server:typeKafkaRequestHandlerPool,nameRequestHandlerAvgIdlePercentValue低于0.3一般意味着Broker接近处理上限这张表别看简单我是真心建议你把它们作为第一版监控面板的基础跑稳定了再去加更多指标。监控不是越多越好一堆指标会在故障时噪声淹没信号。4.5 第一次看到曲线时的细节确认数据能画成图之后最后还要做一件事跟真实值对一对。怎么对用Kafka自带的JMX小工具手动看一眼/usr/java/jdk/bin/jconsole -J-Djava.rmi.server.hostname192.168.1.11 192.168.1.11:9999或者用jcmd直接读MBean属性jcmd 12345 PerfCounter.print对比一下当前瞬时值跟Grafana上最近一个点的值差值应该在几秒的采集间隔误差内。如果Grafana的点比实际值低很多那大概率是JMX拉取频率或聚合粒度设置的问题如果完全对不上那就回头检查attr和resultAlias的映射有没有串行。我之前就干过一件蠢事把BytesInPerSec的OneMinuteRate当成了字节速率单位在图上写“Bytes/s”但实际上大家都知道Kafka这个指标算的是bytes结果某天突然曲线飙升到GB级别一开始慌了后来拉日志一看其实是某个消费者程序在灌历史数据往Kafka里导了一大批旧日志瞬时速率飙了几十倍。配合速率指标一眼定位是谁在写——这就是监控的价值。5. 常见问题与排查技巧实录这套链路别看组件少实际跑起来遇到最多的问题其实就集中在三块JMX连不上、数据没写进influxdb、Grafana画不出图。5.1 jmxtrans连不上Kafka的JMX端口典型的报错是java.io.IOException: Failed to retrieve RMIServer stub: javax.naming.ServiceUnavailableException或者是connect timeout。按下面的顺序排查很快先确认网络通不通telnet 192.168.1.11 9999连不上就查防火墙/安全组是不是把9999端口漏了确认端口监听在哪个网卡netstat -anop | grep 9999看LISTEN的IP是不是预期的业务网卡IP如果显示的是127.0.0.1那就是java.rmi.server.hostname没设对确认MBean路径是否匹配当前Kafka版本不同Kafka版本指标名会有小差异不行就用jconsole连上去实际看一遍MBean树把obj里写的object name跟JMX控制台上看到的完全对齐看jmxtrans日志把日志级别开到info日志里会明确打出哪个query执行失败或者返回空结果。我自己把这一步总结成“先通网络、再审端口、再树形核对、最后查日志”按这个顺序走百分之八九十的问题十分钟内能定位。5.2 数据在influxdb里没出现但jmxtrans没报错这种情况最迷惑。jmxtrans日志显示正常采集InfluxDB里就是查不到数据。我的经历里有三个常见原因outputWriters里的database不存在influxdb对不存在的库写入时会静默丢弃HTTP返回400权限不够虽然账号能连上influxdb但没有对应database的写权限tags里有非法字段类型或者值带了特殊字符导致行协议解析失败。排查手段很简单手动向influxdb写一条测试行协议看看能不能成功然后看一眼jmxtrans启动日志里有没有警告。另外也建议在influxdb里开查询日志能看到写入请求的实际内容两个方向一对基本就找到问题在哪一层了。5.3 Grafana面板有的指标有值有的始终为0这种情况往往是MBean属性本身返回0或者Grafana的聚合间隔设置的比采集间隔还短导致在某个时间窗口内一条数据都没有绘图时被默认填充成0。解决办法是调整Grafana Panel里的Min time interval把它设置成跟jmxtrans采集频率一致或略大。比如jmxtrans每60秒采集一次那Panels的Min time interval就设成60s这样聚合分辨率不会小于数据产生间隔零值填充问题基本就消失了。5.4 长期运行的细节磁盘消耗与采集量控制跑上线时间长了之后另一个容易被忽略的问题是数据量膨胀。Kafka指标乘以多个节点再乘以一个60秒的采集频率一天下来数据量其实不小。InfluxDB的存储压缩已经做得不错但分片和保留策略还是要提前规划。建议在influxdb里设置自动清理的保留策略比如只保留30天CREATE RETENTION POLICY rp_30d ON kafka_metrics DURATION 30d REPLICATION 1 DEFAULT监控数据时间太久了意义也不大保留最近一个月足够分析问题想留更久的就导出归档别让时序库无休止膨胀。6. 实操过程中的几个真实体会这套方案我用了一段时间最大的感受是“稳定、透明、不折腾”。jmxtrans的配置虽然要花点心思写JSON但写明白一次之后加新Broker节点就是复制一段server配置的事influxdb对监控这种写入模式几乎零维护不需要人天天盯着grafana面板搭好后日常看板只需要调整时间区间和切换节点标签。如果非要说有什么缺点那就是jmxtrans对每个Kafka节点会启动独立采集线程集群规模大了以后jmxtrans自身的JVM内存会涨得比较快需要合理设置-Xmx并定期观察它的GC情况。我现在的做法是在jmxtrans部署节点上用Prometheus node exporter额外拉一层操作系统指标顺带也看着jmxtrans进程本身的状态。最后分享一个扩展的小技巧这套链路除了Kafka其实完全可以在同一个jmxtrans进程里配置其他Java中间件实例例如采集同一个节点上的ZooKeeper JMX指标ZooKeeper本身也暴露了大量JMX指标。我后来把一个集群里的Kafka和ZooKeeper的JMX采集都放到了同一个jmxtrans实例里统一输出到同一个influxdb库Grafana里建了一个总览Dashboard就把两者全部覆盖了。这种组合式的采集配置既减少了部署的进程数量也方便在同一个界面上关联排查Kafka和ZooKeeper的相互影响算是这套方案额外给我的一份回报。