【高并发】- 不可不知道的RPC框架服务通信

前言

    前面章节讲解了高并发系统中相关指标、为什么要学习高并发设计思想、高并发系统中每个环节的流量处理等思想。本章节讲解服务通信,来帮助大家更好理解系统间通信过程。

1 RPC框架介绍

    RPC(Remote Procedure Call,远程过程调用)框架,可以让应用中的接口像调用本地方法那样去调用远程服务提供的服务(服务调用者和服务提供者分属于不同进程)。

2 RPC框架的核心原理

(1)RCP框架提供了一个Client Stub组件,其本质上是一个代理类实例,客户端通过它发起方法调用。

(2)Client Stub实现了服务端的接口,它会封装请求信息,主要分为两块:服务名称和请求参数。

(3)Client Stub将上面封装好的信息通过网络(Socket)传输给服务端。

(4)服务端在收到客户端请求后,解析请求信息中的内容及服务名称,并判断服务端是否有该服务信息。

(5)如果在服务端找到请求服务,则调用服务端的接口进行处理。

(6)服务端将处理好的结果封装成响应信息,通过网络返回给客户端。

(7)客户端在收到服务端的响应信息后进行业务处理。

(为了提高传输性能及压缩比,客户端和服务端在进行数据传输时,需要采用合适的传输协议,并对数据进行序列化和反序列化操作)

3. RPC框架需要解决的问题

    通过上面内容了解了RPC框架的核心原理:客户端和服务端按照约定的通信协议进行序列化网络通信;服务端在收到客户端请求后,按照约定协议对数据进行反序列化操作,再将处理结果返回给客户端。由此可看出,RPC框架需要解决以下3个问题:

  • 网络通信问题。

  • 传输协议问题。

  • 序列化和反序列化问题。

(1)网络通信问题

    网络通信问题主要涉及TCP链接及I/O模型两方面的内容,下面进行分析。

    为了实现客户端和服务端之间的数据可靠传输,需要在客户端和服务端进程之间建立传输链接。

    客户端和服务端进行数据传输,需要依赖在两者之间建立的TCP连接,这相当于在客户端和服务端之间建立了一条通道。这样客户端发送的请求就可以通过这个通道到达服务端,服务端也通过这个通道响应数据给客户端。

    从上图可以看出TCP连接的重要性:它不仅需要保证数据的可靠传输,还需要尽量提高传输的效率。所以,在建立连接时,“三次握手”的概念就是用来解决这个问题的。来看下如何建立TCP连接。

    在进行“握手”之前,服务端被动打开相应端口,然后一直监听端口。如果客户端要和服务端建立连接,则主动发起打开端口的请求。客户端主动打开连接后结束CLOSED阶段,被动打开连接的服务端也结束CLOSED阶段,并进入LISTEN阶段,随后进入“三次握手”阶段。

  • 客户端向服务端发起一个请求连接的报文,主要包括:syn=1(代表请求建立新连接),seq=x(seq指客户端初始序号。李二seq=0代表客户端发送的第0号包)。接着客户端进入SYN-SEND状态,等待服务端回复。

  • 客户端在收到客户端的请求连接报文后,如果同意,则让客户端链接,结束自己LISTEN阶段,并返回一段确认报文信息给客户端。确认报文信息包括:syn=1(服务端自己的初始序号),ack=x+1(代表客户端发送到x为止的所有数据都已经被接收,并期望收到对方下一个报文的序号是从x+1开始的)。随后,服务端进入SYN-RCVD阶段。

  • 客户端在收到服务端的确认报文信息后,会确认服务端及自身均具备接收和发送能力,接着结束SYN-SEND阶段,并向服务端返回最后一段报文。报文中主要包括ack=1,seq=x+1,ack=y+1.随后,客户端进入ESTAB-LISHED阶段。

(服务端在收到客户端统一建立新连接的报文后,就知道了客户端和服务端之间的数据传输是正常的,之后结束SYN-RCVD阶段,进入ESTAB-LISHED阶段。在客户端和服务端传输的TCP报文中,客户端和服务端确认号ack及序号seq都是在彼此ack和seq的基础上计算出来的,这样就保证了TCP报文传输的连贯性)

    

    客户端和服务端之间进行TCP通信,主要涉及I/O模型的选择

    常用的网络模型有4种:同步阻塞I/O(BIO)、同步非阻塞I/O(NIO)、I/O多路复用、异步非阻塞I/O(AIO)。其中,AIO属于异步I/O,其他均属于同步I/O。

    ① 同步阻塞I/O:在客户端发送请求后,该请求会在内核中阻塞住,直到服务端的响应数据到来后才进行后续处理。对于客户端每次的请求,服务端都需要创建与之对应的线程,以处理到达的请求并响应数据。这样,如果客户端同时发起的请求过多,则服务端需要同时开启很多线程,但服务器的线程是有限的,若超过了最大负载,那后来的请求将得不到处理。

