背景
最近项目在升级JDK17, 但原先低版本Springboot不能识别jdk17编译的字节码, 为了能够使用JDK17的语法, 因此对SpringBoot也做了升级, 直接升级到了SpringBoot 2.7.11版本.
对一些变更修改升级完成后, 本地启动服务运行, 一切正常!!! 于是发布到公司的容器环境运行, 发布平台却一直显示发布失败.
容器运行环境
- JDK 17
- SpringBoot 2.7.11
- 嵌入式Tomcat 9.0.75
- javaagent: opentelemetry-java-instrumentation
前期排查
- 查看日志文件, 查看是否有异常
日志文件没有任何异常信息 - 怀疑升级Springboot之后健康检查接口变更
访问本地健康检查接口, 能够正常访问, 健康检查接口没有变更, 其它业务接口也能正常访问 - 通过命名查看服务端口是否启动成功
结果: 服务端口绑定成功 - 通过curl命名在容器内访问健康检查端口
健康检查接口返回404异常, 这也是发布平台为什么认为服务没有启动成功的原因, 访问业务接口同样返回404异常 - 怀疑是不是哪边不小心配置了tomcat context-path根路径
经过排查, 也并没有配置tomcat context-path根路径 - 怀疑是异常没有正常抛出
于是对启动main方法内部加了try-catch, 尝试抓住异常, 但是打印"启动正常", 但因为健康检查接口返回404, 容器一会儿会kill点进程public static void main(String[] args) { try { SpringApplication.run(DashboardApplication.class, args); LoggerFactory.getLogger(DashboardApplication.class).info("启动正常"); } catch (Exception ex) { LoggerFactory.getLogger(DashboardApplication.class).error("启动异常:", ex); } }
源码debug
对上面那么多猜测排查下来还是没有找到原因, 只能采取万能debug的方式,为了方便在容器远程debug时快速的添加有效断点,先在本地启动了服务,分析下tomcat处理请求url的源码,以及路径匹配的源码位置。
本地调试发现在org.apache.catalina.mapper.Mapper#internalMap
方法中uri
参数就是访问的url地址
于是开始在容器上远程debug, 发现这个uri
参数竟然是null
, 此时怀疑是不是升级了SpringBoot之后携带升级的Tomcat存在bug
Tomcat github官网issue
尝试从tomcat github官网搜索, 看是否存在同样的问题, 还真的搜到了相关的issue
- https://github.com/apache/tomcat/pull/617
- https://lists.apache.org/thread/vzt0jxmbfm85t2s2mfjzjl1m1sslp9vy
org.apache.tomcat.util.buf.MessageBytes#toString
io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHttpAttributesGetter
io.opentelemetry.javaagent.instrumentation.tomcat.common.TomcatHelper
为什么改变MessageBytes中的type,会导致404 ???
org.apache.catalina.connector.CoyoteAdapter#postParseRequest
解决方案
最低版本要求
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.19.0