下面是错误信息
java.lang.IllegalStateException: Only one connection receive subscriber allowed. at
reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) at
reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) at
io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163) at
io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) at
io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) at
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446) at
io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) at
java.lang.Thread.run(Thread.java:745)
下面是项目的所用的SpringBoot与SpringCloud的版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
<relativePath/>
</parent>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
从下午三点钟到晚上七点, 不断用搜索引擎找, 不断的尝试方法, 最后参考了两位大神, 终于找到了合适的解决方法.
Spring Cloud Gateway中的过滤器不能获取Reques, 随之报出IllegalStateException: Only one connection receive subscriber allowed异常
注意: 这种方法在spring-boot-starter-parent 2.0.6.RELEASE + Spring Cloud Finchley.SR2 body 中生效,
在spring-boot-starter-parent 2.1.0.RELEASE + Spring Cloud Greenwich.M3 body 中不生效,总是为空
@EnableAutoConfiguration
@Configuration
public class ApiLocator {
@Autowired
private RequestFilter requestFilter;
private static final String SERVICE = "/path/**";
private static final String URI = "http://127.0.0.1:3002";
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
/*
route1 是get请求,get请求使用readBody会报错
route2 是post请求,Content-Type是application/x-www-form-urlencoded,readbody为String.class
route3 是post请求,Content-Type是application/json,readbody为Object.class
*/
RouteLocatorBuilder.Builder routes = builder.routes();
RouteLocatorBuilder.Builder serviceProvider = routes
.route("route1",
r -> r
.method(HttpMethod.GET)
.and()
.path(SERVICE)
.filters(f -> {
f.filter(requestFilter);
return f;
})
.uri(URI))
.route("route2",
r -> r
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.and()
.method(HttpMethod.POST)
.and()
.readBody(String.class, readBody -> {
// log.info("request method POST, Content-Type is application/x-www-form-urlencoded, body is:{}", readBody);
return true;
})
.and()
.path(SERVICE)
.filters(f -> {
f.filter(requestFilter);
return f;
})
.uri(URI))
.route("route3",
r -> r
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.and()
.method(HttpMethod.POST)
.and()
.readBody(Object.class, readBody -> {
// log.info("request method POST, Content-Type is application/json, body is:{}", readBody);
return true;
})
.and()
.path(SERVICE)
.filters(f -> {
f.filter(requestFilter);
return f;
})
.uri(URI));
RouteLocator routeLocator = serviceProvider.build();
// log.info("custom RouteLocator is loading ... {}", routeLocator);
return routeLocator;
}
}
@Component
public class RequestFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//String bodyFromRequest = resolveBodyFromRequest(exchange.getRequest());
//System.out.println("body is: " + bodyFromRequest);
Object requestBody = exchange.getAttribute("cachedRequestBodyObject");
// log.info("request body is:{}", requestBody);
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("RequestFilter post filter");
}));
}
@Override
public int getOrder() {
return -5;
}
/**
* 这种方法在spring-boot-starter-parent 2.0.6.RELEASE + Spring Cloud Finchley.SR2 body 中生效,
* 但是在spring-boot-starter-parent 2.1.0.RELEASE + Spring Cloud Greenwich.M3 body 中不生效,总是为空
*
* @param serverHttpRequest
* @return
*/
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
return bodyRef.get();
}
}
最后在我们出现问题的过滤器添加以下代码
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/** 获取预言类的缓存 */
Object requestBody = exchange.getAttribute("cachedRequestBodyObject");
ServerHttpRequest serverHttpRequest = exchange.getRequest();
/** 将缓存重新设置的Request里 */
URI uri = exchange.getRequest().getURI();
ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();
DataBuffer dataBuffer = stringBuffer(requestBody == null ? "" : requestBody.toString());
Flux<DataBuffer> bodyFlux = Flux.just(dataBuffer);
request = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
}