为什么都说Dubbo不适合传输大文件?Dubbo支持的协议

背景

之前公司有一个 Dubbo 服务,内部封装了腾讯云的对象存储服务 SDK,是为了统一管理这种三方服务的SDK,其他系统直接调用这个对象存储的 Dubbo 服务。用来避免因平台 SDK 出现不兼容的大版本更新,导致公司所有系统修改跟着升级的问题。

然而因为 Dubbo 并不适合传输大包,所以虽然想法不错,但这种做法还是并不合适,于是这个系统在上线不久就遭废弃没人用了……

不过系统虽然废弃了,但是我们可以顺着 Dubbo 上传文件的主题来详细分析下,说说看它究竟是因为什么不适合传文件。

Dubbo 怎么传文件?

难道直接这样传 File 吗?

void sendPhoto(File photo);

自然是不可以的!Dubbo 只是将对象进行序列化然后传输,可 File 对象就算序列化也无法处理文件的数据,所以只能直接发送文件内容:

void sendPhoto(byte[] photo);

但这样会导致 consumer 端需要一次性读取完整的文件内容至内存中,这样玩的话,再大的内存也都遭不住。

并且 provider 端在接受数据解析报文时, byte[] 也需要一次性读取至内存中,内存占用过高的问题同样存在。

单连接模型问题

除了内存占用问题之外,Dubbo(这里指 Dubbo 协议)的单连接模型也不适合文件传输。

Dubbo 协议默认是单连接的模型,也就是一个 provider 的所有请求都是用一个 TCP 连接。默认使用 Netty 来进行传输,而 Netty 中为了保证 Channel 线程安全,会将写入事件进行排队处理。那么在单连接下,多个请求都会使用同一个连接,也就是同一个 Channel 进行写入数据;当多个请求同时写入时,如果某个报文过大,会导致 Channel 一直在发送这个报文,其他请求的报文写入事件会进行排队,迟迟无法发送,连数据都没有发送过去,那么其他的 consumer 也自然会处于阻塞等待响应的状态中,一直无法返回了。

所以在单连接下,如果报文过大,将会导致 Netty 地写入事件处理阻塞,数据将无法及时发送至服务端,从而造成请求白白阻塞的问题。

那有的朋友可能会问,单连接模型缺点都这么大了, Dubbo 为什么还要采用单连接呢?

很简单,因为省资源,TCP 连接这样的资源可是很宝贵的,如果单连接可以满足绝大多数场景,那么也就完全没必要为每个请求准备一个连接。

在Dubbo 文档中也提到了单连接设计的原因:

因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压垮,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。

虽然 Dubbo 协议默认单连接模型,但还是可以设置多连接的:

<dubbo:service connections="1"/>
<dubbo:reference connections="1"/>

不过多连接下,连接和请求是一个轮询的机制,并不是一一对应的。

如下图,当配置了数个连接时,对于每一个 Provider 实例都会维护多个连接,在执行请求时会通过轮询的机制,为每次请求分配不同的连接

img

为什么 HTTP 协议“适合”传文件?

这么说其实不严谨,并不是 HTTP 协议适合传文件,Dubbo 还支持 HTTP 协议呢(虽然是半残品),一样不适合传文件。

Dubbo 这类 RPC 框架为了满足“调用本地方法像调用远程一样”,必须将数据序列化成语言里的对象,但这样一来就导致无法处理 File 这种形式的对象了。

如果跳出 Dubbo 这种 RPC 框架特性的限制,单独看 HTTP 协议的话,是很适合传输文件的。因为对于 Client 来说,只需要将报文发送至 Server,比如要传输的文件在本地的话,那我完全可以每次只读取文件的一个 Buffer 大小,然后将这个 Buffer 的数据使用 Socket 发送即可;在这种方式下,同时存在于内存中的数据,只会有一个 Buffer 大小,不会有 Dubbo 那样将全部数据读取至内存的问题。

如下图所示,Client 每次只从1GB 文件中读取 4K 大小的 Buffer 数据,然后用 Socket 发送,直至将文件完全读取并发送成功。那么这种方式下对于单次传输来说,内存始终都是只有 4K buffer 大小的占用,并不会像 Dubbo 那样一次性全部读取为 byte[] 再发送。

img

对于 Server 端也是一样,Server 端也并不用一次性将所有报文读取至内存中,在解析 Header 中的 Content-Length 后,直接包装一个 InputStream,在这个 InputStream 内部进行读取 Socket Buffer 的数据即可,一样不会有内存占用问题

那既然 HTTP 协议“适合”传输文件,Spring Cloud 的标配 RPC 客户端 - Feign 在传输文件上又会有什么问题呢?

Feign 适合传输文件吗

Feign 其实并不能算一套 RPC 框架,它只是一个 Http Client 而已。在使用 Feign 时,Server 可以是任意的 Http Server,比如实现 Servlet 的 Tomcat/Jetty/Undertow,或者是其他语言的 Apache Server 等等。

