所有的请求统一发送至 网关(SpringCloudGateway) 由网关处理完数据后通过nacos分发到子服务

普通的接口请求没有什么大毛病,但是上传大文件时候,上传服务会报EOF错误(如下图)

上传服务的错误:

网关的报错:

  • 原因1: 大家都知道springcloud的gateway并不是使用mvc那套servlet处理的,而是使用webflux处理请求, webflux属于响应式, 请求分发完成后就会等待callback回调, 自己分析可能是这个原因(后面分析并不是)
  • 原因2: 在 org.springframework.web.reactive.socket.adapter.ReactorNettyWebSocketSession 类中, 构造方法指定了单包最大传输数据量是65536(64kb), 数据来源是继承至父类的一个常量

不知道大家有没有主意到NettyWebSocketSessionSupport还有一个构造方法可以指定数据量的大小, 但是这个构造方法我没有找到在哪里可以调用(后面甚至想到了复写这个类, 但是调用太复杂, 复写后还需要覆盖很多关联类, 导致项目启动失败, 放弃这个想法了)

另外除了这个类, 还有一个netty的websocket握手工厂类指定了单包传输量大小io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory

和NettyWebSocketSessionSupport不同的是,这个参数是可以通过yml文件指定的,配置方法如下:

spring:
 cloud:
  gateway:
   httpclient:
     websocket:
       max-frame-payload-length: 1024*1024*10

该参数会通过org.springframework.cloud.gateway.config.HttpClientProperties这个类中的Websocket.setMaxFramePayloadLength方法初始化

  • 原因3: 由于webflux存在大的响应包会拆分返回, 因此猜测请求包是不是也会进行拆分发送 , 导致后台上传服务未正确读到文件结束标志? 因此在gateway增加一个filter处理请求,将请求流读到一个byte[]中, 再重新构建一个reques进行发送
