Spring Cloud Gateway 緩存區(qū)異常問題及解決方案
最近在測試環(huán)境spring cloud gateway突然出現(xiàn)了異常,在這里記錄一下,直接上干貨
1、問題背景
測試環(huán)境spring cloud gateway遇到以下異常
DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144(超出了緩沖區(qū)的最大字節(jié)數限制)

乍一看,問題很簡單啊,通過配置加大緩存區(qū)不就行了啊,于是就在application.yml加了以下配置
#將緩存區(qū)設置為2m
spring:
codec:
max-in-memory-size: 2MB可是問題又出現(xiàn)了,通過調試發(fā)現(xiàn)配置的max-in-memory-size在程序啟動初始化確實是生效的。但是有業(yè)務調用的時候,此參數的接收值為null,maxInMemorySize還是讀取的默認值(256K)。
那咋整,只能從源碼入手了。
2、分析源碼過程
通過異常日志,可以定位到異常位置



后來發(fā)現(xiàn)我們自定義的攔截器獲取body的信息是獲取方式,代碼如下

因為HandlerStrategies.withDefaults() 是每次都需要重新創(chuàng)建對象,并非是spring注入的對象,所以每次獲取的都是默認值,導致配置不生效。
3、解決辦法
在我們自定的攔截器中注入ServerCodecConfigurer類,通過該類獲取配置。這樣獲取到的就是我們在application.yml中配置的緩存區(qū)配置的字節(jié)數限制了。

具體代碼:
@Component
@Slf4j
public class RequestFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return OrderedConstant.HIGHEST_PRECEDENCE;
}
//手動注入ServerCodecConfigurer
@Autowired
ServerCodecConfigurer codecConfigurer;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
long startTime = System.currentTimeMillis();
try {
final Log logDTO = new Log();
ServerHttpRequest request = exchange.getRequest();
// 設置X-Request-Id
AtomicReference<String> requestId = new AtomicReference<>(GenerateIdUtils.requestIdWithUUID());
Consumer<HttpHeaders> httpHeadersConsumer = httpHeaders -> {
String headerRequestId = request.getHeaders().getFirst(HeaderConstant.REQUEST_ID);
if (!Strings.isNullOrEmpty(headerRequestId)) {
requestId.set(headerRequestId);
}
logDTO.setRequestId(requestId.get());
httpHeaders.set(HeaderConstant.REQUEST_ID, requestId.get());
httpHeaders.set(HeaderConstant.START_TIME_KEY, String.valueOf(startTime));
};
// codecConfigurer.getReaders()獲取pplication.yml中配置的緩存區(qū)配置的字節(jié)數
ServerRequest serverRequest = ServerRequest.create(exchange,
codecConfigurer.getReaders());
URI requestUri = request.getURI();
String uriQuery = requestUri.getQuery();
String url = requestUri.getPath() + (!Strings.isNullOrEmpty(uriQuery) ? "?" + uriQuery : "");
HttpHeaders headers = request.getHeaders();
MediaType mediaType = headers.getContentType();
String method = request.getMethodValue().toUpperCase();
// 原始請求體
final AtomicReference<String> requestBody = new AtomicReference<>();
final AtomicBoolean newBody = new AtomicBoolean(false);
if (mediaType != null && LogHelper.isUploadFile(mediaType)) {
requestBody.set("上傳文件");
} else {
if (method.equals("GET")) {
if (!Strings.isNullOrEmpty(uriQuery)) {
requestBody.set(uriQuery);
}
} else {
newBody.set(true);
}
}
logDTO.setLevel(Log.LEVEL.INFO);
logDTO.setRequestUrl(url);
logDTO.setRequestBody(requestBody.get());
logDTO.setRequestMethod(method);
logDTO.setIp(IpUtils.getClientIp(request));
ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeadersConsumer).build();
ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
return build.getSession().flatMap(webSession -> {
logDTO.setSessionId(webSession.getId());
if (newBody.get() && headers.getContentLength() > 0) {
Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
return bodyToMono.flatMap(reqBody -> {
logDTO.setRequestBody(reqBody);
// 重寫原始請求
ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(reqBody.getBytes());
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate()
.request(requestDecorator)
.build()).then(LogHelper.doRecord(logDTO));
});
} else {
return chain.filter(exchange).then(LogHelper.doRecord(logDTO));
}
});
} catch (Exception e) {
log.error("請求日志打印出現(xiàn)異常", e);
return chain.filter(exchange);
}
}
}到此這篇關于Spring Cloud Gateway 緩存區(qū)異常的文章就介紹到這了,更多相關Spring Cloud Gateway 緩存區(qū)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot集成redis實現(xiàn)簡單秒殺系統(tǒng)
這篇文章主要為大家詳細介紹了springboot集成redis實現(xiàn)簡單秒殺系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12
二種jar包制作方法講解(dos打包jar eclipse打包jar文件)
這篇文章主要介紹了二種jar包制作方法講解:dos打包jar和eclipse打包jar文件,大家參考使用吧2013-11-11
Java SpringBoot整合Canal實現(xiàn)數據同步方式
本文介紹了如何開啟和配置Canal,以及如何在Spring Boot中集成Canal,Canal是一種基于MySQL的數據庫變更解析工具,可以將數據庫的變更事件發(fā)送到Kafka、RocketMQ等消息隊列中,用于數據分析和挖掘2025-02-02
spring-data-redis 動態(tài)切換數據源的方法
最近遇到了一個麻煩的需求,我們需要一個微服務應用同時訪問兩個不同的 Redis 集群,一般情況下我們會怎么處理呢,下面通過場景分析給大家介紹spring-data-redis 動態(tài)切換數據源的方法,感興趣的朋友一起看看吧2021-08-08
詳解IDEA中類加載器調用getResourceAsStream()方法需注意的問題
這篇文章主要介紹了詳解IDEA中類加載器調用getResourceAsStream()方法需注意的問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02

