本地接口级 E2E 测试配方:JDK 8、假 PATH 和请求体的三个坑
导读
给告警规则任务级化改造做接口级 E2E 时,踩了一圈环境坑——服务在 JDK 17 下「能起端口、能收请求」,错误全在运行期深处才爆;Git Bash 里
export PATH是个假动作;curl 带中文直接乱码。每个坑都有实锤报错。这篇把过程沉淀成配方,下次本地起服务测接口照抄即可。
🎧 文章导读
🎵 背景音乐
背景:告警规则任务级化改造 需要在本地完整跑通「建规则 → 上报事件 → 命中规则 → 推弹窗」链路。 Spring Cloud 老项目(Spring Boot 2.5.7 + JDK 8 技术栈),以下配方在这套环境实测通过。
一、服务必须跑 JDK 8(不只是编译)
编译用 JDK 8 谁都记得,但运行也得是 JDK 8。JDK 17 下服务能起端口、能收请求,看起来一切正常,错误全在运行期深处才爆:
| 服务 | JDK 17 症状 |
|---|---|
| admin | Feign default method 反射 InaccessibleObjectException(启动即失败) |
| gateway | ClassNotFoundException: javax.xml.bind.DatatypeConverter(jjwt 要 JAXB,登录转发挂) |
| warning | cglib BeanCopier InaccessibleObjectException(接口能通,走到 BeanCopier 才 500) |
warning 这个最阴:接口是通的,请求打到具体逻辑才 500,而且 @Transactional 回滚不留脏数据——你会以为是业务 bug,其实是 JDK 模块系统把 cglib 的反射路封了。
启动命令用显式路径,最稳:
1 | "E:/jdk/bin/java.exe" -jar service-provider/xxx-service/target/xxx.jar |
坑:
export PATH="E:/jdk/bin:$PATH"是假的。
Git Bash 里E:后面的冒号会被当成 PATH 分隔符,这一条被拆成E和/jdk/bin两个无效条目,which java仍指向 JDK 17。mvn 编译没事是因为它吃JAVA_HOME;java -jar就中招。要用 PATH 就写 Unix 风格:export PATH="/e/jdk/bin:$PATH"。
二、最小服务组合与请求路径
不用把全家桶起起来。最小组合:
1 | flowchart LR |
MySQL / Redis / Nacos 本机常驻,不用管。
请求统一打网关,带 devDebug=1:
1 | curl "http://localhost:10001/{svc}/{path}?devDebug=1" ... |
网关日志出现 Route matched ... uri=http://127.0.0.1:<port> 即 devDebug 生效、路由正确。
绕过上游模拟:测 warning 不用起 security / integrate-iot——直接 curl alarmingEvenSink/inputIteration,这就是 security 实际调用的接口,自己造 JSON 事件即可。省掉两个服务,也省掉「上游没数据」这一类干扰。
三、登录拿 token
本地建一个测试号(超管角色 + 绑定园区,建在 building_admin 库;被清了就按 sys_user + sys_user_role + sys_user_park 三表重建)。
密码哈希算法与前端 JsSHA numRounds=2 一致(已实测):
1 | SALT_B64=$(printf 'e2etest' | base64) # 用户名 base64 做盐 |
注意第二轮:先把第一轮的 hex 字符串 xxd -r -p 转回原始字节,再做 sha512 hex。直接对 hex 字符串再哈希会得到错误结果。
1 | curl -X POST "http://localhost:10001/admin/user/login" -H "Content-Type: application/json" \ |
密码连错 5 次锁号(
is_enable置位),别拿没把握的 hash 反复试。先用一次确认 HASH 算对了再进脚本循环。
四、请求体三坑
1. 中文必须文件投递。 Windows Git Bash 里 curl -d 直接带中文,按 GBK 编码发出去,服务端 Jackson 报 Invalid UTF-8 middle byte。JSON 写进 UTF-8 文件,用 curl --data-binary @file 发送,避开终端编码干扰。
2. 裸 Date 字段传 epoch 毫秒。 没有 @JsonFormat 的 Date 字段(如 AlarmingEventData.alarmTime)不收 "yyyy-MM-dd HH:mm:ss"(报「请求参数不正确」),传毫秒数:
1 | { "alarmTime": 1786000430000 } |
3. 网关偶发 500 是 DNS。 本机 DNS 偶发超时导致 DnsNameResolverTimeoutException,重试即可,不是代码问题。偶发 500 先重试两次再开始排查,能省半小时。
五、排查看日志顺序
网关 500 时的固定顺序:
- 先看 gateway 日志
Route matched的 uri——确认路由到了正确的服务端口,devDebug 生效。 - 再看被测服务日志——路由对了才是服务内部问题。
服务后台启动时输出重定向:
1 | "E:/jdk/bin/java.exe" -jar xxx.jar > /tmp/xxx.log 2>&1 & |
报错直接过滤:
1 | grep -v DEBUG /tmp/xxx.log | tail -50 |
小结:照抄清单
下次本地起服务做接口级 E2E,按这个顺序:
- 显式路径启 JDK 8:
"E:/jdk/bin/java.exe" -jar ...(别信export PATH的 Windows 路径写法)。 - 最小组合:gateway + admin + 被测服务;测下游服务用上游实际调用的接口直接造事件。
- 算好登录 HASH(两轮 sha512,第二轮前先转字节),错 5 次锁号,谨慎试。
- 请求体:中文走
--data-binary @file,裸 Date 传 epoch 毫秒。 - 偶发 500 先重试(DNS);持续 500 按「gateway 路由日志 → 被测服务日志」顺序查。
这套配方最大的价值不是某一条命令,而是把「以为是业务 bug、实际是环境问题」的几类伪装提前点名:JDK 运行期反射、假 PATH、GBK 编码、DNS 抖动。环境坑的特征都是报错位置和真实原因隔着好几层,先排除环境再查业务,顺序不能反。