某天线上服务突然出现偶发性超时,排查了半天,日志里卡在 HttpClient.execute() 一动不动,没有任何异常抛出,线程就那么"假死"了。重启能恢复,但过一阵子又复现,让人头皮发麻。
最终定位到根因:Java 在网络解析时优先尝试了 IPv6,而服务器环境实际只有 IPv4 通,导致连接超时前的等待时间被无限拉长。
报错:
dMiniProgramBackend-debug | java.net.http.HttpConnectTimeoutException: HTTP connect timed out
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:917)
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:133)
WordMiniProgramBackend-debug | at com.xkcoding.http.support.java11.HttpClientImpl.exec(HttpClientImpl.java:67)
WordMiniProgramBackend-debug | at com.xkcoding.http.support.java11.HttpClientImpl.get(HttpClientImpl.java:140)
WordMiniProgramBackend-debug | at com.xkcoding.http.HttpUtil.get(HttpUtil.java:131)
WordMiniProgramBackend-debug | at me.zhyd.oauth.utils.HttpUtils.get(HttpUtils.java:37)
WordMiniProgramBackend-debug | at me.zhyd.oauth.request.AuthWechatMiniProgramRequest.getAccessToken(AuthWechatMiniProgramRequest.java:37)
WordMiniProgramBackend-debug | at me.zhyd.oauth.request.AuthDefaultRequest.login(AuthDefaultRequest.java:61)
WordMiniProgramBackend-debug | at com.word.user.service.impl.UserServiceImpl.login(UserServiceImpl.java:54)
WordMiniProgramBackend-debug | at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
WordMiniProgramBackend-debug | at java.base/java.lang.reflect.Method.invoke(Method.java:565)
WordMiniProgramBackend-debug | at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
WordMiniProgramBackend-debug | at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:715)
WordMiniProgramBackend-debug | at com.word.user.service.impl.UserServiceImpl$$SpringCGLIB$$0.login(<generated>)
WordMiniProgramBackend-debug | at com.word.user.controller.UserController.login(UserController.java:48)
WordMiniProgramBackend-debug | at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
WordMiniProgramBackend-debug | at java.base/java.lang.reflect.Method.invoke(Method.java:565)
WordMiniProgramBackend-debug | at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:252)
WordMiniProgramBackend-debug | at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:184)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:934)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:853)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:86)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:866)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1000)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:903)
WordMiniProgramBackend-debug | at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:649)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:874)
WordMiniProgramBackend-debug | at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:710)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:128)
WordMiniProgramBackend-debug | at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
WordMiniProgramBackend-debug | at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
WordMiniProgramBackend-debug | at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:355)
WordMiniProgramBackend-debug | at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:272)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:110)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:199)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:77)
WordMiniProgramBackend-debug | at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113)
WordMiniProgramBackend-debug | at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72)
WordMiniProgramBackend-debug | at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
WordMiniProgramBackend-debug | at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
WordMiniProgramBackend-debug | at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
WordMiniProgramBackend-debug | at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1779)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:946)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:480)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:57)
WordMiniProgramBackend-debug | at java.base/java.lang.Thread.run(Thread.java:1474)
WordMiniProgramBackend-debug | Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:68)
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.HttpClientImpl.purgeTimeoutsAndReturnNextDeadline(HttpClientImpl.java:1751)
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:1348)
WordMiniProgramBackend-debug | Caused by: java.net.ConnectException: HTTP connect timed out
WordMiniProgramBackend-debug | at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:69)
WordMiniProgramBackend-debug | ... 2 more
WordMiniProgramBackend-debug | 2026-06-07 07:32:31 http-nio-8080-exec-1 me.zhyd.oauth.log.Log(error:45) [ERROR] - Failed to login with oauth authorization.
WordMiniProgramBackend-debug | me.zhyd.oauth.exception.AuthException: HTTP connect timed out
WordMiniProgramBackend-debug | at me.zhyd.oauth.utils.HttpUtils.check(HttpUtils.java:123)
WordMiniProgramBackend-debug | at me.zhyd.oauth.utils.HttpUtils.getBody(HttpUtils.java:129)
WordMiniProgramBackend-debug | at me.zhyd.oauth.request.AuthWechatMiniProgramRequest.getAccessToken(AuthWechatMiniProgramRequest.java:37)
WordMiniProgramBackend-debug | at me.zhyd.oauth.request.AuthDefaultRequest.login(AuthDefaultRequest.java:61)
WordMiniProgramBackend-debug | at com.word.user.service.impl.UserServiceImpl.login(UserServiceImpl.java:54)
WordMiniProgramBackend-debug | at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
WordMiniProgramBackend-debug | at java.base/java.lang.reflect.Method.invoke(Method.java:565)
WordMiniProgramBackend-debug | at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
WordMiniProgramBackend-debug | at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:715)
WordMiniProgramBackend-debug | at com.word.user.service.impl.UserServiceImpl$$SpringCGLIB$$0.login(<generated>)
WordMiniProgramBackend-debug | at com.word.user.controller.UserController.login(UserController.java:48)
WordMiniProgramBackend-debug | at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
WordMiniProgramBackend-debug | at java.base/java.lang.reflect.Method.invoke(Method.java:565)
WordMiniProgramBackend-debug | at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:252)
WordMiniProgramBackend-debug | at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:184)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:934)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:853)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:86)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:866)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1000)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:903)
WordMiniProgramBackend-debug | at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:649)
WordMiniProgramBackend-debug | at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:874)
WordMiniProgramBackend-debug | at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:710)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:128)
WordMiniProgramBackend-debug | at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142)
WordMiniProgramBackend-debug | at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
WordMiniProgramBackend-debug | at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:355)
WordMiniProgramBackend-debug | at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:272)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:110)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:199)
WordMiniProgramBackend-debug | at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
WordMiniProgramBackend-debug | at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:107)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:77)
WordMiniProgramBackend-debug | at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113)
WordMiniProgramBackend-debug | at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83)
WordMiniProgramBackend-debug | at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72)
WordMiniProgramBackend-debug | at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
WordMiniProgramBackend-debug | at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397)
WordMiniProgramBackend-debug | at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
WordMiniProgramBackend-debug | at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1779)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:946)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:480)
WordMiniProgramBackend-debug | at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:57)
WordMiniProgramBackend-debug | at java.base/java.lang.Thread.run(Thread.java:1474)
Java 的网络栈默认是双栈(Dual-Stack) 模式,即 IPv4 和 IPv6 同时启用。当发起一个 HTTP 请求时:
这在容器化环境(Docker / K8s)中尤其常见,因为容器网络默认就启用了 IPv6 支持。
一行参数搞定,在启动命令中加入:
java -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true -jar your-app.jar
| 参数 | 作用 |
|---|---|
-Djava.net.preferIPv4Stack=true |
强制 JVM 使用 IPv4 协议栈,彻底禁用 IPv6 |
-Djava.net.preferIPv4Addresses=true |
当 DNS 同时返回 IPv4/IPv6 地址时,优先使用 IPv4 |
两个参数一起上,双重保险。
如果是 Docker 部署,在 docker-compose.yml 或 Dockerfile 中设置:
# docker-compose.yml
services:
your-service:
image: your-image
environment:
- JAVA_TOOL_OPTIONS=-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true
为什么用 JAVA_TOOL_OPTIONS 而不是 JAVA_OPTS ?
JAVA_TOOL_OPTIONS 是 JVM 原生读取的环境变量,无需应用做任何适配,任何 Java 进程启动时都会自动加载JAVA_OPTS 需要你的启动脚本(如 entrypoint.sh)手动拼接,容易遗漏K8s 同理:
# deployment.yaml
spec:
containers:
- name: your-app
image: your-image
env:
- name: JAVA_TOOL_OPTIONS
value: "-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true"
启动后检查日志,如果看到类似输出说明已生效:
Picked up JAVA_TOOL_OPTIONS: -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true
也可以用 jinfo 验证运行中的进程:
jinfo -sysprops <pid> | grep preferIPv4
输出:
java.net.preferIPv4Stack = true
java.net.preferIPv4Addresses = true
这其实反映了一个更普遍的教训:默认行为不一定适合你的环境。 Java 的双栈设计在 IPv6 普及的今天看似"先进",但在很多内网、容器网络中反而是坑。类似的还有:
-Djava.awt.headless=true:服务器环境没 GUI,不设这个图片处理可能报错-Dfile.encoding=UTF-8:避免跨平台文件编码乱码-Duser.timezone=Asia/Shanghai:统一时区,避免日志时间混乱建议把这些基础 JVM 参数做成团队的基础镜像或启动脚本模板,一劳永逸。
[!Tip] 一句话总结
JAVA_TOOL_OPTIONS="-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true" — 加这行到环境变量,HttpClient 假死问题一去不返。
暂无评论,欢迎第一个留言。
评论