Spring Cloud Gateway 缓存区异常

目录

1、问题背景

2、分析源码过程

3、解决办法


最近在测试环境spring cloud gateway突然出现了异常,在这里记录一下,直接上干货

1、问题背景

测试环境spring cloud gateway遇到以下异常

DataBufferLimitException: Exceeded limit on max bytes to buffer : 262144(超出了缓冲区的最大字节数限制)

乍一看,问题很简单啊,通过配置加大缓存区不就行了啊,于是就在application.yml加了以下配置

#将缓存区设置为2m
spring:
  codec:
    max-in-memory-size: 2MB

可是问题又出现了,通过调试发现配置的max-in-memory-size在程序启动初始化确实是生效的。但是有业务调用的时候,此参数的接收值为null,maxInMemorySize还是读取的默认值(256K)。

那咋整,只能从源码入手了。

2、分析源码过程

通过异常日志,可以定位到异常位置

后来发现我们自定义的拦截器获取body的信息是获取方式,代码如下

因为HandlerStrategies.withDefaults() 是每次都需要重新创建对象,并非是spring注入的对象,所以每次获取的都是默认值,导致配置不生效。

3、解决办法

在我们自定的拦截器中注入ServerCodecConfigurer类,通过该类获取配置。这样获取到的就是我们在application.yml中配置的缓存区配置的字节数限制了。

具体代码:

@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中配置的缓存区配置的字节数
            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("请求日志打印出现异常", e);
            return chain.filter(exchange);
        }
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/292703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring 面试题学习笔记整理

Spring 面试题学习笔记整理 Spring的理解IOC读取 xml注入 配置过程解析注解注入过程 高频 &#xff1a;IOC 理解 及原理 底层实现IoC的底层实现高频&#xff1a;Bean的生命周期&#xff08;图解&#xff09;高频&#xff1a;Bean的生命周期&#xff08;文解&#xff09;扩展知识…

STM32和ESP8266的WiFi模块控制与数据传输

基于STM32和ESP8266 WiFi模块的控制与数据传输是一种常见的嵌入式系统应用。在这种应用中&#xff0c;STM32作为主控制器负责控制和与外部传感器交互&#xff0c;而ESP8266 WiFi模块则用于实现无线通信和数据传输。本文将介绍如何在STM32上控制ESP8266模块&#xff0c;建立WiFi…

【React系列】React生命周期、setState深入理解、 shouldComponentUpdate和PureComponent性能优化、脚手架

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 生命周期 1.1. 认识生命周期 很多的事物都有从创建到销毁的整个过程&#xff0c;这个过程称之为是生命周期&…

3D 纹理的综合指南

在线工具推荐&#xff1a;3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 我们经常看到超现实主义的视频游戏和动画电影角色出现在屏幕上。他们皮肤上的…

EasyRecovery2024永久免费版电脑数据恢复软件

EasyRecovery是一款操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序&#xff0c;它不会往源驱上写任何东西&#xff0c;也不会对源驱做任何改变。它支持从各种各样的存储介质恢复删除或者丢失的文件&#xff0c;其支持的媒体介质包括&#xff1a;硬盘驱动器、光驱、…

嵌入式(三)中断解析 | 中断基本概念 CC2530中断系统 中断编程全解析

文章目录 1中断的概念和作用1.1 概念1.2 作用1.3 中断 其他概念 2. CC2530的中断系统3 中断编程3.1 中断配置3.1.1 使能端口组的中断功能3.1.2 使能当前端口组有哪些端口引脚中断3.1.3 设置中断触发方式 3.2 中断处理函数编写3.2.1 基本编写格式3.2.2 识别触发外部中断的端口Po…

实验笔记之——bug:in /usr/local/lib/libfmt.a(format.cc.o) is referenced by DSO

最近在编译D-MAP的时候遇到下面的问题 在github issue好像也有类似的提问 compiling error with fmt Issue #4 hku-mars/D-Map GitHub 这应该是fmt配置没有连接上。为此寻找所有包含的fmt文件&#xff0c;在头文件处加入 #define FMT_HEADER_ONLY #include "fmt/for…

Java学习苦旅(十九)——详解Java的堆和优先级队列