而一般都是在 Spring Cloud 全家桶环境下用 Feign,服务端往往是默认的 Tomcat。而 Tomcat 在读取文件报文(form-data)时,会先将报文暂存至磁盘,然后通过 FileItem 读取磁盘中的报文内容。所以在对于 Server 端来说,不会一次性将完整的报文数据读取至内存中,也就不会有内存占用过高的问题。

Feign 中上传文件有以下几种方式:

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;}
}

Feign 中将参数的编码/序列化抽象为一个 Encoder,对于 HTTP 协议的文件上传也提供了一个 feign-form 模块,该模块中提供了一些 FormEncoder。可无论哪种 FormEncoder 最后都是通过 Feign 封装的 Output 对象进行输出,不过这个 Output 对象却不是那种包装 Socket InputStream 作为中转发送,而是直接作为一个数据的载体,用一个 ByteArrayOutputStream 来存储编码完成的数据。

所以无论怎么定义 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 端来说不会有这个内存问题。

总结

其实 Dubbo 不光是不适合传输文件,大报文场景下都不太合适,Dubbo 的设计更适合小业务报文的传输(默认报文大小只有8MB)。

所以如果有文件上传的场景,尽可能地用客户端直传的方式吧,节省资源又友好!

Dubbo支持的协议

dubbo(默认):单一长连接和NIO异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步,Hessian 序列化。

http:基于 Http 表单提交的远程调用协议,使用Spring的HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用。

hessian:集成Hessian 服务,基于HTTP通讯,采用Servlet暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与Hession服务互操作。多个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。

memcache:基于memcached实现的RPC协议。传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。

rmi:采用JDK标准的rmi协议实现,传输参数和返回参数对象需要实现Serializable接口,使用java标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。多个短连接,TCP 协议传输,同步传输,适用常规的远程服务调用和 rmi 互操作。在依赖低版本的 Common-Collections包,java 序列化存在安全漏洞。

webservice:基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用。

redis:基于redis实现的RPC协议。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言编写劫持dll,c语言-----劫持自己02

在上一节 c语言-----劫持原理01 已经叙述了劫持原理&#xff0c;下边正式进入劫持实战1. 需要实现的功能在c语言中system("notepad") 可以打开一个记事本system("mspaint") 可以打开画图工具所以这次我们需要把 可以打开一个记事本 这个功能更改为 在控制…

Java中Runtime类

一、概述 Runtime类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例&#xff0c;使应用程序能够与其运行的环境相连接。 一般不能实例化一个Runtime对象&#xff0c;应用程序也不能创建自己的 Runtime 类实例&#xff0c;但可以通过 getRuntime 方法获取当前R…

在Java中调用Python

写在前面 在微服务架构大行其道的今天&#xff0c;对于将程序进行嵌套调用的做法其实并不可取&#xff0c;甚至显得有些愚蠢。当然&#xff0c;之所以要面对这个问题&#xff0c;或许是因为一些历史原因&#xff0c;或者仅仅是为了简单。恰好我在项目中就遇到了这个问题&#…

android 导航动画,安利一个Android导航库

SlidingRootNav这是一个像DrawerLayout一样的抽屉式的导航库&#xff0c;这个库实现的抽屉在content view的下层&#xff0c;滑动之后&#xff0c;才能看到相应的导航页使用Gradle添加依赖compile com.yarolegovich:sliding-root-nav:1.0.2使用说明创建一个 content_view.xml或…

android 界面组件,安卓开发学习周第三篇——Android中的UI组件

原标题&#xff1a;安卓开发学习周第三篇——Android中的UI组件在Android APP中&#xff0c;所有的用户界面元素都是由View和ViewGroup的对象构成的。View是绘制在屏幕上的用户能与之交互的一个对象。而ViewGroup则是一个用于存放其他View(和ViewGroup)对象的布局容器&#xff…

BigDecimal.ZERO替代new BigDecimal(0)

这里只是想分享下&#xff0c;平时如果我们想要定义一些BigDecimal类型的变量&#xff0c;可以先看看BigDecimal有没有已经先做了定义&#xff0c;如new BigDecimal(0)就可以用BigDecimal.ZERO来代替&#xff0c;如下&#xff1a; BigDecimal bigDecimal BigDecimal.ZERO;描述…

用js拼html写下拉框,js实现下拉框效果(select)

效果图&#xff1a;代码如下&#xff1a;*{padding: 0;margin:0;}ul,li{list-style: none}.left{float: left;}.right{float: right;}.select_contain{font-size: 14px;color: #333;line-height: 38px;margin: 30px 0;}.select_item{margin-right: 50px;position: relative;}.s…

Gson Builder — 基础 命名策略

文章目录Gson Builder — 基础& 命名策略GsonBuilder 基础命名策略字段命名策略 - IDENTITY字段命名策略 - LOWER_CASE_WITH_UNDERSCORES字段命名策略 - LOWER_CASE_WITH_DASHES字段命名策略 - UPPER_CAMEL_CASE字段命名策略 - UPPER_CAMEL_CASE_WITH_SPACESSerializedName…

keil html转换工具,网页转换工具FCARM和makefsfile的使用简介

