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

Spring RestClient与JdkClientHttpRequestFactory、WireMock测试遇EOF异常求助

问题:RestClient结合WireMock测试时抛出「EOF reached while reading」异常

在使用RestClient的Spring Boot应用中编写WireMock测试时,持续遇到EOF reached while reading异常。运行MyGatewayTest时抛出如下异常:

org.springframework.web.client.ResourceAccessException: I/O error on PUT request for "http://localhost:57591/something": EOF reached while reading

    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.createResourceAccessException(DefaultRestClient.java:557)
    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:482)
    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.retrieve(DefaultRestClient.java:444)
    at org.springframework.web.client.support.RestClientAdapter.exchangeForBody(RestClientAdapter.java:73)
    at org.springframework.web.service.invoker.HttpServiceMethod$ExchangeResponseFunction.lambda$create$4(HttpServiceMethod.java:379)
    at org.springframework.web.service.invoker.HttpServiceMethod$ExchangeResponseFunction.execute(HttpServiceMethod.java:336)
    at org.springframework.web.service.invoker.HttpServiceMethod.invoke(HttpServiceMethod.java:130)
    at org.springframework.web.service.invoker.HttpServiceProxyFactory$HttpServiceMethodInterceptor.invoke(HttpServiceProxyFactory.java:303)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:220)
    at jdk.proxy2/jdk.proxy2.$Proxy16.doSomething(Unknown Source)
    at com.example.restclientjdkissue.MyGateway.doSomething(MyGateway.java:15)
    at com.example.restclientjdkissue.MyGatewayTest.testDoSomething(MyGatewayTest.java:27)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.io.IOException: EOF reached while reading
    at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:964)
    at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:133)
    at org.springframework.http.client.JdkClientHttpRequest.executeInternal(JdkClientHttpRequest.java:102)
    at org.springframework.http.client.AbstractStreamingClientHttpRequest.executeInternal(AbstractStreamingClientHttpRequest.java:70)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
    at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:476)
    ... 14 more
Caused by: java.io.EOFException: EOF reached while reading
    at java.net.http/jdk.internal.net.http.Http2Connection$Http2TubeSubscriber.onComplete(Http2Connection.java:1655)
    at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadSubscription.signalCompletion(SocketTube.java:648)
    at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:853)
    at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(SocketTube.java:181)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:207)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:280)
    at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:233)
    at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(SocketTube.java:782)
    at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadEvent.signalEvent(SocketTube.java:965)
    at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowEvent.handle(SocketTube.java:253)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.handleEvent(HttpClientImpl.java:1467)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.lambda$run$3(HttpClientImpl.java:1412)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:1412)

但显式配置RestClient的requestFactory为SimpleClientHttpRequestFactory时,测试能正常运行:

@Bean
public RestClient restClient() {
  return RestClient.builder()
      .requestFactory(new SimpleClientHttpRequestFactory())
      .baseUrl(properties.baseUrl())
      .build();
}

想确认这是RestClient、JdkClientHttpRequestFactory的bug,还是其他环节存在问题?


分析与解答

这个问题的核心是JDK HttpClient(JdkClientHttpRequestFactory默认使用的客户端)与WireMock之间的HTTP/2兼容性问题。

原因拆解:

  1. Spring Boot 3.x及以上版本中,RestClient默认使用JdkClientHttpRequestFactory,而JDK HttpClient默认启用HTTP/2协议。
  2. WireMock在默认配置下,对HTTP/2的支持存在局限性——虽然WireMock 2.35+开始支持HTTP/2,但在测试场景下,可能存在服务器提前关闭连接、或HTTP/2流处理不匹配的情况,导致JDK HttpClient在读取响应时遇到EOF。
  3. SimpleClientHttpRequestFactory基于传统的JDK HttpURLConnection,默认使用HTTP/1.1,不会触发HTTP/2相关的连接问题,因此测试可以正常运行。

结论:

这并非RestClient或JdkClientHttpRequestFactory的直接bug,而是两者(JDK HTTP/2客户端与WireMock)在HTTP/2交互时的兼容性问题。不过也存在Spring框架对JDK HttpClient的适配细节优化空间,以及WireMock在HTTP/2测试场景下的完善需求。

可行解决方案:

  • 方案1:强制使用HTTP/1.1:给JdkClientHttpRequestFactory配置禁用HTTP/2的HttpClient,既保留JDK HttpClient的特性,又避免兼容性问题:
    @Bean
    public RestClient restClient() {
        HttpClient httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1) // 强制使用HTTP/1.1
            .build();
        return RestClient.builder()
            .requestFactory(new JdkClientHttpRequestFactory(httpClient))
            .baseUrl(properties.baseUrl())
            .build();
    }
    
  • 方案2:切换至SimpleClientHttpRequestFactory:即你当前使用的方案,适合对HTTP客户端特性无特殊要求的场景。
  • 方案3:升级WireMock版本:尝试使用最新版WireMock,其对HTTP/2的支持可能已修复相关连接关闭的问题。

内容的提问来源于stack exchange,提问作者Wim Deblauwe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:05:04