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é)數(shù)限制)

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



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

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

具體代碼:
@Component
@Slf4j
public class RequestFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return OrderedConstant.HIGHEST_PRECEDENCE;
}
//手動(dòng)注入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();
// 設(shè)置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é)數(shù)
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);
}
}
}到此這篇關(guān)于Spring Cloud Gateway 緩存區(qū)異常的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway 緩存區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Cloud Gateway層限流實(shí)現(xiàn)過程
- Spring Cloud Gateway內(nèi)置的斷言和過濾器作用說明
- spring cloud gateway中redis一直打印重連日志問題及解決
- Spring?Cloud?Gateway?2.x跨域時(shí)出現(xiàn)重復(fù)Origin的BUG問題
- SpringCloud-Gateway網(wǎng)關(guān)的使用實(shí)例教程
- 使用SpringCloud Gateway解決跨域問題
- Spring Cloud Gateway組件的三種使用方式實(shí)例詳解
- 一文掌握spring cloud gateway(總結(jié)篇)
相關(guān)文章
springboot集成redis實(shí)現(xiàn)簡單秒殺系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了springboot集成redis實(shí)現(xiàn)簡單秒殺系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
二種jar包制作方法講解(dos打包jar eclipse打包jar文件)
這篇文章主要介紹了二種jar包制作方法講解:dos打包jar和eclipse打包jar文件,大家參考使用吧2013-11-11
Java SpringBoot整合Canal實(shí)現(xiàn)數(shù)據(jù)同步方式
本文介紹了如何開啟和配置Canal,以及如何在Spring Boot中集成Canal,Canal是一種基于MySQL的數(shù)據(jù)庫變更解析工具,可以將數(shù)據(jù)庫的變更事件發(fā)送到Kafka、RocketMQ等消息隊(duì)列中,用于數(shù)據(jù)分析和挖掘2025-02-02
spring-data-redis 動(dòng)態(tài)切換數(shù)據(jù)源的方法
最近遇到了一個(gè)麻煩的需求,我們需要一個(gè)微服務(wù)應(yīng)用同時(shí)訪問兩個(gè)不同的 Redis 集群,一般情況下我們會(huì)怎么處理呢,下面通過場景分析給大家介紹spring-data-redis 動(dòng)態(tài)切換數(shù)據(jù)源的方法,感興趣的朋友一起看看吧2021-08-08
詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題
這篇文章主要介紹了詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
加速spring/springboot應(yīng)用啟動(dòng)速度詳解
這篇文章主要介紹了加速spring/springboot應(yīng)用啟動(dòng)速度詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì)對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
Linux中Java開發(fā)常用軟件安裝方法總結(jié)
這篇文章主要介紹了Linux中Java開發(fā)常用軟件安裝方法總結(jié),需要的朋友可以參考下2020-02-02

