线上spring cloud gateway时,偶尔出现几DataBufferLimitException异常,堆栈信息如下:
org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144
at org.springframework.core.io.buffer.LimitedDataBufferList.raiseLimitException(LimitedDataBufferList.java:101)
使用max-in-memory-size配置无效
spring:
codec:
max-in-memory-size: 1048576
package org.springframework.cloud.gateway.handler.predicate;
...
public class ReadBodyPredicateFactory
extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> {
...
/**
* 使用webFlux创建了默认配置的Reader
*/
private static final List<HttpMessageReader<?>> MESSAGE_READERS = HandlerStrategies
.withDefaults().messageReaders();
@Override
@SuppressWarnings("unchecked")
public AsyncPredicate<ServerWebExchange> applyAsync(Config config) {
return new AsyncPredicate<ServerWebExchange>() {
@Override
public Publisher<Boolean> apply(ServerWebExchange exchange) {
...
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
(serverHttpRequest) -> ServerRequest
.create(exchange.mutate().request(serverHttpRequest)
.build(), MESSAGE_READERS)
.bodyToMono(inClass)
.doOnNext(objectValue -> exchange.getAttributes().put(
CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
.map(objectValue -> config.getPredicate()
.test(objectValue)));
...
}
package org.springframework.core.io.buffer;
...
public class LimitedDataBufferList extends ArrayList<DataBuffer> {
private final int maxByteCount; //最大数据容量
private int byteCount; //当前接受的数据量
//设置maxByteCount
public LimitedDataBufferList(int maxByteCount) {
this.maxByteCount = maxByteCount;
}
...
private void updateCount(int bytesToAdd) {
if (this.maxByteCount >= 0) {
//接受数据超过2147483647个byte
if (bytesToAdd > 2147483647 - this.byteCount) {
this.raiseLimitException(); //异常
} else {
this.byteCount += bytesToAdd;
//接受数据超过maxByteCount个byte
if (this.byteCount > this.maxByteCount) {
this.raiseLimitException(); //异常
}
}
}
}
...
}
gateway-2.2.3以上版本修复了该bug,在GatewayAutoConfiguration中加入了配置写入,但只限ReadBodyPredicateFactory类,如自定义类型需要使用方案二。
package org.springframework.cloud.gateway.handler.predicate;
...
public class ReadBodyPredicateFactory
extends AbstractRoutePredicateFactory<ReadBodyPredicateFactory.Config> {
...
private final List<HttpMessageReader<?>> messageReaders;
public ReadBodyPredicateFactory() {
super(Config.class);
this.messageReaders = HandlerStrategies.withDefaults().messageReaders();
}
/**
* GatewayAutoConfiguration初始化配置写入相关配置
*/
public ReadBodyPredicateFactory(List<HttpMessageReader<?>> messageReaders) {
super(Config.class);
this.messageReaders = messageReaders;
}
...
}
重写ReadBodyPredicateFactory,注入ServerCodecConfigurer,使用ServerCodecConfigurer.getReaders()获取相关配置。
...
/**
* @description: 自定义ReadBodyPredicateFactory,copy之ReadBodyPredicateFactory
* @author: lizz
* @date: 2020/6/8 14:22
*/
@Component
public class GwReadBodyPredicateFactory extends AbstractRoutePredicateFactory<GwReadBodyPredicateFactory.Config> {
/**
* 获取Spring配置,解决最大body问题
*/
@Autowired
ServerCodecConfigurer codecConfigurer;
@Override
@SuppressWarnings("unchecked")
public AsyncPredicate<ServerWebExchange> applyAsync(GwReadBodyPredicateFactory.Config config) {
...
return new AsyncPredicate<ServerWebExchange>() {
@Override
public Publisher<Boolean> apply(ServerWebExchange exchange) {
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,
(serverHttpRequest) -> ServerRequest
.create(exchange.mutate().request(serverHttpRequest)
.build(), codecConfigurer.getReaders())
.bodyToMono(inClass)
.doOnNext(objectValue -> exchange.getAttributes().put(
CACHE_REQUEST_BODY_OBJECT_KEY, objectValue))
.map(objectValue -> config.getPredicate()
.test(objectValue)));
...
}