XXL-JOB 2.5.0 多节点部署踩坑总结 场景生产环境将 xxl-job-core 从 2.3.x 升级至 2.5.0calculation 服务部署在双节点11.122.187.86 / 11.122.187.87升级后 XXL-JOB Admin 无法调用 calculation 执行器报Connection reset。一、背景说明SD 系统使用 XXL-JOB 做定时任务调度整体结构如下┌──────────────────────────────────────────┐ │ sdms-job调度中心 自身执行器 │ │ server.port 9093 │ │ executor.appname xxl-job-executor-sample│ │ executor.port 9998 │ └────────────────┬─────────────────────────┘ │ 调度 ┌───────────┴──────────┐ ▼ ▼ sd-calculation sd-calculation (11.122.187.86) (11.122.187.87) server.port 9094 server.port 9094 executor.port 9988 executor.port 9988 appname em-calculation-executor升级 xxl-job-core 到2.5.0后sdms-job 自身执行器正常但调度中心调用 sd-calculation 的两个节点时持续报错xxl-job remoting error(Connection reset), for url : http://11.122.187.86:9988/run xxl-job remoting error(Connection reset), for url : http://11.122.187.87:9988/run二、遇到的报错清单升级过程中依次遭遇以下问题报错 1NoClassDefFoundError: io/netty/handler/timeout/IdleStateHandlerjava.lang.NoClassDefFoundError: io/netty/handler/timeout/IdleStateHandler at com.xxl.job.core.server.EmbedServer.start(EmbedServer.java:...)原因xxl-job-core 2.5.0 依赖 Netty但项目原有 Netty 版本4.1.43.Final与 2.5.0 要求不兼容或 Netty 未被正确传递依赖。解决在 pom.xml 中将 Netty 显式升级到4.1.90.FinaldependencygroupIdio.netty/groupIdartifactIdnetty-all/artifactIdversion4.1.90.Final/version/dependency报错 2Optional int parameter triggerStatus cannot be translated into a null valuejava.lang.IllegalStateException: Optional int parameter triggerStatus is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.原因xxl-job 2.5.0 Admin 页面的某些查询接口中请求参数triggerStatus使用了基本类型int接收但前端传参时值为空Spring MVC 无法将空值转为int。解决在调度中心sdms-job对应 Controller 的入参中将int triggerStatus改为Integer triggerStatus。报错 3Connection reset— 核心问题xxl-job remoting error(Connection reset), for url : http://11.122.187.86:9988/run这是本次升级最核心的问题下一章详细分析。三、核心问题Connection reset的根本原因3.1 XXL-JOB Executor 的通信机制XXL-JOB Executor 内嵌了一个Netty HTTP Server专门用于接收来自 Admin 的调度指令/run、/beat、/kill等。Admin ──HTTP──▶ Executor 的 Netty Serverexecutor.port──▶ 执行 JobHandler这个 Netty Server 监听的端口就是配置文件中的executor.port与 Spring Boot 的server.port完全独立。3.2 IP 自动探测机制当配置文件中executor.ip和executor.address均为空时XXL-JOB 会调用IpUtil.getIp()自动探测本机 IP逻辑如下// XXL-JOB 源码 IpUtil.getIp()简化for(NetworkInterfaceiface:NetworkInterface.getNetworkInterfaces()){if(iface.isLoopback()||iface.isVirtual()||!iface.isUp())continue;for(InetAddressaddr:iface.getInetAddresses()){if(addrinstanceofInet4Address){returnaddr.getHostAddress();// 返回第一个找到的非回环 IPv4}}}对于 86、87 两台可直接访问的宿主机而言自动探测完全没有问题86 节点自动探测到11.122.187.8687 节点自动探测到11.122.187.87各自向 Admin 注册自己的地址Admin 可正常回调。⚠️注意如果是容器Docker/K8s部署自动探测拿到的是容器内网 IP如172.17.0.xAdmin 将无法路由此时必须显式指定ip和address。3.3 问题链路还原本次Connection reset的根本原因是配置错误——有人试图把两个节点的地址用逗号写在同一份配置里详见 3.4导致注册地址非法Admin 拿到错误的 URL 后发起调用自然 reset。1. sdms-calculation 配置了非法 address逗号分隔多个 IP 注册地址http://11.122.187.87:9988,http://11.122.187.86:9988整体当成一个字符串 2. Admin 触发任务 调用http://11.122.187.87:9988,http://11.122.187.86:9988/run ← 非法 URL 3. 报错 Connection reset为什么 sdms-job 主节点没问题sdms-job 只部署一个节点address为空自动探测到本机 IP 正常注册Admin 与执行器在同一服务内调用自己链路完全可达。3.4 配置写成多个 IP 逗号分隔是错误的部分同学尝试将两个节点的 IP 写在同一份配置里# ❌ 错误写法executor:address:http://11.122.187.87:9988,http://11.122.187.86:9988ip:11.122.187.87,11.122.187.86这是不合法的。executor.address和executor.ip是单值字段代表这个 JVM 实例自己的地址。逗号分隔后框架会把整个字符串当成一个 URL 去注册导致注册异常Admin 控制台出现非法地址。四、多节点部署的正确配置方式核心原则同一个项目打一个 JAR 包部署到多台机器。每台机器启动后XXL-JOB 自动探测本机 IP分别向 Admin 注册各自的地址从而在 Admin 上形成一个多机执行器集群。4.1 正确理解多节点 ≠ 一份配置写多个 IPXXL-JOB 的多节点是通过**“多个实例各自注册”**实现的不是在一份配置里写多个地址同一份 application-emeaprod.ymlappname em-calculation-executorip/address 留空 │ ├── 部署到 11.122.187.86 → 启动后自动注册 http://11.122.187.86:9988 │ └── 部署到 11.122.187.87 → 启动后自动注册 http://11.122.187.87:9988 ↓ 两个实例都向 Admin 注册后 ↓ Admin 中 em-calculation-executor 执行器组 ┌─────────────────────────────┐ │ http://11.122.187.86:9988 │ [在线] │ http://11.122.187.87:9988 │ [在线] └─────────────────────────────┘ 按路由策略分发任务4.2 配置文件两台机器共用同一份ip和address留空XXL-JOB 在每台机器上启动时自动探测本机 IP 注册两台机器无需区分配置# application-emprod.ymlxxl:job:admin:addresses:http://vicoe-xs-job-prod.earth.xpaas.t.comaccessToken:uqCkJP#xB4mmiPZ3WIVvO9sQP78aXd!timeout:30executor:appname:em-calculation-executor# 两台机器相同代表同一个执行器组address:# 留空自动探测本机 IP 生成注册地址ip:# 留空自动探测本机 IPport:9988# Netty 监听端口两台机器相同logpath:/data/applogs/emeaCalculation/jobhandlerlogretentiondays:-1为什么 86 和 87 可以留空自动探测因为两台机器均为可直接访问的宿主机网络接口与业务 IP 一一对应。86 机器自动探测结果就是11.122.187.8687 机器自动探测结果就是11.122.187.87Admin 可以直接回调无需手动指定。4.3 启动命令两台机器相同java-jarsdms-calculation.jar--spring.profiles.activeemeaprod86 节点启动 → 自动注册http://11.122.187.86:998887 节点启动 → 自动注册http://11.122.187.87:99884.4 什么情况下需要显式指定 ip/address场景是否需要显式指定裸机 / VM单网卡IP 直接可达❌ 不需要留空自动探测即可裸机 / VM多网卡需指定特定网卡✅ 需要否则可能探测到错误网卡容器部署Docker/K8s宿主机与容器 IP 不同✅ 必须指定宿主机 IP否则注册容器内网 IP 导致 Admin 无法回调五、实现轮询效果两台机器都正确注册后在XXL-JOB Admin → 任务管理中将em-calculation-executor执行器下的任务路由策略设为路由策略说明ROUND轮询依次调用每个在线节点均衡分发 ✅ 推荐RANDOM随机随机选一个节点FAILOVER故障转移优先调用第一个在线节点故障时切换BUSYOVER忙碌转移优先调用空闲节点六、验证步骤步骤 1确认 9988 端口在宿主机上监听在 86/87 节点执行ss-lntp|grep9988# 或netstat-lntp|grep9988期望输出java ... LISTEN 0.0.0.0:9988步骤 2确认 Admin 可达执行器端口在 Admin 所在机器执行curl-vhttp://11.122.187.86:9988/ --max-time3curl-vhttp://11.122.187.87:9988/ --max-time3只要没有Connection reset/Connection refused即为可达返回什么 HTTP 状态码不重要。步骤 3查看执行器启动注册日志在 sdms-calculation 日志中搜索xxl-job registry success确认打印的注册地址是http://11.122.187.86:9988而不是http://172.17.x.x:9988。步骤 4Admin 控制台确认执行器管理 → emea-calculation-executor机器地址列表应显示http://11.122.187.86:9988 [在线] http://11.122.187.87:9988 [在线]七、总结问题原因解决方案NoClassDefFoundError: IdleStateHandlerNetty 版本不兼容 2.5.0升级netty-all到4.1.90.FinaltriggerStatus cannot be translated into nullAdmin Controller 参数类型用int接收空值改为Integer包装类型Connection reset无法调用执行器address/ip用逗号写了两个节点的地址框架将整个字符串当成非法 URL 注册Admin 调用失败留空让每台机器自动探测各自 IP两台机器用相同appname各自注册即可组成集群多节点配置误区认为在一份配置里写多个 IP 就能告知两台机器实际上address/ip是当前实例自身地址的单值字段同一 JAR 包部署到多台机器每台各自启动注册Admin 自动感知所有在线节点核心记住一句话XXL-JOB 多节点集群的正确做法同一份配置ip/address留空 同一个appname部署到多台可直接访问的机器每台机器启动后自动探测本机 IP 向 Admin 注册Admin 按路由策略轮询/随机等在所有在线节点间分发任务。