先贴问题:
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,也为大家遇到同类的问题,提供一种解决方式