Zabbix Java Gateway 这东西,老运维应该不陌生,但真正动手部署过的人往往栽在同一个地方:Gateway 装好了,日志里却全是连接失败。说白了,Zabbix Server 要监控 Java 程序的内存、线程、GC 这些 JVM 指标,靠它自己没法直接跟 JVM 对话,中间必须有一个翻译官——Java Gateway。它是独立进程,默认监听 10052 端口,Zabbix Server 把 JMX 请求发给它,它再转给被监控的 Java 应用,数据原路返回。这篇部署笔记就是把这套链路完整捋一遍:装 Gateway、给 Java 应用开 JMX、前端接入主机和模板,最后把常见的连接报错一次说清。适合已经装好 Zabbix Server、打算增加 Java 应用监控的人,也适合被 Connection refused 逼到挠头的新手。
1. 为什么 Java Gateway 非要单独占一个进程
1.1 内嵌 JMX 轮询的瓶颈
先讲个容易混淆的点。Zabbix Server 并不是没有 JMX 采集能力,它进程里本来就带着 JMX poller。但这里有个现实问题:Server 的 poller 线程是和普通 agent 采集、SNMP 采集、trapper 采集共享资源池的,JMX 查询又特别重,一次查询要先做 RMI 握手,再创建连接,拿到 MBean 属性,最后拆包回传。Java 实例少的时候无所谓,实例一多,poller 线程全都跪在等待上,普通监控项跟着遭殃。
我之前维护过一套环境,二十多个 Java 微服务同时开 JMX,Server 的 item 队列积压到几分钟才刷新一次,基础监控全滞后。后来把 JMX 任务全部挪到独立 Gateway 上,Server 侧的负载立竿见影降下来。这个问题的本质是:JMX 采集属于典型的慢操作,而 Zabbix Server 的核心职责是调度和汇总,让慢操作占用核心线程池,等于让快递分拣员自己去跑长途送货,效率一定难看。
1.2 Gateway 隔离层的作用
独立进程最大的好处是故障隔离。Gateway 挂了,Zabbix Server 只损失 JMX 相关监控,agent 采集、SNMP 采集、主动上报全不受影响;反过来,一个 JVM 的连接卡死了,worker 线程卡住的是 Gateway 自己的线程池,不会反向拖垮 Server。这对生产环境太重要了,我最怕的就是一个应用状态异常,把整台监控服务器的采集能力全带走。
从架构上看,这条链路很清晰:Zabbix Server 通过内部协议把 JMX 请求发给 Gateway,Gateway 再通过标准 JMX/RMI 协议连接目标 JVM,拿到 MBean 属性后原路返回。Zabbix Server 不需要关心 JMX 协议的细节,只需要告诉 Gateway“去这个地址拿这个监控项的值”就够了。这种设计让 Gateway 成了一个标准代理,后续想加认证、SSL、连接池,都只改 Gateway 这一层,不用动 Server 核心。
1.3 哪些场景必须上 Gateway
不是所有环境都必须部署 Java Gateway,但以下四类场景建议别省:
- Java 实例数量多。哪怕只有五六个实例,每个实例几十个监控项,内嵌 poller 就可能开始排队。
- 需要采集 Web 容器、消息中间件等第三方 JMX 指标。这些 MBean 对象名复杂、查询频率高,放在独立进程中更好控制。
- 网络分区。Zabbix Server 在外网,Java 应用在内网,Gateway 可以部署在内网充当跳板,避免给 Server 开一大堆防火墙端口。
- 安全和统一出口。团队要求所有出网监控流量都走指定主机,Gateway 就是天然的出入口。
判断标准很简单:如果你已经因为 JMX 丢过数据、卡过采集,就不要再犹豫,直接上 Gateway。
2. 部署前的三个决定:版本、端口和 JMX 开关
2.1 版本匹配别贪新
Java Gateway 版本必须和 Zabbix Server 主版本对齐。5.0 的 Server 配 6.0 的 Gateway,协议不兼容,连上也会出现各种奇怪报错。处理办法很粗暴:全用同一个大版本的最新 patch。Zabbix 有 LTS 版本的概念,只要 Server 是 LTS,Gateway 也跟着用同一个 LTS 大版本,省心很多。
版本匹配这件事我再多说一句:有人为了“新功能”给 Server 升了级,但 Gateway 没动,结果新 Server 对 Gateway 发了一个旧版本不认识的请求,整个 JMX 采集全挂。升级时一定要把 Server、Gateway 甚至前端版本列成清单一起检查,别单独动某一环。
2.2 端口规划一次做清
JMX 监控涉及三个方向的端口,部署前最好列个表,省得后面排查时一头雾水:
| 链路 | 默认端口 | 说明 |
|---|---|---|
| Zabbix Server -> Java Gateway | 10052 | Gateway 监听端口,Server 侧通过 JavaGatewayPort 指定 |
| Java Gateway -> 被监控 JVM | 12345 | JVM 启动参数里的 jmxremote.port,即 JMX RMI 注册端口 |
| Java Gateway -> 被监控 JVM | 12346 | RMI 导出端口,建议固定,否则每次启动随机生成 |
最后一行的 RMI 导出端口是最容易踩的坑。JVM 只固定了注册端口,但 RMI 实际传输对象的端口如果不显式指定,会在启动时随机挑一个。防火墙你只放行了 12345,连接照样超时。所以规划阶段就要定好两个端口,一个注册端口、一个导出端口,防火墙两个都放。
2.3 被监控端 JMX 开关确认
这一步最容易被人跳过。Java 应用默认不会开 JMX,必须在启动脚本里加参数。如果你不是应用负责方,先拿到应用的启动权限和重启窗口,别等 Gateway 部署完了才发现目标 JVM 根本不开端口。
另外确认一下 JDK 版本。现在常见的 JDK 8、JDK 11、JDK 17 都支持标准的 JMX 启动参数,但不同发行版对参数的宽容度不同,有些自定义的 JDK 会忽略不认识的参数,导致你以为开了 JMX 实际没开。稳妥做法是部署后在应用服务器上确认端口真的在监听,这个后面会专门讲验证方法。
3. 安装 Java Gateway 并接上 Zabbix Server
3.1 用官方软件仓库安装
安装本身不复杂,重点是仓库要对。以 Red Hat 系为例,先从 Zabbix 官方仓库下载对应版本的 release 包并安装,然后直接装组件:
rpm -ivh zabbix-release-5.0-1.el7.noarch.rpm yum clean all yum install -y zabbix-java-gatewayDebian 系也一样,下载对应发行版的 release 包,dpkg 安装后刷新索引再装:
dpkg -i zabbix-release_5.0-1+focal_all.deb apt update apt install -y zabbix-java-gateway装完先别急着启动,确认两个关键路径:配置文件在/etc/zabbix/zabbix_java_gateway.conf,日志目录在/var/log/zabbix/。如果你的系统启用了 SELinux,还要留意 zabbix_java_gateway 服务的 SELinux 策略,不过大多数发行版装完包会自动配套好,不需要手动处理。
3.2 配置文件四个关键参数
默认配置其实就能跑,但建议打开看一眼,至少要理解这四个参数:
# /etc/zabbix/zabbix_java_gateway.conf LISTEN_IP="127.0.0.1" LISTEN_PORT="10052" PID_FILE="/var/run/zabbix/zabbix_java_gateway.pid" START_POLLERS="5"- LISTEN_IP 不填就监听所有网卡;填 127.0.0.1 只允许本机 Server 访问,安全但局限。
- LISTEN_PORT 保持默认 10052,改了的话 Server 侧要同步改。
- PID_FILE 一般不用动。
- START_POLLERS 是 Gateway 内部 worker 线程数,每个 worker 同一时间只能处理一个 JMX 查询,这是后续调优的核心参数。
改完配置后重启服务,配置文件不是热加载的,这点和很多现代中间件不一样,务必记牢。
3.3 Server 端要开启 Java poller
Gateway 装好只是第一步,Zabbix Server 自己还处于“不关心 JMX”的状态。需要修改 Server 配置文件:
# /etc/zabbix/zabbix_server.conf JavaGateway=127.0.0.1 JavaGatewayPort=10052 StartJavaPollers=5JavaGateway 指向本机没问题,因为 Java Gateway 默认部署在 Server 同机;如果 Gateway 在另一台机器,这里填那台机器的 IP。StartJavaPollers 是 Server 分配给 JMX 采集的专用 poller 线程数。特别注意:StartJavaPollers 如果保持默认 0,前端所有 JMX 监控项都会显示“不支持”,这是新手最容易忽略的地方。
改完 zabbix_server.conf 后必须重启 zabbix-server 进程,光重载配置不生效。有人说改了没反应,十有八九就是没重启干净,建议重启后看一眼进程启动时间确认。
3.4 启动后的自检步骤
按顺序做以下三步,基本能确定 Gateway 是否就绪:
systemctl enable --now zabbix-java-gateway ss -lntp | grep 10052 tail -f /var/log/zabbix/zabbix_java_gateway.logss 能看到 10052 端口在监听,说明进程起来了。日志里没有 ERROR 或者 Unable to load configuration 之类的报错,就说明配置解析正常。这里有个细节:如果日志完全没动静,反而是正常的,因为 Gateway 在没有收到 Server 请求时不会输出太多信息,真正的问题要等前端有 JMX 监控项后才会暴露。
提示:修改配置前先备份,尤其是团队共用的服务器。我曾经在别人服务器上改了 LISTEN_IP,结果把别的项目正在用的 Gateway 搞挂了,从那以后养成了任何配置改动先 cp 一份的习惯。
4. 被监控端 Java 程序的 JMX 开启方法
4.1 最简无认证参数模板
Java 应用开启 JMX,核心就是在启动脚本里加一段 JVM 参数。先给一个最简模板:
JAVA_OPTS="$JAVA_OPTS \ -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=12345 \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false \ -Dcom.sun.management.jmxremote.local.only=false \ -Djava.rmi.server.hostname=192.0.2.20 \ -Dcom.sun.management.jmxremote.rmi.port=12346"逐个解释一下:
com.sun.management.jmxremote是总开关,没有它后面全白搭。port=12345是 RMI 注册端口,防火墙要放行。authenticate=false表示不启用用户名密码认证,只能用于内网调试,生产不建议。ssl=false同理,先跑通链路再谈加密。local.only=false允许非本机发起连接。新版本 JDK 默认值更严格,不显式关掉可能只能本机访问。java.rmi.server.hostname=192.0.2.20一定要填应用服务器的对外 IP。如果不填,JVM 会尝试用主机名解析,解析出来的地址不对,Gateway 就会拿到一个连不上的 stub 地址。rmi.port=12346就是前面说的固定 RMI 导出端口。
这个模板我先强调一点:那段java.rmi.server.hostname参数,几乎每个踩坑的人都栽在它身上。你以为端口通了就没事,结果 Gateway 日志一直报 refused,原因就是 JVM 注册的 RMI 地址指向了 localhost。
4.2 把 RMI 导出端口固定下来
刚才提到随机端口问题,这里展开讲透。JMX 连接建立时会起两个端口:注册端口是固定的,但真正传输数据的 RMI 对象端口默认是动态的。防火墙如果只放行了注册端口,Gateway 能连上注册端口,但后续数据通道被防火墙拦死,表现就是连接反复超时。
所以生产环境务必加上-Dcom.sun.management.jmxremote.rmi.port=12346把导出端口固定下来,然后防火墙把 12345 和 12346 都放行。这个参数在 JDK 7 及以后都支持,老版本 JDK 不支持,不过还在用 JDK 6 的环境也确实太老了,不建议继续凑合。
4.3 生产环境的认证模式
无认证模式跑通后,生产环境至少要把认证加上。配置分两个文件:一个是密码文件,一个是权限文件。从 JDK 目录复制模板过来改:
cp $JAVA_HOME/lib/management/jmxremote.password /etc/zabbix/jmxremote.password chmod 600 /etc/zabbix/jmxremote.password echo "monitoruser strongpass" >> /etc/zabbix/jmxremote.password权限文件手册一般要求包含只读角色的定义,先给最低权限:
echo "monitoruser readonly" > /etc/zabbix/jmxremote.access然后启动参数里替换认证开关:
-Dcom.sun.management.jmxremote.authenticate=true \ -Dcom.sun.management.jmxremote.password.file=/etc/zabbix/jmxremote.password \ -Dcom.sun.management.jmxremote.access.file=/etc/zabbix/jmxremote.access"密码文件权限必须 600,文件属主尽量和应用启动用户一致,否则 JVM 会因为权限过宽直接拒绝启动。后面在 Zabbix 前端配置里,填的就是 monitoruser 和 strongpass 这对账号密码。
注意:认证模式下 Zabbix 前端的主机配置里需要额外填写 JMX 用户名和密码,宏里面通常叫 {$JMX_USER}、{$JMX_PASSWORD},忘了填的话监控项会报 Authentication failed。这个细节在官方文档里不算突出,但实操中几乎必然遇到。
4.4 用工具验证 JMX 端口
参数加好、应用重启后,先别急着回 Zabbix 前端,在被监控机上确认端口真的在监听:
ss -lntp | grep 12345 ss -lntp | grep 12346然后在 Gateway 所在机器上验证网络连通性:
nc -zv 192.0.2.20 12345 nc -zv 192.0.2.20 12346端口能通只代表网络层没问题。要真正验证 JMX 协议层是否正常,可以用 jconsole 连一下,或者在 Zabbix 前端监控项里点“测试”按钮。我最推荐的其实是后一种,因为测试时走的就是 Zabbix Server 到 Gateway 到 JVM 的完整链路,一步到位。
5. 前端接入:主机、模板与监控项
5.1 把 Java 应用注册成独立主机
Zabbix 前端的接入逻辑很明确:一个 Java 应用在监控系统里就是一个主机。和 Linux agent 主机不冲突,建议单独建一个主机,避免把 JMX 监控项和系统监控项混在一起。
操作路径:数据采集 -> 主机 -> 创建主机。关键字段如下:
| 字段 | 值 |
|---|---|
| 主机名称 | JavaApp-Prod |
| 所属主机组 | 按业务划分 |
| 接口类型 | JMX agent |
| JMX 接口 IP | 192.0.2.20 |
| JMX 接口端口 | 12345 |
这里最容易犯的错是接口类型选成 Agent,然后填了 JMX 端口,Zabbix 会一直尝试用 agent 协议去连接 12345 端口,监控项全挂。接口类型必须明确选 JMX agent,别图省事。
5.2 关联官方模板与宏
官方模板库里有一个通用 Java JMX 模板,不同版本名字略有差异,5.0 里常见的是 Template App Generic Java JMX,6.0 的模板列表里可能叫 Generic by JMX。找名字里带 Java 或 JMX 的官方模板就行,它自带 JVM 内存、GC、线程、类加载等基础监控项,能覆盖大部分通用需求。
关联模板后还有一个关键动作:打开主机的宏配置,把模板里 JMX 端口相关的宏改成 12345。模板默认端口往往写的是 127.0.0.1 或者某个示例端口,不改的话监控项会连不上你的应用。不同模板的宏名不一样,常见的可能是 {$JMX_PORT} 或 {$JAVA_JMX_URL},具体以你模板定义页为准,把端口相关宏替换成实际值。
5.3 写一个自定义 JMX 监控项
官方模板覆盖的是 JVM 普遍指标,你要是想监控某个具体的业务 MBean,就得自己写监控项。在主机 -> 监控项 -> 创建监控项里,类型选 JMX agent,键值格式如下:
jmx["java.lang:type=Memory","HeapMemoryUsage.used"]这段键值的意思是查 MBean 对象名java.lang:type=Memory的属性HeapMemoryUsage.used,返回的是堆内存已用字节数。监控项的信息类型选浮点数,单位填 B(字节)。
再给一个 GC 的例子:
jmx["java.lang:type=GarbageCollector,name=G1 Young Generation","CollectionTime"]注意 MBean 对象名里如果带逗号或空格,整个对象名要用引号包起来,键值本身再用双引号包一层,转义别搞错。写完点右下角“测试”,输入键值直接就能看到返回结果,能跑通说明链路没问题,省得等到下一个采集周期才发现报错。
5.4 等待数据并核对链路
关联模板和写监控项之后,别急着下结论,等一两个采集周期,一般 1 到 2 分钟内数据就会出现在最新数据里。看到绿色 ZBX 图标说明采集正常;显示“不支持”就顺着键值、宏、认证三方面排查。
顺便说一句:第一次配置 JMX 监控,最新数据里有那么两三个“不支持”是正常的,因为有些监控项依赖的 MBean 在特定 JDK 版本里不存在。把真正关键的那几个指标盯好就行,不要因为几个无关紧要的监控项全盘推翻配置。
6. 采集失败排查与调优实录
6.1 先学会看 Gateway 日志
排查 JMX 问题,日志是第一现场。Java Gateway 的日志位置固定:
tail -f /var/log/zabbix/zabbix_java_gateway.log最常出现的两行错误:
cannot connect to [192.0.2.20:12345]: Connection refused cannot connect to [192.0.2.20:12345]: Connection timed outConnection refused 和 Connection timed out 的指向完全不同:refused 是目标主机明确拒绝了 TCP 连接,多半是端口没监听或者 JMX 没开;timed out 是网络层被拦或者路由不通,多半是防火墙、安全组问题。这两个词的区分能帮你砍掉一半的排查分支。
6.2 Connection refused 的排查顺序
遇到 refused,按这个顺序查:
- 在被监控机上执行
ss -lntp | grep 12345,看不到监听就说明 JMX 参数没生效,回应用启动脚本里检查参数。 - 确认应用进程的命令行里真的带上了 JMX 参数:
ps -ef | grep java,看进程实际参数有没有把启动脚本的变量展开。 - 如果应用是通过容器跑的,确认端口是否正确映射到宿主机,容器内 JMX 参数里的
java.rmi.server.hostname还要写宿主机 IP。 - 检查防火墙:
firewall-cmd --list-all或者云平台安全组有没有放行 12345 和 12346。 - 确认 RMI 导出端口是否固定,没有固定的话防火墙放行注册端口也没用。
这套顺序我每次都用,基本能覆盖九成 refused 场景。
6.3 连接成功但监控项一直不支持
端口通了、日志里没有连接报错,但监控项就是“不支持”,这种情况最常见的原因是 MBean 名写错。Java 的 MBean 对象名是区分大小写的,type=Memory写成了type=memory就查不到。
还有一类原因是模板宏没改。关联模板后如果没设置 JMX 端口宏,模板里的监控项会在默认端口上反复失败。解决方法是回到主机宏设置里,把模板用到的端口宏改成实际值,然后等下一次采集。
另外提个通用技巧:在 Zabbix 前端监控项列表里点测试按钮,输入键值直接看返回。我习惯先在 jconsole 的 MBean 浏览器里找到准确的属性名,再复制到 Zabbix 键值里,这样极少出错。
6.4 性能相关的两个线程数
JMX 采集的性能瓶颈通常在两个线程数的配合上:
| 参数 | 位置 | 作用 |
|---|---|---|
| StartJavaPollers | zabbix_server.conf | 控制 Server 同时向 Gateway 发起的连接数 |
| START_POLLERS | zabbix_java_gateway.conf | 控制 Gateway 内部处理 JMX 查询的 worker 数 |
这两个值建议保持一致。比如 Server 开了 5 个 poller,Gateway 也至少保持 5 个 worker,否则任务会排队。监控项特别多、采集频率又高时,可以把两处同时调到 10 到 20,但我个人不建议无脑拉高,JMX 查询再快也是走网络和 CPU 的,线程数翻倍往往意味着目标 JVM 的连接压力也跟着翻倍,从 5 调到 20 没解决问题,反而把 JVM 搞卡的情况我见过不止一次。
调优后的验证方式也很简单:观察一个采集周期内最新数据是否全部更新,以及 Gateway 日志里是否频繁出现 no free workers 之类的排队提示。
6.5 问题速查表
| 症状 | 可能原因 | 处理办法 |
|---|---|---|
| Connection refused | 端口未监听 / JMX 参数没生效 | 检查启动脚本和 ps 进程参数 |
| Connection timed out | 防火墙拦截 / 网络不通 | 检查防火墙、安全组、路由 |
| 连上后立刻断开 | RMI 导出端口随机导致防火墙拦死 | 固定 rmi.port 并放行 |
| Authentication failed | 账号密码错误或 access 文件权限不对 | 检查前端宏配置和文件权限 |
| 监控项一直不支持 | MBean 名错 / 宏没改 / JDK 版本差异 | 用前端测试按钮逐个验证键值 |
| 数据刷新很慢 | poller 线程不足或目标 JVM 连接过多 | 同步调大 StartJavaPollers 和 START_POLLERS |
再补一条进阶排查技巧:实在查不出问题,可以把 zabbix_server.conf 里的 LogLevel 临时调高,重启 Server 后日志会打出 JMX item 分发和超时相关的内部信息,用完记得调回来。这个方法能救命,但会造成日志量暴增,别在生产环境长期开着。
我自己在部署这类监控时有个习惯:先把无认证模式跑通,再切认证,每一步都用 nc 和前端测试确认,别一上来就加 SSL 加认证,排错难度直接翻倍。还有个小技巧,Java 应用发布比较频繁的话,记得在发布脚本里自动校验 JMX 参数字段是否还在,应用团队改启动参数时经常顺手把 JMX 相关配置整段删掉,等 Zabbix 告警出来才发现,那时候已经丢了半个小时的监控数据。先跑通,再加固,最后再固化到发布流程里,这套路对 Zabbix 加 JMX 这套组合尤其适用。