请求头超长问题解决

336 阅读2分钟

先贴问题:

request url:xxx/saveAllSampleCabinet
        request ip:xx.xx.1.28
    request method:POST
   request headers:[Accept:"application/json",Content-Encoding:"UTF-8", Content-Type:"application/json; charset=UTF-8", User-Agent:"Java/1.8.0_281", Host:"xxx.xxx.xxx.xxx:xxx", Connection:"keep-alive", Content-Length:"456157"]
      request body:[]
      request time:2024-11-07 16:35:52
     exception msg: Exceeded limit on max bytes to buffer : 262144
      response msg: {"error"}

2024-11-07 16:35:52.916 ERROR 45901 --- [r-http-kqueue-2] reactor.core.publisher.Operators         : Operator called default onErrorDropped

io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
	at io.netty.util.internal.ReferenceCountUpdater.toLiveRealRefCnt(ReferenceCountUpdater.java:74)
	at io.netty.util.internal.ReferenceCountUpdater.release(ReferenceCountUpdater.java:138)
	at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:100)
	at io.netty.handler.codec.http.DefaultHttpContent.release(DefaultHttpContent.java:92)
	at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:88)
	at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:222)
	at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:351)
	at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:348)
	at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:484)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:90)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:214)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:321)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:295)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.kqueue.AbstractKQueueStreamChannel$KQueueStreamUnsafe.readReady(AbstractKQueueStreamChannel.java:544)
	at io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe.readReady(AbstractKQueueChannel.java:381)
	at io.netty.channel.kqueue.KQueueEventLoop.processReady(KQueueEventLoop.java:211)
	at io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:289)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:750)

框架: SpringCloud,用到了gateway

原因: 因为业务需要,请求数据1700行,请求头超过了默认限制

报错关键信息:

//实际长度
Content-Length:"456157"

//默认长度
Exceeded limit on max bytes to buffer : 262144

//netty报错 查询之后有的需求说是netty计数器的问题,我这个并不是
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1

查了一下,请求头默认大小256kb,我们长度明显是超长了

解决方式:

方式一

查询的时候有人提供的解决方式是增加codec配置

spring:
  codec:
    max-in-memory-size: 8MB

未生效,断点查看,在项目启动的时候会修改大小,但是请求的时候会重新调用默认值

还有两种配置netty大小的,也贴出来

server:
  tomcat:
    max-http-post-size: 10MB  # 设置为你需要的大小,例如 10MB
    
server:
  jetty:
    max-http-post-size: 10MB

以上方式都未生效

方式二

使用全局过滤器GlobalFilter,重新修改请求头长度

@Component
@Slf4j
public class RequestFilter implements GlobalFilter, Ordered {
    
    //获取codec配置
    @Autowired
    ServerCodecConfigurer codecConfigurer;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求
        ServerHttpRequest request = exchange.getRequest();
        log.info("requestFilter执行成功");
        try {
            //判断请求头长度 -- 这个地方可以优化一下,超过默认长度在执行
            if (request.getHeaders().getContentLength() > 0) {
                ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());
                //获取Mono对象
                Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
                //对每一个请求头内容做操作
                return bodyToMono.flatMap(reqBody -> {
                    //判断json,这是我们业务需要,不需要可以不加
                    if (isJson(reqBody)) {
                        String perms = JSONObject.parseObject(reqBody).getString("perms");
                        if (StringUtils.isNotEmpty(perms)) {
                            exchange.getAttributes().put("perms", perms);
                        }
                    }
                    
                    //关键 ServerHttpRequestDecorator
                    ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {

                        @Override
                        public Flux<DataBuffer> getBody() {
                            
                            NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
                            //重新设置请求头长度 
                            //此处可以写成配置项,配置codec长度,然后通过codecConfigurer获取,设置为固定长度
                            DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(reqBody.getBytes());
                            return Flux.just(bodyDataBuffer);
                        }
                    };
                    // 过滤器重新设置请求头限制
                    return chain.filter(exchange.mutate().request(requestDecorator).build());
                });
            }
            return chain.filter(exchange);
        } catch (Exception e) {
            log.error("Exception[RequestFilter]:", e);
            return chain.filter(exchange);
        }
    }

    @Override
    public int getOrder() {
        //过滤器执行先后顺序,多个过滤器,越小越先执行
        return -300;
    }
}

结果: 问题解决

请求头长度问题可以通过这个方式去解决。因为自己时间原因,没有仔细研究其他人提供的方式,具体的解决逻辑是什么

记录一下自己遇到的BUG,也为大家遇到同类的问题,提供一种解决方式