• springcloud gateway nullpointerexception (NettyRoutingFilter)


    最近在做一个下载功能时,发现直接调用服务是可以下载的,但是通过gateway路由下载会报NPE异常,具体如下

    java.lang.NullPointerException: null
            at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011) ~[na:1.8.0_111]
            at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006) ~[na:1.8.0_111]
            at org.springframework.cloud.gateway.filter.NettyRoutingFilter.lambda$filter$3(NettyRoutingFilter.java:117) ~[spring-cloud-gateway-core-2.0.0.RELEASE.jar!/:2.0.0.RELEASE]
            at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:177) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
            at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:108) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
            at reactor.core.publisher.FluxRetryPredicate$RetryPredicateSubscriber.onNext(FluxRetryPredicate.java:81) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
            at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:146) ~[reactor-core-3.1.8.RELEASE.jar!/:3.1.8.RELEASE]
            at reactor.ipc.netty.channel.PooledClientContextHandler.fireContextActive(PooledClientContextHandler.java:85) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
            at reactor.ipc.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:578) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
            at reactor.ipc.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:136) ~[reactor-netty-0.7.8.RELEASE.jar!/:0.7.8.RELEASE]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:438) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310) ~[netty-codec-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:284) ~[netty-codec-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:253) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965) ~[netty-transport-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:808) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:408) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:308) ~[netty-transport-native-epoll-4.1.25.Final.jar!/:4.1.25.Final]
            at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) ~[netty-common-4.1.25.Final.jar!/:4.1.25.Final]
            at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_111]

    分析异常 发现其中和spring gateway有关的是这句

     at org.springframework.cloud.gateway.filter.NettyRoutingFilter.lambda$filter$3(NettyRoutingFilter.java:117) ~[spring-cloud-gateway-core-2.0.0.RELEASE.jar!/:2.0.0.RELEASE]

    定位到相关的代码片段查看 org.springframework.cloud.gateway.filter.NettyRoutingFilter

    return this.httpClient.request(method, url, req -> {
                final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
                        .headers(httpHeaders)
                        .chunkedTransfer(chunkedTransfer)
                        .failOnServerError(false)
                        .failOnClientError(false);
    
                if (preserveHost) {
                    String host = request.getHeaders().getFirst(HttpHeaders.HOST);
                    proxyRequest.header(HttpHeaders.HOST, host);
                }
    
                return proxyRequest.sendHeaders() //I shouldn't need this
                        .send(request.getBody().map(dataBuffer ->
                                ((NettyDataBuffer)dataBuffer).getNativeBuffer()));
            }).doOnNext(res -> {
                ServerHttpResponse response = exchange.getResponse();
                // put headers and status so filters can modify the response
                HttpHeaders headers = new HttpHeaders();
    
                res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
    
                exchange.getAttributes().put("original_response_content_type", headers.getContentType());
    
                HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
                        this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
                
                response.getHeaders().putAll(filteredResponseHeaders);
                HttpStatus status = HttpStatus.resolve(res.status().code());
                if (status != null) {
                    response.setStatusCode(status);
                } else if (response instanceof AbstractServerHttpResponse) {
                    // https://jira.spring.io/browse/SPR-16748
                    ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code());
                } else {
                    throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass());
                }
    
                // Defer committing the response until all route filters have run
                // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter
                exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
            }).then(chain.filter(exchange));

    出问题的为这句

    exchange.getAttributes().put("original_response_content_type", headers.getContentType());

    因为ConcurrentHashMap不允许出现null值,可见此时header中的ContentType为空导致。像下载文件,或者在controller中进行跳转时,都会出现这种情况。

    这个bug在spring cloud gateway的github上作者已经说明的解决方案:将springcloud gateway升级至2.0.1或以上的版本即可,我之前使用的是2.0.0,对应

    springcloud的版本 为 Finchley.RELEASE

    我升级到了2.0.2 对应springcloud的版本为 Finchley.SR2   对应部分的代码

        return responseMono.doOnNext(res -> {
                ServerHttpResponse response = exchange.getResponse();
                // put headers and status so filters can modify the response
                HttpHeaders headers = new HttpHeaders();
    
                res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));
    
                String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
                if (StringUtils.hasLength(contentTypeValue)) {
                    exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
                }
    
                HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(
                        this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
                
                response.getHeaders().putAll(filteredResponseHeaders);
                HttpStatus status = HttpStatus.resolve(res.status().code());
                if (status != null) {
                    response.setStatusCode(status);
                } else if (response instanceof AbstractServerHttpResponse) {
                    // https://jira.spring.io/browse/SPR-16748
                    ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code());
                } else {
                    throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass());
                }
    
                // Defer committing the response until all route filters have run
                // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter
                exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);
            })
                    .onErrorMap(t -> properties.getResponseTimeout() != null && t instanceof ReadTimeoutException,
                            t -> new TimeoutException("Response took longer than timeout: " +
                                    properties.getResponseTimeout()))
                    .then(chain.filter(exchange));

    可以看到关键部分的代码 和之前的相比较 已经做了判空

          String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
                if (StringUtils.hasLength(contentTypeValue)) {
                    exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
                }
  • 相关阅读:
    Android深度探索--HAL与驱动开发----第十章读书笔记
    Android深度探索--HAL与驱动开发----第九章读书笔记
    Android深度探索--HAL与驱动开发----第八章读书笔记
    Android深度探索--HAL与驱动开发----第七章读书笔记
    Android深度探索--HAL与驱动开发----第六章读书笔记
    Android深度探索--HAL与驱动开发----第五章读书笔记
    Android深度探索--HAL与驱动开发----第四章读书笔记
    Android深度探索--HAL与驱动开发----第三章读书笔记
    Android深度探索--HAL与驱动开发----第二章读书笔记
    Android深度探索--HAL与驱动开发----第一章读书笔记
  • 原文地址:https://www.cnblogs.com/hetutu-5238/p/10920826.html
Copyright © 2020-2023  润新知