本篇博客将详细讲解堆和优先级队列。 文章目录 堆概念向下调整 优先级队列概念内部原理入队列出队列返回队首元素java中的优先级队列常用操作 topK问题结尾 堆 概念 堆逻辑上是一棵完全二叉树。 堆物理上是保存在数组中。 满足任意结点的值都大于其子树中结点的值&#xff…

北京大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;教育漏洞报告平台(EDUSRC) 兑换价格&#xff1a;30金币 获取条件&#xff1a;北京大学任意中危或以上级别漏洞

【React系列】Portals、Fragment

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) Portals 某些情况下&#xff0c;我们希望渲染的内容独立于父组件&#xff0c;甚至是独立于当前挂载到的DOM元素中&am…

浅谈基于物联网的建筑物综合环境能耗监测管理系统

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;随着社会经济的快速发展&#xff0c;我国建筑能源消费总量逐年增加&#xff0c;占社会能源消费总量的近30%。国际发达国家建设部科技司的相关研究表明&#xff0c;随着城市化进程的加快和人民生活质量的提高&…

案例091:基于微信小程序的农场驿站平台的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

ubuntu桥接方式上网

vmvare:VMware Workstation 17 Pro ubuntu: Ubuntu 14.04.6 LTS window10 下面是我的电脑配置 下面是ubuntu虚拟机的配置 vi /etc/network/interfaces 下面的gateway就是window -ipconfig 截图里的默认网关 auto lo iface lo inet loopbackauto eth0 iface eth0 inet stat…

日常工作 经验总结

1,在使用vue2开发项目时,快捷有效的组件化component 若有参数传递时,可以通过这样传递 在component中: 2,上拉加载,下拉刷新 若是使用局部进行上拉加载 下拉刷新 且需要用到scroll-view时 那么需要切记scroll-view在内被mescroll-uni包裹。若场景有限 对于无数据显示…

优雅实现微信小程序动态tabBar,根据不同用户角色显示不同底部导航——更新版(支持自由组合总数超过5个tabBar菜单)

背景 在开发小程序过程中&#xff0c;有个需求是&#xff0c;小程序底部的tabBar需要根据不同用户角色显示不同底部导航。此时就需要用到自定义底部导航 custom-tab-bar。 上次发文是组合显示4个底部tabBar导航&#xff0c;很多小伙伴评论说组合超过5个怎么办。他们的需求总数…

C语言KR圣经笔记 5.6指针数组;指针的指针

5.6 指针数组&#xff1b;指针的指针 因为指针本身也是变量&#xff0c;所以它们也能像其他变量一样保存在数组里面。我们写个程序来说明&#xff0c;该程序将一些文本行按照字母顺序排列&#xff0c;算是 UNIX 程序 sort 的精简版本。 在第三章中&#xff0c;我们介绍了对一…

设计创新,流程优化:3D开发HOOPS在数字化工厂中的多面应用

随着科技的不断发展&#xff0c;数字化转型已经成为各行各业的共同趋势&#xff0c;而工业领域也不例外。在这一浩大的变革浪潮中&#xff0c;Tech Soft 3D的HOOPS正以其卓越的性能和多功能性&#xff0c;成为数字化工厂领域的关键推动力。 数字化工厂概述 数字化工厂是指通过…

ssl证书(https/wss)内网测试

前言 一般后端部署到外网&#xff0c;可以去申请免费的SSL 证书&#xff0c; 但在内网测试时&#xff0c;需要自己生成证书 本章主要讲述ssl证书生成 1:环境 生成证书 openssl &#xff08;windows or linux 都行) 2:生成证书 1>生成私钥 pkcs#1私钥 openssl genrsa -out…

uniCloud 云函数

相对于云函数&#xff0c;官方更推荐使用 云对象 新建云函数 编辑云函数 uniCloud-aliyun/cloudfunctions/hello_func/index.js use strict; exports.main async (event, context) > {let {name} eventreturn 你好&#xff0c;${name}! };云函数接收的参数从event中解构获…

Js的String的replace(和replaceAll(

EcmaJavascriptJs的String的 replace( 和 replaceAll( 方法 String.prototype.replaceString.prototype.replaceAll 相同点 都是String.prototype的函数都是用于字符串替换都是两个参数第一个参数都可以是正则或字符串第二参数都可以是字符串或者回调函数, 回调会传入一个参…