【RPC系列】1、聊一聊Rpc实现流程
发布日期:2021-06-24 15:28:50 浏览次数:2 分类:技术文章

本文共 3128 字,大约阅读时间需要 10 分钟。

RPC(Remote Procedure Call,远程过程调用)是建立在Socket之上的一种多进程间的通信机制。

可以自动处理通信协议、对象序列化、网络传输等复杂细节。

1、先了解Socket通信,类似聊天工具

客户端:

//绑定连接主机和端口Socket client = new Socket("127.0.0.1", 9999);//创建一个输出流,就用最简单的BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()), 1000);//获取控制台输入BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "utf-8"));//往流里面写数据while(true) {	String str = br.readLine();	bw.write(str);	bw.write("\n");	//关闭	bw.flush();	//退出聊天	if("exit".equals(str)) {		break;	}}client.close();

服务端:

//开启一个端口监控连接ServerSocket server = new ServerSocket(9999);//等待连接Socket socket = server.accept();//获取输入流BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//读数据while(true) {	String str = br.readLine();	if("exit".equals(str)) {		break;	}	if(str != null) {		System.out.println("<<<" + str);	}}server.close();

2、熟悉的NIO框架

从以上简单的socket通信来看,使用了字符串的传输,还会遇到字符串的编码问题,要形成生产实用的框架还需要考虑:

(1)网络异常情况。就是

(2)复杂数据传输编解码问题。

(3)多客户端连接以及服务端并发处理问题,连接复用,于是出现Nio模型的多路复用等。

(4)健康检查(心跳)机制等。

(5)缓存机制提高读写效率

要学习TCP、UDP通信,还是需要恶补一下Socket的知识:

TCP通信:每个TCP Socket在内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式及TCP的滑动窗口就是依赖于两个独立的Buffer(缓冲区)。接收缓冲区把数据缓存入内核,若应用进程一直没有调用Socket的read方法,那么数据就会一直被缓存在接收缓冲区。read所做的事就是把内核接收缓冲区的数据复制到应用层用户的Buffer里面send所做的事就是把应用层用户的Buffer的数据复制到内核发送缓冲区

UDP通信:有接收缓冲区,没有发送缓冲区,意思就是有数据就发,不管对方收到没有。

TCP/IP滑动窗口和流量控制机制:想一下,如果Buffer满了怎么办,因为都无限制的去接收和发送。如果应用程序一直没有read,那么Buffer满了之后就会通知对端TCP协议中的窗口关闭,保证TCP socket接收缓冲区不会溢出,这就是滑动窗口实现。因为对方不允许发出超过窗口大小的数据,所以如果对方无视窗口大小而发出了超大数据,接收方就会选择丢弃,就是TCP的流量控制。

了解了滑动窗口和流量控制的概念,就很容易理解“阻塞”的概念了,如果Buffer里的数据是空的,那么read的时候线程就会阻塞(等待)了,直到有数据进入接收缓冲区,发送也是一样。如果待发送的数据大于Buffer发送缓冲区的大小了,那么就会阻塞在write方法上,等待缓冲区的数据发送出去,再继续发送下一段。

以上可以看出,每个socket的读写都要创建一个线程去处理,而且A/B两方的读写效率会互相影响,A写的效率慢,势必造成B阻塞在读上,多个socket连接就会出现大量线程阻塞在I/O等待上。

那如何做到“非阻塞”?无非就是将socket的读写与I/O线程解耦的问题。也就是NIO(No-Blocking I/O),于是就引入了事件的机制。有了事件,那就要想到需要有个事件驱动的总调度中心。我们可以认为NIO底层就存在一个I/O调度线程,它可以轮询扫描每个socket的缓冲区,当发现写入缓冲区未满的时候,就产生一个可写事件,一次写不完就等待下次可写事件的通知,当发现接收缓冲区中有数据时就产生可读事件。当然这都是程序去控制的,一定要注意轮询的深度,切忌空轮询。

以上可见阻塞和非阻塞的区别就是线程是否处于等待状态中

关于Reactor模型在下节讲诉。

3、大名鼎鼎RPC

首先清晰的理解下RPC和Socket在编程上的区别,以上基本了解了socket的编程和建立在socket通信的nio的模型,我们可以认为是客户端与服务端之间的通信连接编程,socket连接我们可以n vs 1,服务端绑定端口,客户端只要提供Host和Port就可以进行连接了,那如果多服务端会出现什么情况。

大名鼎鼎的RPC开发框架为了针对分布式系统开发诞生了。是由Sun公司提出的。为了将上面的socket程序改写成Rpc程序,我们需要将服务端和客户端分离,即在socket接口外封装一层Stub模块,实现过程远程调用所需要的通信功能,比如参数及调用结果的序列化功能,并通过网络完成远程传输。

看一下简单的rpc实现流程:

整个rpc的调用过程如下:

(1)服务消费方(客户端,消费者,订阅者)-caller以本地调用方式调用服务。

(2)User-stub获取到User的调用后负责将方法、参数等组装成能够在网络中传输的消息体。

(3)RPC-runtime找到服务地址,并将消息发送到服务端。

(4)Callee收到消息后将请求交给Server-stub进行解码。

(5)Server-stub解码完之后根据服务指令交给Server去调用。

(6)Server执行完并将结果返回给Server-stub进行打包并发送至Caller。

(7)Caller接收到响应包后交给User-stub进行解码并返回结果给User。

其中实现主要有三个模块的技术要点:

  • 高性能网络编程技术
  • 复杂结构消息体的序列化和反序列化技术
  • 动态代理编程技术

说到rpc就会说到微服务架构,一般而言,如果分布式系统具有以下几个特点,就可以称之为微服务架构:

  1. 任何一个服务都由多个独立的进程提供服务,这些进程可以分布在多台物理机上,任何进程宕机,都不会影响系统提供服务
  2. 整个系统是由多个微服务有机组成的一个分布式系统,换而言之,不是一个巨大的的单体应用。

目前主流的微服务架构分为一下三类:

  • 基于传统的高性能RPC技术和服务治理的微服务架构,代表为ZeroC IceGrid
  • 以Http Rest为通信机制的通用性微服务架构,最典型的为Spring cloud
  • 基于容器技术,目标是部署在公有云平台上的微服务架构基础平台,他们并没有提供特定的RPC通信机制,只保证TCP的可达性,提供一个微服务平台,任何分布式系统都可以部署上面,只需要设定通信协议:REST、Thrift、gRPC或者自定义

本文参考Leader-us著《架构解密》、张亮等著《未来架构》

 

转载地址:https://blog.csdn.net/weixin_33602978/article/details/105221653 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【RPC系列】2、从Reactor模型到Netty核心
下一篇:【工具使用】解决IDEA创建后的maven工程没有src/main/java等目录

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月13日 07时34分43秒