网站首页 > 博客文章 正文
思考这样一个场景:电商系统的 QPS已经到了 2w/s ,做了服务化拆分之后,我们把业务逻辑拆分到了单独部署的服务中,那么如何设计如此大的请求量呢?
- 选择合适的网络模型,有针对性地调整网络参数优化网络传输性能;
- 选择合适的序列化方式,以提升封包、解包的性能。
你所知道的 RPC
说到 RPC(Remote Procedure Call,远程过程调用),你不会陌生,它指的是通过网络调用另一台计算机上部署服务的技术。
而 Rpc框架封装了网络调用的细节,让你像调用本地服务一样调用远程服务。你也许觉得只有像 Dubbo、Grpc、Thrift 这些新兴的框架才算是 RPC 框架,其实严格来说,你很早之前就接触到与 RPC 相关的技术了。
时至今日,你仍然可以通过 Spring 的“RmiServiceExporter”将 Spring 管理的 bean 暴露成一个 RMI 的服务,从而继续使用 RMI 来实现跨进程的方法调用。之所以 RMI 没有像 Dubbo、Grpc 一样大火,是因为它存在着一些缺陷:
- RMI 使用专为 Java 远程对象定制的协议 JRMP(Java Remote Messaging Protocol)进行通信,这限制了它的通信双方只能是 Java 语言的程序,无法实现跨语言通信;
- RMI 使用 Java 原生的对象序列化方式,生成的字节数组空间较大,效率很差。
另一个你可能听过的技术是 Web service ,也可以认为是RPC的一种实现方式,使用 HTTP+SOAP 协议,保证了调用可以跨语言、跨平台。只要你支持 HTTP 协议,可以解析 XML,那么就能够使用 Web Service。在我看来,由于它使用 XML 封装数据,数据包大,性能还是比较差。
借上面几个例子我主要是想告诉你,RPC 并不是互联网时代的产物,也不是服务化之后才衍生出来的技术,而是一种规范,只要是封装了网络调用的细节能够实现远程调用其他服务,就可以算作是一种 RPC 技术了。
那么你的垂直电商项目在使用 RPC 框架之后会产生什么变化呢?
如果是独立出来的商品服务、评论服务、店铺服务之后,就需要分别调用这三个服务,而这三个服务又会分别调用各自数据库,这就是6次网络请求,如果你服务拆分的更加细粒度,那么多出的网络调用就会更多,这就是为了提升系统的扩展性在性能上所付出的代价。
那么 如何优化网络的调用,减少性能的影响呢?首先 你需要了解一次完整的RPC经过了哪些步骤:
- 在一次 RPC 调用过程中,客户端首先会将调用的类名、方法名、参数名、参数值等信息,序列化成二进制流;
- 在一次 RPC 调用过程中,客户端首先会将调用的类名、方法名、参数名、参数值等信息,序列化成二进制流;
- 服务端接收到二进制流之后将它反序列化,得到需要调用的类名、方法名、参数名和参数值,再通过动态代理的方式调用对应的方法得到返回值;
- 服务端将返回值序列化,再通过网络发送给客户端;
- 客户端对结果反序列化之后,就可以得到调用的结果了。
总这张图可以看出网络传输的过程,将请求序列化和反序列化的过程,所以提升性能 需要从网络传输和序列化 两个维度着手。
如何提升网络传输性能
网络传输优化中,你首先需要考虑的就是选择一种高性能的I/O 模型,所谓 I/O 模型,就是我们处理 I/O 的方式。而一般单次 I/O 请求会分为两个阶段,每个阶段对于 I/O 的处理方式是不同的。
首先,I/O 会经历一个等待资源的过程,比如 等待网络传输数据可用,这个过程我们有两种处理方式:
- 阻塞:指的是数据不可用时I/O 请求一直阻塞,知道数据返回
- 非阻塞:数据不可用时 I/O 请求立即返回,知道被通知资源可用为止
然后是使用资源的阶段,比如从网络上接收到数据,并且拷贝到应用程序的缓冲区里面,这个阶段我们也有两种处理方式:
- 同步处理。I/O 请求在读取或者写入数据时会阻塞,知道读取或者写入数据完成
- 异步处理。I/O 请求在读取或者写入数据时立即返回,当操作系统处理完成I/O 请求并且将数据拷贝到用户提供的缓冲区后,再通知应用I/O 请求执行完成。
将这两个阶段的四种处理方式做一些排列组合,再做一些补充,就得到了我们常见的五种 I/O 模型:
- 同步阻塞 I/O;
- 同步非阻塞 I/O;
- 同步多路 I/O 复用;
- 信号驱动 I/O;
- 异步 I/O。
下面我们通过例子 来理解这五种模型,
- 如果你站在灶台边上一直等着(资源)水烧开,然后倒水(使用资源)那么就是同步阻塞 I/O
- 如果你烧水的时候在沙发上看电视(不再等待资源),但是还是时不时的去看水开了没有,一旦开了,马上去倒水(使用资源)那么就是同步非阻塞 I/O
- 如果你需要很多壶水,看电视的间隙去看看哪湖水开了(等待多个资源)哪壶开了就使用,这就是同步多路复用 I/O
- 不过你发现自己总是跑厨房去看水开了没,太累了,于是你考虑给你的水壶加一个报警器(信号),只要水开了就马上去倒水,这就是信号驱动 I/O;
- 最后一种就高级了,你发明了一个智能水壶,在水烧好后自动就可以把水倒好,这就是异步 I/O。
这五种 I/O 模型中最被广泛使用的是多路 I/O 复用,Linux 系统中的 select、epoll 等系统调用都是支持多路 I/O 复用模型的,Java 中的高性能网络框架 Netty 默认也是使用这种模型。你可以选择它。
选了高性能I/O模型是不是就是高效传输了呢?没有那么简单,还涉及网络参数的调优,这里就不举例子了。
选择合适的序列化方式
在对网络数据传输完成调优之后,另外一个需要关注的点就是数据的序列化和反序列化。通常所说的序列化是将传输对象转换成二进制串的过程,而反序列化则是相反的动作,是将二进制串转换成对象的过程。
从上面的 RPC 调用过程中你可以看到,一次 RPC 调用需要经历两次数据序列化的过程和两次数据反序列化的过程,可见它们对于 RPC 的性能影响是很大的,那么我们在选择序列化方式的时候需要考虑哪些因素呢?
首先需要考虑的肯定是性能嘛,性能包括时间上的开销和空间上的开销,时间上的开销就是序列化和反序列化的速度,这是显而易见需要重点考虑的,而空间上的开销则是序列化后的二进制串的大小,过大的二进制串也会占据传输带宽影响传输效率。
除去性能之外,我们需要考虑的是它是否可以跨语言、跨平台,这一点也非常重要,因为一般的公司的技术体系都不是单一的,使用的语言也不是单一的,那么如果你的 RPC 框架中传输的数据只能被一种语言解析,这无疑限制了框架的使用。
另外,扩展性也是一个需要考虑的重点问题。你想想,如果对象增加了一个字段就会造成传输协议的不兼容,导致服务调用失败,这会是多么可怕的事情。
综合上面的几个考虑点,在我看来,我们的序列化备选方案主要有以下几种:
首先是大家熟知的 JSON,它起源于 JavaScript 是一种最广泛使用的序列化协议,它的优势简单易用,同时在性能上相比 XML 有比较大的优势。
另外的 Thrift 和 Protobuf 都是需要引入 IDL(Interface description language)的,也就是需要按照约定的语法写一个 IDL 文件,然后通过特定的编译器将它转换成各语言对应的代码,从而实现跨语言的特点。
Thrift 是 Facebook 开源的高性能的序列化协议,也是一个轻量级的 RPC 框架;Protobuf 是谷歌开源的序列化协议。它们的共同特点是无论在空间上还是时间上都有着很高的性能,缺点就是由于 IDL 存在带来一些使用上的不方便。
那么你要如何选择这几种序列化协议呢?这里我给你几点建议:
- 如果对于性能要求不高,在传输数据占用带宽不大的场景下可以使用 JSON 作为序列化协议;
- 如果对于性能要求比较高,那么使用 Thrift 或者 Protobuf 都可以。而 Thrift 提供了配套的 RPC 框架,所以想要一体化的解决方案,你可以优先考虑 Thrift;
- 在一些存储的场景下,比如说你的缓存中存储的数据占用空间较大,那么你可以考虑使用 Protobuf 替换 JSON 作为存储数据的序列化方式。
猜你喜欢
- 2024-10-30 SpringCloudRPC远程调用核心原理:代理模式与RPC客户端实现类
- 2024-10-30 基于HTTP RESTFul的远程调用和基于RPC的远程调用有什么区别?
- 2024-10-30 基于HTTPRESTFul的远程调用和基于RPC的远程调用有什么区别?
- 2024-10-30 远程方法调用RMI详解,和RPC的思路很相似
- 2024-10-30 一文读懂几种远程调用的区别与联系
- 2024-10-30 架构实战面试必备:如何实现RPC调用保护
- 2024-10-30 事务的使用:不要在事务中包含任何rpc调用
- 2024-10-30 SpringBoot+RabbitMQ 实现 RPC 调用
- 2024-10-30 Java进阶:远程过程调用(RPC)及回调方法(一)
- 2024-10-30 跨域RPC调用,一个不可以被忽略的技术问题,5分钟科普
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)