Spring WebFlux消费SSE触发io.netty ReadTimeoutException如何保持长连接
Spring WebFlux SSE客户端长连接超时断开解决方案
问题根因
你遇到的ReadTimeoutException是WebClient底层Netty组件的默认读超时配置导致的:默认规则为60秒未读取到任何网络数据时,主动断开连接,该规则不适用于推送时机随机的SSE长连接场景。
可行解决方案
方案1:自定义WebClient禁用读超时
直接修改WebClient的底层HttpClient配置,移除默认的读超时限制,适配SSE长连接场景,代码如下:
import io.netty.handler.timeout.ReadTimeoutHandler; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import reactor.netty.http.client.HttpClient; // 构建无读超时的HttpClient HttpClient httpClient = HttpClient.create() .doOnConnected(conn -> conn.removeHandler(ReadTimeoutHandler.class)); // 用自定义HttpClient构建WebClient实例 WebClient customWebClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build();
原有业务消费代码无需修改,替换为上述自定义的WebClient实例即可。
方案2:增加自动重试机制(推荐配合使用)
如果不想完全禁用读超时(避免真的出现连接假死无法感知),可以在消费逻辑中增加异常重试规则,触发超时后自动重连:
import org.springframework.web.reactive.function.client.WebClientRequestException; import io.netty.handler.timeout.ReadTimeoutException; import reactor.util.retry.Retry; import java.time.Duration; customWebClient.get() .uri("http://localhost:8080/events") .accept(MediaType.TEXT_EVENT_STREAM) .retrieve() .bodyToFlux(type) // 仅针对读超时异常进行无限次1秒间隔重试,可根据需求调整重试次数和间隔 .retryWhen(Retry.fixedDelay(Long.MAX_VALUE, Duration.ofSeconds(1)) .filter(e -> e instanceof WebClientRequestException && e.getCause() instanceof ReadTimeoutException)) .subscribe(event -> process(event));
方案3:服务端增加SSE心跳(协议层面最优)
如果有权限调整服务端逻辑,可让服务端每隔30秒发送一条SSE注释行(格式为: 心跳内容)作为心跳包,这类注释不会被客户端识别为业务事件,同时能保证有持续数据传输,无需修改客户端超时配置就能避免超时断开,稳定性最高。
注意事项
- 单独禁用读超时的场景下,如果服务端出现异常导致连接假死,客户端会长期持有无效连接,建议配合重试机制或服务端心跳使用。
内容的提问来源于stack exchange,提问作者Arun Kumar V
相关产品推荐
相关产品推荐

