在上一篇时,我们在使用gateway的反向代理功能时,发现了一个很严重的问题,那就是通过gateway去访问后端服务时,如果发起的是Get请求,就一切正常,如果是Post请求,就会报错。无论是使用什么filter。
java.lang.IllegalStateException: Only one connection receive subscriber allowed.
at reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
at reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
at reactor.ipc.netty.channel.FluxReceive$$Lambda$714/21949105.run(Unknown Source) [reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) [netty-common-4.1.29.Final.jar:4.1.29.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) [netty-common-4.1.29.Final.jar:4.1.29.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:446) [netty-transport-4.1.29.Final.jar:4.1.29.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) [netty-common-4.1.29.Final.jar:4.1.29.Final]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]
如果使用的是Springboot2.0.5之前的版本,不存在该问题,之后的但凡是非Get请求,就会报该错误。
该异常意思是请求体只能被消费一次,也就是说这个请求的body已经被读取过一次了,再次封装转发时会报错。这个问题比较怪异,很早之前我们知道@RequestBody接收的参数,是不能被读取第二次的,假如被网关的日志读取消费过了,那么后续的服务就无法再接收到该参数了。要想保持参数还在,就只能自己再去构造一个同样的RequestBody,发给后端的服务。但是,这次是普通的Post的form表单,居然也报这个错,就比较奇怪了。
gateway反向代理的原理是,首先读取原请求的数据,然后构造一个新的请求,将原请求的数据封装到新的请求中,然后再转发出去。
该错误是Springboot的升级导致的,具体的详细解析在https://github.com/spring-cloud/spring-cloud-gateway/issues/541
最终作者也给了解决方案
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new HiddenHttpMethodFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange);
}
};
}
加上上面这一段就OK了。
实测,确实OK。