ServerRequest serverRequest = new DefaultServerRequest(exchange, messageReaders);
        DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
        Mono<String> rawBody = serverRequest.bodyToMono(String.class).map(s -> s);
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(rawBody, String.class);
        HttpHeaders tempHeaders = new HttpHeaders();
        tempHeaders.putAll(exchange.getRequest().getHeaders());
        tempHeaders.remove(HttpHeaders.CONTENT_LENGTH);
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, tempHeaders);
        AtomicReference<ResponseResult> flag = new AtomicReference<>(ResponseResult.ok());
        //读取请求流
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
            Flux<DataBuffer> body = outputMessage.getBody();
            DataBufferHolder holder = new DataBufferHolder();
            body.subscribe(dataBuffer -> {
                try {
                    int len = dataBuffer.readableByteCount();
                    holder.length = len;
                    byte[] bytes = new byte[len];
                    dataBuffer.read(bytes);
                    DataBufferUtils.release(dataBuffer);
                    DataBuffer data = bufferFactory.allocateBuffer();
                    data.write(bytes);
                    holder.dataBuffer = data;
                }catch (BusinessException e){
                    flag.set(new ResponseResult(e.getCode(),e.getErrorMessage()));
            ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public HttpHeaders getHeaders() {
                    long contentLength = tempHeaders.getContentLength();
                    HttpHeaders httpHeaders = new HttpHeaders();
                    httpHeaders.putAll(super.getHeaders());
                    if (contentLength > 0) {
                        httpHeaders.setContentLength(contentLength);
                    } else {
                        httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                    return httpHeaders;
                @Override
                public Flux<DataBuffer> getBody() {
                    return Flux.just(holder.dataBuffer);
            if(flag.get().getCode().equals(ResponseResult.CodeStatus.OK)){
                return chain.filter(exchange.mutate().request(requestDecorator).build());
            }else{
                return gatewayResponse(flag.get(),exchange);

代码更正(上面的代码会导致byte数据长度翻倍, 设置的content-length和数据源相差巨大, 导致上传的文件内容失效,上传后无法访问)

return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    DataBufferUtils.release(dataBuffer);
                    Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                        DataBufferUtils.retain(buffer);
                        return Mono.just(buffer);
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                    ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
                    return ServerRequest.create(mutatedExchange, MESSAGE_READERS)
                            .bodyToMono(byte[].class)
                            .then(chain.filter(mutatedExchange));

重启后解决, 至于到底是哪个导致的上传失败, 只怪自己学艺不精, 害 有时间看看源码才知道

前端:vue2,vue3,vue-cli,webuploader,html5 后端:SpringBoot 数据库:MySQL,Oracle,SQL Server,达梦,人大金仓,国产化数据库 协议:HTTP WebServer:Tomcat,Resin 服务器:Linux,国产化系统 功能:大文件上传,断点续传,秒传,加密传输,加密存储,文件夹上传,文件夹层级结构 技术:支持第三方软件集成, 该说不说,最近这块好像挻火的,今天早上又有网友加我微信,也是想了解一下这块的技术和方案,也不知道这位老哥是从哪里找到
Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 我们使用前面《SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(中)》的两个微服务示例,分别是库存微服务和订单微服务,基于Nacos注册中心和配置中心的使用,前
服务注册/发现&注册中心: A服务调用B服务,A服务并不知道B服务当前在哪几台服务器有,哪些是正常的,哪些服务已经下线。解决这个问题可以引入注册中心。配置中心用来几种管理微服务的配置信息。 服务熔断&服务降级: 在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。 例如:订单服务–>商品服务–>库存服务; 库存服务出现
GATEWAY网关上传文件问题 ​ gateway网关上传文件,MultipartFile在接口中无法解析到,所以需要把要上传的文件进行Base64编码,通过json格式传给后台。 解析前端传的Base64数据 1、自定义File类 继承MultipartFile类 import org.springframework.web.multipart.MultipartFile; import java.io.*; * base64 转 MultipartFile * @author PC
作者:陈镇坤27 日期:2022年3月15日 摘要:使用Apache的上传组件,部署服务后,前端上传文件,服务端与客户端建立socket连接,迟迟未接收到requestURI line,超过配置的时间时,会关闭tomcat线程connection,此时Apache组件会报Unexpected EOF错误。 思路历程:百度、谷歌异常解决方案,求证tomcat的connectionTimeOut的含义,查询连接与请求差异, 关键词:tomcat、ht 我看了下我的host文件,在安装了k8s后自动增加了一条对kubernetes.docker.internal的地址解析。 cat /etc/hosts # Added by Docker Desktop # To allow the same kube context to work on the host and the contai
在 Python 中,处理文件读取的 EOF(End of File)异常是一个常见的做法,因为 Python 提供了一种更优雅的方式来捕获并处理这类错误。你可以使用 `try-except` 结构结合内置的 `open()` 和 `readline()` 或 `readlines()` 方法来做到这一点。例如: ```python with open('file.txt', 'r') as f: while True: line = f.readline() if not line: # 如果读到空字符串,说明到了文件尾 break # 进行处理,如打印或分析每行 print(line) except UnicodeDecodeError: pass # 可能会遇到编码问题,忽略并继续读下一行 except FileNotFoundError: print("文件未找到") 在这个例子中,`try` 内部尝试读取每一行,如果文件结束(`readline()` 返回 `''`),则跳出循环。如果在读取过程中遇到 `UnicodeDecodeError`(例如处理非 UTF-8 编码文件),可以选择捕获并忽略这个异常。
泽济天下: return ServerRequest.create(mutatedExchange, MESSAGE_READERS) .bodyToMono(byte[].class) .then(chain.filter(mutatedExchange)); 这段代码中MESSAGE_READERS是什么 使用springboot框架写项目打包jar发布后读取项目下静态文件异常 yinyiYi159: 不知道楼主有没有解决,我也遇到了 使用springboot框架写项目打包jar发布后读取项目下静态文件异常 asdjiushiwo822: 问题4:使用easyui comboTree加载菜单时,后台字段和规定字段不一致无法加载问题 星空下飞舞: 谢谢分享,刚好需要呢! 问题2:shiro配置redis管理session后,每次重新请求重新生成session问题 我这好像没有用 请问是怎么一回事