用 Dubbo 传输文件,被老大一顿揍。。
相关阅读:
AI全套:Python3+TensorFlow打造人脸识别智能小程序
最新人工智能资料-Google工程师亲授 Tensorflow-入门到进阶
黑马头条项目 - Java Springboot2.0(视频、资料、代码和讲义)14天完整版
作者:空无
来源:juejin.cn/post/6963642641506369566
背景
这样可以避免因平台 SDK 出现不兼容的大版本更新,从而导致公司所有系统修改跟着升级的问题。
想法是好的,不过这种做法并不合适,因为 Dubbo 并不适合传输文件。好在这个系统在上线不久就没人用废弃了……
虽然系统废弃了,不过就这个 Dubbo 上传文件的主题还是可以详细分析下,聊聊它到底为什么不适合传文件。
void sendPhoto(File photo);
void sendPhoto(byte[] photo);
但这样就会导致 consumer 端需要一次性读取完整的文件内容至内存中,再大的内存也扛不住这样玩。而且 provider 端在接受数据解析报文时,也需要一次性将 byte[] 读取至内存中,也是一样有内存占用过高问题。
Dubbo 文档中也提到了单连接设计的原因:
因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。
不过多连接下,连接和请求并不是一一对应的,而是一个轮询的机制。如下图所示,当配置了N个连接时,对于每一个 Provider 实例都会维护多个连接,在执行请求时会通过轮询的机制,为每次请求分配不同的连接
而一般用 Feign 时,都是在 Spring Cloud 全家桶环境下,服务端往往是默认的 Tomcat。而 Tomcat 在读取文件报文(form-data)时,会先将报文暂存至磁盘,然后通过 FileItem 读取磁盘中的报文内容。所以在对于 Server 端来说,不会一次性将完整的报文数据读取至内存中,也就不会有内存占用过高的问题。搜索公众号互联网架构师后台回复“2T”,获取一份惊喜礼包。
interface SomeApi {
// File parameter
@RequestLine("POST /send_photo")
@Headers("Content-Type: multipart/form-data")
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") File photo);
// byte[] parameter
@RequestLine("POST /send_photo")
@Headers("Content-Type: multipart/form-data")
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") byte[] photo);
// FormData parameter
@RequestLine("POST /send_photo")
@Headers("Content-Type: multipart/form-data")
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") FormData photo);
// MultipartFile parameter
@RequestLine("POST /send_photo")
@Headers("Content-Type: multipart/form-data")
void sendPhoto(@RequestPart(value = "photo") MultipartFile photo);
// Group all parameters within a POJO
@RequestLine("POST /send_photo")
@Headers("Content-Type: multipart/form-data")
void sendPhoto (MyPojo pojo);
class MyPojo {
@FormProperty("is_public")
Boolean isPublic;
File photo;
}
}
所以无论怎么定义 FormEncoder,最后数据都会写入到这个 Output 的 ByteArrayOutputStream 中,仍然会将所有数据完整的读取至内存中,一样会有内存占用高的问题。
@RequiredArgsConstructor
@FieldDefaults(level = PRIVATE, makeFinal = true)
public class Output implements Closeable {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//所有的数据在“编码”之后,仍然会写入到 ByteArrayOutputStream 这个内存 OutputStream 中
public Output write (byte[] bytes) {
outputStream.write(bytes);
return this;
}
public Output write (byte[] bytes, int offset, int length) {
outputStream.write(bytes, offset, length);
return this;
}
public byte[] toByteArray () {
return outputStream.toByteArray();
}
}
但好在 Feign 只是个 HTTP Client,Server 端还是“增量”读取的,对于 Server 端来说不会有这个内存问题。
所以如果有文件上传的场景,尽可能的用客户端直传的方式吧,友好又节省资源!
全栈架构社区交流群
「全栈架构社区」建立了读者架构师交流群,大家可以添加小编微信进行加群。欢迎有想法、乐于分享的朋友们一起交流学习。
看完本文有收获?请转发分享给更多人
往期资源: