所有的请求统一发送至 网关(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 编码文件),可以选择捕获并忽略这个异常。
泽济天下:
使用springboot框架写项目打包jar发布后读取项目下静态文件异常
yinyiYi159:
使用springboot框架写项目打包jar发布后读取项目下静态文件异常
asdjiushiwo822:
问题4:使用easyui comboTree加载菜单时,后台字段和规定字段不一致无法加载问题
星空下飞舞:
问题2:shiro配置redis管理session后,每次重新请求重新生成session问题