DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144

现象

线上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

异常原因

  • 在使用ReadBodyPredicateFactory(org.springframework.cloud.gateway.handler.predicate)获取body后重新创建ServerRequest时,org.springframework.core.io.buffer.LimitedDataBufferList中判断接收数据大小超过制,org.springframework.core.codec.AbstractDataBufferDecoder中的默认262144。
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)));
...
}

 

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lizz666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值