gateway网关层进行了参数的加解密操作,但是由于加解密比较复杂,造成参数过多,参数值也比较长,导致网关报错,查询日志发现报org.springframework.core.io.buffer.DataBufferLimitException:Exceeded limit on max bytes to buffer : 262114,查询相关资料说是网关拦截器读取requestBody时,因为requestBody太大(默认不能超过256kb),抛出的此异常。
SpringBoot版本:2.3.2.RELEASE
Spring core版本:5.2.8.RELEASE
查看报错位置的源码,发现里面有maxByteCount参数,通过debug方法发现此数值为262144。为默认的256kb。
public class LimitedDataBufferList extends ArrayList<DataBuffer> {
private final int maxByteCount;
private int byteCount;
public LimitedDataBufferList(int maxByteCount) {
this.maxByteCount = maxByteCount;
}
private void raiseLimitException() {
throw new DataBufferLimitException("Exceeded limit on max bytes to buffer : " + this.maxByteCount);
}
}
查找调用LimitedDataBufferList类构造方法传入maxByteCount的类,这里主要复制调用部分的源码。
public abstract class DataBufferUtils {
private static final Log logger = LogFactory.getLog(DataBufferUtils.class);
private static final Consumer<DataBuffer> RELEASE_CONSUMER = DataBufferUtils::release;
public DataBufferUtils() {
}
/**
* spring 5.1.2.RELEASE版本调用此方法,没有传入,默认为-1
**/
public static Mono<DataBuffer> join(Publisher<? extends DataBuffer> dataBuffers) {
return join(dataBuffers, -1);
}
/**
* spring 5.2.8.RELEASE版本调用此方法,这里传入了maxByteCount
**/
public static Mono<DataBuffer> join(Publisher<? extends DataBuffer> buffers, int maxByteCount) {
Assert.notNull(buffers, "'dataBuffers' must not be null");
return buffers instanceof Mono ? (Mono)buffers : Flux.from(buffers).collect(() -> {
//此处传入的在创建LimitedDataBufferList对象时传入了maxByteCount
return new LimitedDataBufferList(maxByteCount);
}, LimitedDataBufferList::add).filter((list) -> {
return !list.isEmpty();
}).map((list) -> {
return ((DataBuffer)list.get(0)).factory().join(list);
}).doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
private int maxInMemorySize = 262144;
protected AbstractDataBufferDecoder(MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
}
public void setMaxInMemorySize(int byteCount) {
this.maxInMemorySize = byteCount;
}
public int getMaxInMemorySize() {
return this.maxInMemorySize;
}
public Mono<T> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return DataBufferUtils.join(input, this.maxInMemorySize).map((buffer) -> {
return this.decodeDataBuffer(buffer, elementType, mimeType, hints);
});
}
}
通过上面的源码,可以发现AbstractDataBufferDecoder类中maxInMemorySize = 262144为默认值,但是有set方法,所以应该可以通过set方法来设置新值。
论坛上找了一些方法,但是自己测试发现都不生效,可能是我的Spring版本问题。遇到该问题的小伙伴可以先参考下面链接:
自定义WebFluxConfiguration配置类,配置默认maxInMemorySize为16Mb。
@Configuration
public class WebFluxConfiguration implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024);
}
}
在新建的GatewayFilterFactory构造器中将ServerCodecConfigurer作为参数传递进去,并获取messageReaders,祥见示例代码。
@Component
@Slf4j
public class MyModifyRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyModifyRequestBodyGatewayFilterFactory.Config> {
//此处需要定义List<HttpMessageReader<?>>
private final List<HttpMessageReader<?>> messageReaders;
//构造方法
public MyModifyRequestBodyGatewayFilterFactory(ServerCodecConfigurer serverCodecConfigurer) {
super(MyModifyRequestBodyGatewayFilterFactory.Config.class);
//将messageReaders通过传进来的serverCodecConfigurer赋值。
this.messageReaders = serverCodecConfigurer.getReaders();
}
@Override
public GatewayFilter apply(Config config) {
return new MyModifyRequestBodyGatewayFilter(config);
}
public class MyModifyRequestBodyGatewayFilter implements GatewayFilter, Ordered {
private final Config config;
public MyModifyRequestBodyGatewayFilter(Config config){
this.config = config;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!config.isEnable()){
return chain.filter(exchange);
}
//此处创建ServerRequest一定要传入messageReaders,不能使用ServerRequest serverRequest = new DefaultServerRequest(exchange);创建
ServerRequest serverRequest = ServerRequest.create(exchange,messageReaders);
ServerHttpRequest serverHttpRequest = exchange.getRequest();
MediaType mediaType = serverHttpRequest.getHeaders().getContentType();
Mono<String> modifyBody = serverRequest.bodyToMono(String.class).flatMap(o->{
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)){
// TODO: 2020/12/25 o为获取到的requestBody,最大限制为前面配置的16mb,默认是256k。
return Mono.justOrEmpty(o);
}
return Mono.empty();
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifyBody,String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.set(HttpHeaders.CONTENT_TYPE,serverHttpRequest.getHeaders().getFirst("Content-Type"));
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange,headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
.then(Mono.defer(()->{
ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()){
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength>0){
httpHeaders.setContentLength(contentLength);
}else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING,"chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(requestDecorator).build());
}));
}
@Override
public int getOrder() {
return 0;
}
}
public static class Config{
private boolean enable;
public Config(){
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
}
}
希望可以帮助到遇到此问题的小伙伴,本文解决方法参考:https://github.com/spring-cloud/spring-cloud-gateway/issues/1658