网页转换工具FCARM和makefsfile的使用简介[复制链接]首先在这里要提一下Keil的网页转换工具FCARM.exe的使用&#xff0c;花费了我好几个小时。TI也有一个类似的程序&#xff0c;是Makefsfile&#xff0c;并且源码公开的。1.gif (31.45 KB, 下载次数: 0)2010-11-8 16:44 上传2.j…

Google Gson用法详解

文章目录一、简介二、依赖三、基本用法1、创建Gson实例1.1、new Gson()1.2、GsonBuilder.build()2、Java对象–>JSON3、JSON–>Java对象4、漂亮地输出5、JSON array --> Java array/list5.1 、 JSON array -->Java对象5.2 、JSON array–>List5.3 、JSON array–…

Socket通信原理

Socket通信原理 一、Socket的定义 1、Socket是一个网络通信的套接字&#xff08;接口&#xff09; 二、Socket的实现流程 1、Socket在客户端和服务端发生了什么&#xff1f; 服务器&#xff1a; a.socket()创建socket对象 b.bind()为socket对象绑定协议&#xff0c;赋予名…

CRC校验原理及步骤

1、CRC是用来干嘛的&#xff1f; 检测数据传输过程中是否出现错误&#xff08;某些位&#xff0c;或某几位&#xff0c;或者某块区域位错误&#xff09;。 2、CRC是否能校正数据传输中的错误&#xff1f; CRC只能检错&#xff0c;不能纠错。如果发现错误&#xff0c;可根据双…

在组策略中用户策略仅对特定计算机生效,将组策略应用到满足条件的计算机---配置组策略筛选...

配置组策略筛选Microsoft?Windows?Management Instrumentation (WMI) 大概是我们已知的 Microsoft 保存最好的秘密。尽管如此&#xff0c;但毫无疑问&#xff0c;WMI 是 Microsoft 主要的针对 Windows 的管理支持技术。在Windows Server 2008的组策略高级管理中&#xff0c;对…

Idea 类和包的路径后面出现100%methods;84% lines coveredIdea coverage覆盖率测试工具的使用

Idea 类和包的路径后面出现100%methods;84% lines covered 其他先不说&#xff0c;先看一下出现的问题&#xff1a; 类和包的路径后面多了100%methods;84% lines covered&#xff0c;以前类路径后面是什么都没的&#xff1b; 长话短说&#xff1b; 产生原因&#xff1a; 因…

SpringBoot:整合监听器/过滤器和拦截器

整合监听器/过滤器和拦截器 在实际开发过程中&#xff0c;经常会碰见一些比如系统启动初始化信息、统计在线人数、在线用户数、过滤敏/高词汇、访问权限控制(URL级别)等业务需求。实现以上的功能&#xff0c;都会或多或少的用到过滤器、监听器、拦截器。 一.SpringBoot整合过…

全国计算机英语四六级准考证打印准考证号,2017全国大学生英语四六级准考证打印...

2017年上半年度CET考试时间为6月17日&#xff0c;同学们打印准考证了吗?为方便大家打印四六级准考证&#xff0c;yjbys小编为大家分享英语四级考试准考证打印官方主页入口如下&#xff1a;2017年上半年度CET考试时间及报名方式一、考试科目及时间1.笔试考试时间日期(6月17日)考…

Centos7配置Jenkins

Centos7配置Jenkins jenkins 官方下载地址&#xff1a;https://mirrors.jenkins-ci.org/redhat/ maven 官方下载地址&#xff1a;https://archive.apache.org/dist/maven/maven-3/ tomcat 官方下载地址&#xff1a;https://tomcat.apache.org/download-90.cgi 1、配置jdk …

计算机硬盘用u盘维修,U盘装机大师修复磁盘坏道详细教程

我们都知道当我们的磁盘使用的时间久了就会容易出现各种问题&#xff0c;然而硬盘的坏道是最常见的问题之一。关于磁盘出现坏道有很多原因&#xff0c;诸如硬盘本身质量问题&#xff0c;老化&#xff0c;使用不当等等。我们的硬盘内存太小也会导致应用软件对硬盘频繁访问&#…

多屏幕炒股计算机配置,多屏幕股票交易计算机配置建议使用i59400F计算机主机配置(最多六个屏幕)...

在经历了几年的熊市股市之后&#xff0c;2019年股市似乎有所回升. 最近&#xff0c;许多用户询问多屏股票交易计算机的配置&#xff0c;例如三屏&#xff0c;四屏&#xff0c;和六屏. 实际上&#xff0c;与普通计算机的最大区别是该图形卡需要配备多屏幕图形卡. 一台计算机可以…

小学二年级上学期计算机教案,小学数学二年级上册分苹果教案设计

小学数学二年级上册分苹果教案设计〖教学目标1.经历分苹果等实际操作&#xff0c;初步体会有余数除法与生活的密切联系&#xff0c;进一步体会除法的意义。2.通过实际操作&#xff0c;抽象出有余数除法的书写格式&#xff0c;并体会余数一定要比除数小。〖教材分析分苹果是二年…