记一个SpringBoot的坑: IllegalStateException: Only one connection receive subscriber allowed

下面是错误信息

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;
                }
            };
        }

参考1
参考2

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值