Spring Gateway使用RestTemplate/Feign遇阻塞异常的解决求助
Spring Gateway中Feign触发阻塞操作异常的解决方案
问题原因
Spring Gateway 2021.0.8基于Reactor响应式框架构建,reactor-http-nio-3线程属于非阻塞事件循环线程,禁止执行block()/blockFirst()/blockLast()这类阻塞操作。传统Feign是同步实现,内部依赖阻塞式HTTP客户端,调用时会触发上述异常;而WebClient是响应式客户端,天然适配非阻塞线程模型,因此可以正常运行。
解决方案
1. 切换到Reactive Feign(推荐)
使用基于WebClient实现的Reactive Feign,完全遵循响应式编程模型,避免阻塞操作,同时支持Eureka服务发现。
- 引入依赖:根据Spring Gateway对应Spring Boot 2.6.x版本,添加Reactive Feign的Spring Cloud starter:
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-reactive-spring-cloud-starter</artifactId> <version>3.2.2</version> <!-- 适配Spring Boot 2.6.x的版本 --> </dependency> - 定义Reactive Feign接口:使用
@ReactiveFeignClient注解,方法返回Mono/Flux类型:@ReactiveFeignClient(name = "target-service") // target-service为Eureka中的服务名 public interface TargetServiceClient { @PostMapping("/api/your-endpoint") Mono<ResponseEntity<YourResponse>> sendPostRequest(@RequestBody YourRequest requestBody); } - 在Gateway中使用:在Filter或自定义路由逻辑中注入接口,直接调用方法并保持响应式链式调用,绝对不要调用
block():@Component public class CustomFilter implements GatewayFilter { private final TargetServiceClient client; public CustomFilter(TargetServiceClient client) { this.client = client; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { YourRequest request = buildRequestFromExchange(exchange); return client.sendPostRequest(request) .flatMap(response -> { // 处理响应并写入exchange exchange.getResponse().setStatusCode(response.getStatusCode()); return exchange.getResponse().writeWith(Mono.just(exchange.getResponse() .bufferFactory().wrap(response.getBody().getBytes()))); }); } }
2. 传统Feign适配(临时过渡方案)
如果必须使用传统Feign,需将Feign调用转移到独立的阻塞线程池,避免占用reactor非阻塞线程:
- 引入异步HTTP客户端:比如OkHttp,替换Feign默认的URLConnection客户端:
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>11.8</version> </dependency> - 开启OkHttp并配置线程池:在
application.yml中配置:feign: okhttp: enabled: true client: config: default: connectTimeout: 5000 readTimeout: 5000 - 切换线程执行Feign调用:使用Reactor的
Schedulers.boundedElastic()将Feign调用转移到阻塞线程池:
注意:该方案会引入线程切换开销,破坏响应式非阻塞特性,仅适合临时过渡。@Component public class CustomFilter implements GatewayFilter { private final TraditionalFeignClient feignClient; public CustomFilter(TraditionalFeignClient feignClient) { this.feignClient = feignClient; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { YourRequest request = buildRequestFromExchange(exchange); // 从Callable包装Feign调用,切换到boundedElastic线程池 return Mono.fromCallable(() -> feignClient.sendPostRequest(request)) .subscribeOn(Schedulers.boundedElastic()) .flatMap(response -> { // 处理响应逻辑 exchange.getResponse().setStatusCode(HttpStatus.OK); return exchange.getResponse().setComplete(); }); } }
3. 核心原则:保持响应式链路
无论使用哪种方案,绝对不要在reactor-http-nio-3线程中调用任何阻塞方法,所有业务逻辑、依赖调用都要保持Mono/Flux的链式调用,让Reactor框架负责线程调度。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

