高并发系统设计学习笔记(二十一) RPC框架10万QPS下如何实现毫秒级的服务调用

目录

一、场景分析

二、RPC(Remote Procedure Call)远程过程调用

1.RMI

2.Web Service

3.调用分析

三、如何提升网络传输性能

1.等待资源的阶段

2.使用资源的阶段

四、选择合适的序列化方式


一、场景分析

来思考这样一个场景: 你的垂直电商系统的QPS已经达到了每秒2万次,在做了服务化拆分之后,由于我们把业务逻辑都拆分到了单独部署的服务中,那么假设你在完成一次完整的请求时需要调用4~5次服务,计算下来,RPC服务需要承载大概每秒10万次的请求。而你该如何设计RPC框架承载如此大的请求量呢?

  • 选择合适的网络模型,有针对性地调整网络参数优化网络传输性能;
  • 选择合适的序列化方式,以提升封包、解包的性能

二、RPC(Remote Procedure Call)远程过程调用

1.RMI

也许觉得只有像Dubbo、Grpc、Thrift这些新兴的框架才算是RPC框架,其实严格来说,Java原生就有一套远程调用框架叫做RMI(Remote Method Invocation),它可以让Java程序通过网络调用另一台机器上的Java对象的方法。它是一种远程调用的方法,也是J2EE时代大名鼎鼎的EJB的实现基础。

2.Web Service

它也可以认为是RPC的一种实现方式。它的优势是使用HTTP+SOAP协议,保证了调用可以跨语言、跨平台。只要你支持HTTP协议,可以解析XML,那么就能够使用Web Service。在我看来,由于它使用XML封装数据,数据包大,性能还是比较差

3.调用分析

电商系统中商品详情页面需要商品数据、评论数据还有店铺数据,如果在一体化的架构中,你只需要从商品库、评论库和店铺库获取数据就可以了,不考虑缓存的情况下有三次网络请求

但是如果独立出商品服务、评论服务和店铺服务之后,那么就需要分别调用这三个服务,而这三个服务又会分别调用各自的数据库,这就是六次网络请求。如果你服务拆分得更细粒度,那么多出的网络调用就会越多,请求的延迟就会更长,而这就是你为了提升系统的扩展性在性能上所付出的代价

RPC步骤如下

  • 在一次RPC调用过程中,客户端首先会将调用的类名、方法名、参数名、参数值等信息,序列化成二进制流
  • 然后客户端二进制流通过网络发送给服务端
  • 服务端接收到二进制流之后将它反序列化,得到需要调用的类名、方法名、参数名和参数值,再通过动态代理的方式调用对应的方法得到返回值
  • 服务端返回值序列化,再通过网络发送给客户端;
  • 客户端对结果反序列化之后,就可以得到调用的结果

三、如何提升网络传输性能

1.等待资源的阶段

等待资源的阶段I/O会经历一个等待资源的阶段,在这个过程中我们对I/O会有两种处理方式:

  • 阻塞。指的是在数据不可用时I/O请求一直阻塞直到数据返回
  • 非阻塞。指的是数据不可用时I/O请求立即返回直到被通知资源可用为止

2.使用资源的阶段

比如说从网络上接收到数据,并且拷贝到应用程序的缓冲区里面。在这个阶段我们也会有两种处理方式:

  • 同步处理。指的是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默认也是使用这种模型。 


四、选择合适的序列化方式

  • 如果对于性能要求不高,在传输数据占用带宽不大的场景下可以使用JSON作为序列化协议;
  • 如果对于性能要求比较高,那么使用Thrift或者Protobuf都可以。而Thrift提供了配套的RPC框架,所以想要一体化的解决方案,你可以优先考虑Thrift
  • 在一些存储的场景下,比如说你的缓存中存储的数据占用空间较大,那么你可以考虑使用Protobuf替换JSON作为存储数据的序列化方式
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值