(这种同步阻塞I/O,适用于连接数比较少的业务场景。这样服务端就不会因为线程数过多而拖垮服务器,且编程方式更简单,更容易被理解)

    ② 同步非阻塞I/O:在客户端每次发送请求时,在内核空间中,该请求即使没有等到响应数据也不会阻塞住,会继续往下执行(避免进程阻塞在某个连接上,可以在同一个线程中处理所有连接)。

(同步非阻塞I/O模型需要频繁地轮询,非常耗CPU资源。在高并发场景中,这种方式会浪费很多时间去轮询没有任何数据的连接。因此,同步非阻塞I/O模型一般只出现在提供某种特定功能的系统中)

    ③ I/O多路复用:在客户端每次发送请求时,服务端不需要每次都创建对应的线程去进行处理,而是通过I/O多路复用技术将多个I/O通道复用到一个复用器上。这样可以实现单线程同时处理多个客户端的请求,提升了系统的整体性能。

(多路复用无须采用多线程的方式,也不用去轮询,只需要阻塞在select()函数上即可。select()函数同时管理多个连接,并且不断地查看各个连接的请求。这种模式更适合于高并发的场景。当select()函数管理的连接数比较少时,这种模型将变成阻塞I/O模型)

    ④ 异步非阻塞I/O:客户端在发起一个I/O操作后,不需要等待直接返回。等I/O操作完成后,操作系统内核会主动通知客户端数据已经准备好了。此时,客户端只需要在应用中对数据进行处理即可。不需要进行实际的I/O读写操作,真正的I/O读写操作由操作系统内核完成,这样就不存在阻塞等待的问题。

(AIO模型更适用于“连接数比较多,且请求消耗比较重”的业务场景,但是其编程比较复杂,操作系统支持不好。在Linux中更多的还是采用I/O多路复用模型。应基于业务场景,选择合适的网络I/O模型进行网络编程。建议使用一些目前比较成熟的框架去构建,例如开源的Netty、Mina等都是被业界大规模使用且已被验证的框架)

(2)传输协议问题

    HTTP协议是最常见的协议,它是客户端和服务端之间请求和应答的标准。如今大部分的应用都通过浏览器进行访问,所以,它们大都使用HTTP协议进行数据的传输。

    私有协议在RPC框架中比较常见。在对数据传输效率有极高要求的场景中经常要自定义私有协议,如Dubbo框架中的Dubbo协议、Thrift框架中的Thrift协议等都可以自定义私有协议。

    无论是开放的HTTP协议,还是自定义的私有协议,都是一种“契约”。客户端和服务端都遵循这个“契约”进行数据的传输,其交互方式如下:

    ① 客户端按照约定的协议对要传输的数据进行编码,然后通过网络层将编码后的数据传输出去。

    ② 服务端通过网络层接收客户端传来的数据,之后按照同样的协议对数据进行解码和相关业务处理,再将处理后的结果进行过编码,然后通过网络将编码后的数据传输出去。

    ③ 客户端在接收到服务端的响应数据后,采用同样的方式对数据进行解码得到最终的响应数据。

    传输协议一般包含两个部分:消息头和消息体。

  • 消息头中存放的是一些公共的信息和一些扩展信息。

  • 消息体中存放的是要传输的数据信息。

    对于HTTP协议,在请求时,在消息头中,Host代表对方主机,Connection代表链接是否复用,Content-Type代表请求数据类型,Content-Lenght代表请求消息体数据长度。

(3)序列化和反序列化问题

    在数据网络传输中,客户端先将数据进行编码,然后通过网络传输出去;服务端收到传输数据,然后对数据解码。这就是序列化和反序列化的过程。

  • 序列化:将对象转换为字节序列。

  • 反序列化:将字节序列恢复成对相关。

(把对象信息【集合或者具体对象等】持久化保存下来,这个过程也是序列化,即把内存中的对象变成字节序列。)

    序列化和反序列化的核心是对象状态的保存和重建。序列化有如下几个优点:

  • 在将对象转换为字节序列存储到硬盘之后,即使虚拟机停止,字节序列也不受影响,当虚拟机再次启动时,依然可以将字节序列恢复成对象。

  • 由于在网络传输中采用的是字节序列形式,所以传输效率较高。

  • 序列化后的对象占用的内存空间较小。

最常用的序列化方式主要有两种:

  • JSON/XML格式的序列化:结构较为清晰,对客户端友好,在编程上也很方便,具有很强的可读性。在对外接口上,使用这种方式居多。

  • 二进制序列化:这种方式在考虑性能的场景(如对传输速度和压缩比有很高要求的场景)中使用的比较多,但是可读性较弱。另外,由于采用二进制方式存储,所以这种方式在通用性上不是太好。

ps:在选择序列化方式时,需要从以下方面进行考虑:

  • 是否支持更多的数据结构。如果支持更多的数据结构,则意味着在编程时简单易用。

  • 是否具备跨语言特性,即在Java语言和C语言中序列化方式是一样的。

  • 在性能上是否有足够多的考量,如存储空间的占用、传输速度的比较等。

    本章节主要针对系统间通信过程中对RPC服务通信框架进行了讲解,希望能够对小伙伴有所帮助,下一章节会对分布式事务管理设计方案进行讲解

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值