大家好,我是哪吒。
在服务化拆分之后,服务提供者和服务调用者会运行在两台不同的物理机的不同的进程内,他们之间的调用称为远程方法调用,简称RPC。
RPC的大体流程是:
- 建立网络连接;
- 按照某种约定的协议进行网络通信;
- 正常通信后,服务端接收到客户端的请求,然后进行处理;
- 处理成功后,对处理结果进行压缩、序列化,再将其返回给客户端;
这也就对应着4个问题:
- 客户端和服务端如何建立网络连接?
- 服务端如何处理请求?
- 数据传输采用什么协议?
- 数据该如何序列化和反序列化?
一、客户端和服务端如何建立网络连接?
1、HTTP通信
HTTP通信是一种基于请求和响应模型的通信协议,用于在网络中传输数据。它由客户端和服务器组成,客户端向服务器发送请求,服务器处理请求并返回适当的响应。
(1)HTTP通信的过程如下
- 建立连接:HTTP协议是应用层协议,需要在传输层协议(如TCP)的基础上建立连接。当客户端向服务器发送请求时,首先需要建立TCP连接。
- 发送请求:一旦建立了TCP连接,客户端就可以向服务器发送HTTP请求。请求命令包括请求方法(如GET、POST、PUT、DELETE等)、请求URI、HTTP协议版本以及请求头和请求体等信息。
- 接收请求:服务器接收到请求后,会对其进行解析和处理,包括对请求方法、URI、协议版本以及请求头和请求体的处理。
- 返回响应:服务器处理完请求后,会返回适当的响应。响应包括HTTP协议版本、响应状态码、响应头和响应体等信息。
- 断开连接:在完成通信后,客户端和服务器会断开TCP连接。
(2)HTTP通信的特点如下:
- 支持B/S模式:HTTP通信是浏览器和服务器之间的通信,支持B/S模式,不需要客户端和服务器建立直接的连接。
- 简单快速:HTTP协议简单快速,能够快速地传输数据,减少网络延迟。
- 灵活:HTTP协议支持多种请求方法,可以支持不同的操作类型,如GET用于获取数据,POST用于提交数据等。
- 无连接:HTTP协议无连接,每个请求都需要建立TCP连接,但连接建立后可以处理多个请求,减少了网络开销。
- 无状态:HTTP协议无状态,每个请求都是独立的,服务器不会记录之前请求的状态。这使得HTTP通信具有更好的可扩展性和可维护性。
完成请求后,再经历一次"四次挥手"的过程来断开连接。
2、Socket通信
Socket通信是基于TCP/IP协议的封装,建立一次Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ;另一个运行于服务器端,称为ServerSocket 。就像下图所描述的,Socket通信的过程分为四个步骤:服务器监听、客户端请求、连接确认、数据传输。
- 服务器监听:ServerSocket通过调用bind()函数绑定某个具体接口,然后调用listen()函数实时监控网络状态,等待客户端的连接请求;
- 客户端请求:ClientSocket调用connect()函数向ServerSocket绑定的地址和端口发起连接请求;
- 服务端连接确认:当ServerSocket监听到或者接收到ClientSocket的连接请求时,调用accept()函数响应ClientSocket的请求,同客户端建立连接;
- 数据传输:当ClientSocket和ServerSocket建立连接后,ClientSocket调用send()函数,ServerSocket调用receive()函数,ServerSocket处理完请求后,调用send()函数,ClientSocket调用receive()函数,就可以得到返回结果。
当客户端和服务端建立网络连接后,就可以发起请求了。但网络不一定总是可靠的,经常会遇到网络闪断、连接超时、服务端宕机等各种异常,通常的处理手段有两种:
- 链路存活检测:客户端需要定时发送心跳检测信息(一般为ping请求)给服务端,如果服务端连续n次心跳检测或者超过规定的时间都没有回复消息,则认为此链路已经失效,这个时候客户端就需要重新与服务端建立连接。
- 断连重试:通常多种情况会导致连接断开,比如客户端主动关闭、服务端宕机、网络故障等。这个时候客户端就需要与服务端重新建立连接,但一般不会立刻完成连接,而是要等待一个固定的时间后再发起连接,避免服务端的连接回收不及时,而客户端瞬间重连的请求太多而把服务端的连接数占满。
二、服务端如何处理请求?
网络已经连通,服务端如何处理客户端请求呢?
1、通常来说,有三种处理方式:
- 同步阻塞BIO,客户端的每一个请求,服务端就会生成一个线程去处理。当客户端同时发起的请求很多时,服务端需要创建很多的线程去处理每一个请求,如果达到了系统最大的线程瓶颈数,新来的请求就无法处理了。
- 同步非阻塞NIO,客户端的每一个请求,服务端并不是每次都创建一个新的线程来处理,而是通过IO多路复用技术进行处理。系统的一个线程可以处理多个客户端请求,这种方式的优势是开销小,不用为每个请求创建一个新的线程,可以节省系统开销。
- 异步非阻塞方式AIO,客户端只需要发起一个IO操作,然后会立即返回,等IO操作完成后,客户端得到IO操作完成的通知,此时客户端再对返回值进行处理,不需要进行IO读写操作,真正的IO读取和写入操作已经由内核完成了。这种方式的优势是客户端无需等待,不存在阻塞等待的问题。
2、不同的处理方式对应着不同的业务场景:
- BIO适用于连接数比较少的业务场景,系统会有足够的线程供你使用,这种程序比较简单直观,易于理解;
- NIO适用于连接数比较多但是每个请求的业务量不是很大,处理速度很快,比如聊天服务器。
- AIO适用于连接数比较多,而且每个请求的业务量比较大,处理比较耗时。
三、HTTP协议传输流程
最常用的协议是HTTP协议, 它是一种开放协议,各大网站的服务器和浏览器之间的数据传输大都采用这种协议。
- 根据协议的规定,服务消费者对数据进行处理;
- 然后通过网络传送到服务端;
- 服务端从网络中获取数据后,根据规定,对传输的数据进行解码操作;
- 然后根据业务逻辑进行处理;
- 再把处理结果返回给服务消费者;
- 服务消费者对返回的结果进行解码处理,最终得到服务端返回的数据结果。
通常协议规定中包含消息头和消息体两部分。消息头存放的是协议的公共字段以及用户扩展字段,消息体存放的是传输数据的具体内容。
四、数据该如何序列化和反序列化?
在微服务中,数据的序列化和反序列化是非常重要的环节,它们涉及到数据的传输和存储。以下是一些关于微服务中数据序列化和反序列化的建议:
1、选择合适的序列化方式
序列化是将数据结构或对象状态转换为可以存储或传输的格式的过程,而反序列化则是将已序列化的数据还原为原始数据结构或对象状态的过程。在微服务中,需要选择一种高效的、易于理解和使用的序列化方式。常见的序列化方式包括JSON、XML、Protocol Buffers等。其中,JSON是一种轻量级的数据交换格式,它以可读性高、兼容性好、支持多种语言等优点而被广泛使用。而Protocol Buffers则是一种高效的数据序列化格式,它能够将结构化的数据序列化成字节流,并可以在网络中进行传输。
2、考虑数据传输效率
在微服务中,数据传输效率是非常重要的,因此需要选择一种高效的序列化方式。JSON和XML虽然易于理解和使用,但它们的传输效率相对较低。而Protocol Buffers则是一种更高效的序列化格式,它能够将数据结构化并压缩成更小的字节流,从而提高了数据传输效率。
3、考虑数据结构的变化
在微服务中,数据结构可能会随着业务需求的变化而变化。因此,在选择序列化方式时需要考虑数据结构的变化情况。如果数据结构经常变化,那么选择一种灵活的序列化方式是非常重要的。如果数据结构相对稳定,那么可以选择一种更高效的序列化方式。
4、考虑跨语言和跨平台的需求
微服务通常需要支持不同的语言和平台。因此,在选择序列化方式时需要考虑跨语言和跨平台的需求。JSON和XML是一种通用的数据交换格式,支持多种语言和平台。而Protocol Buffers则是一种跨语言的数据序列化格式,它可以在不同的语言之间进行数据交换。
5、考虑数据的安全性
在微服务中,数据的传输和存储都需要考虑安全性问题。因此,在选择序列化方式时需要考虑数据的安全性。如果需要在网络中进行数据传输,那么可以选择一种加密的序列化方式来保护数据的安全性。如果需要在本地存储数据,那么可以选择一种加密的存储方式来保护数据的安全性。
在微服务中,数据的序列化和反序列化是非常重要的环节。需要根据实际情况选择合适的序列化方式,并考虑数据传输效率、数据结构的变化、跨语言和跨平台的需求以及数据的安全性等因素。
五、总结
- 通信框架,主要解决客户端和服务端如何建立连接、管理连接以及服务端如何处理请求的问题;
- 通信协议,主要解决客户端和服务端采用哪种数据传输协议的问题;
- 序列化和反序列化,主要解决客户端和服务端采用哪种数据编解码的问题。
这三个部分就组成了一个完整的RPC调用框架,通信框架提供了基础的通信能力,通信协议描述了通信契约,而序列化和反序列化则用于数据的编/解码。一个通信框架可以适配多种通信协议,也可以采用多种序列化和反序列化的格式,比如服务化框架Dubbo不仅支持Dubbo协议,还支持RMI协议、HTTP协议等,而且还支持多种序列化和反序列化格式,比如JSON、Hession 2.0以及Java序列化等。