You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot应用中HttpClient请求出现ConnectException问题求助

SpringBoot中使用JDK HttpClient发起请求抛出ConnectException的排查思路

在SpringBoot应用的StartListener的onApplicationEvent方法中执行以下HTTP请求代码时,每次都会抛出java.net.ConnectException:

try {
    System.out.println("StartListener.onApplicationEvent()");
    HttpRequest request = HttpRequest.newBuilder().GET()
            .uri(URI.create(
                    "https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-A-B-C-Corse.ics"))
            .build();
    HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
    HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
    System.out.println(response.body());
} catch (IOException | InterruptedException e) {
    e.printStackTrace();
}

异常栈信息如下:

java.net.ConnectException     at
java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:573)
at
java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:123)
at
fr.utensilia.covoit.back.config.StartListener.onApplicationEvent(StartListener.java:42)
at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method)   at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)   at
org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:344)
at
org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:229)
at
org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:166)
at
org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at
org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at
org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at
org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421)
at
org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378)
at
org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:938)
at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
at
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
at
org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at
org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
at
fr.utensilia.covoit.CovoitApplication.main(CovoitApplication.java:10)
at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native
Method)   at
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)   at
org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.net.ConnectException  at
java.net.http/jdk.internal.net.http.common.Utils.toConnectException(Utils.java:1047)
at
java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:198)
at
java.net.http/jdk.internal.net.http.PlainHttpConnection.checkRetryConnect(PlainHttpConnection.java:230)
at
java.net.http/jdk.internal.net.http.PlainHttpConnection.lambda$connectAsync$1(PlainHttpConnection.java:206)
at
java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
at
java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911)
at
java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1773)
at
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833) Caused by:
java.nio.channels.ClosedChannelException  at
java.base/sun.nio.ch.SocketChannelImpl.ensureOpen(SocketChannelImpl.java:195)
at
java.base/sun.nio.ch.SocketChannelImpl.beginConnect(SocketChannelImpl.java:760)
at
java.base/sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:848)
at
java.net.http/jdk.internal.net.http.PlainHttpConnection.lambda$connectAsync$0(PlainHttpConnection.java:183)
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
at
java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:185)
... 9 more

但将相同代码放到普通Java类中执行时却能正常运行:

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Test {

    public static void main(String[] args) {
        try {
            System.out.println("StartListener.onApplicationEvent()");
            HttpRequest request = HttpRequest.newBuilder().GET()
                    .uri(URI.create(
                            "https://fr.ftp.opendatasoft.com/openscol/fr-en-calendrier-scolaire/Zone-A-B-C-Corse.ics"))
                    .build();
            HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.body());
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

    }

}

同时浏览器也能正常打开该URL,以下是排查解决的思路:

  • 检查代理配置差异:SpringBoot可能会加载系统级或配置文件中的代理设置,而普通Java程序默认不会读取这些配置。可以在代码中显式禁用代理测试:

    HttpClient httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)
            .proxy(ProxySelector.of(new InetSocketAddress("localhost", 0))) // 禁用代理
            .build();
    

    也可以打印系统属性System.getProperty("https.proxyHost")、System.getProperty("https.proxyPort")确认是否有代理被自动设置。

  • 切换事件触发时机:当前请求是在容器刷新事件中执行,此时部分网络初始化逻辑可能未完成。可以将代码迁移到ApplicationReadyEvent事件中执行,该事件在Spring容器完全启动、所有Bean初始化完成后触发。

  • 排查SpringDevTools影响:异常栈中出现了RestartLauncher,说明启用了SpringDevTools。DevTools会修改类加载器和线程环境,可能影响网络请求。可以临时关闭DevTools(移除spring-boot-devtools依赖)后重新测试。

  • 切换HTTP版本:目标服务器可能对HTTP/2支持存在兼容性问题,尝试将HttpClient的版本改为HTTP/1.1:

    HttpClient httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .build();
    
  • 添加超时与日志调试:为HttpClient添加超时配置,并开启网络日志,查看详细的交互过程:

    HttpClient httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    

    在application.properties中添加日志配置:

    logging.level.java.net.http=DEBUG
    logging.level.sun.net.www.protocol.https=DEBUG
    
  • 对比JVM参数差异:检查SpringBoot启动时的JVM参数和普通Java类启动时的参数,查看是否存在网络相关的限制(如java.net.preferIPv6Addresses等),尝试统一参数后测试。

内容的提问来源于stack exchange,提问作者Josselin Beaud'huy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 13:10:57