| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> 手写一个RPC框架 -> 正文阅读 |
|
[网络协议]手写一个RPC框架 |
什么是RPC,为什么需要RPC?? ? ? ? RPC(Remote Procedure Call)远程过程调用。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 ????????在单体应用的情况下,方法调用只需要知道函数指针就可以进行方法的调用,这种情况是不需要rpc的。而如果是分布式微服务的结构,模块A的某个对象要调用模块B的某个方法,由于模块A与模块B不在同一个机器(服务器)上,所以A是不可能知道B的函数指针的,如果想进行方法调用,必然需要先进行 通信 ,而通信的手段就是 网络编程(Socket 编程),然后A找到B的服务地址,并发送信息,B收到信息后调用本地服务,并将结果返回给A。整个过程如果都需要编程人员来写,将会是一个很复杂的工程。而RPC的作用就是,封装这一系列操作,让客户端调用远程服务时就像调用本地服务一样简便。? RPC需要实现的一些功能和设计思路? ? ? ? 1.公共接口:服务端与客户端都能访问到的方法接口,但只有服务端有接口的实现。 ? ? ? ? 2.服务注册与发现:服务端启动时把服务名称及地址注册到注册中心,客户端通过服务名称从注册中心中找到服务地址,之后才能进行网络请求。 ? ? ? ? 3.传输协议:服务端与客户端之间必须确定好通信的消息结构。 ? ? ? ? 4.序列化协议:远程通信必须要将对象转化为二进制传输,也就是序列化。 ? ? ? ? 5.负载均衡:服务请求量过大时,不能把请求落在单个服务器上。 公共接口:? ? ? ? 创建一个公共接口,但是只在服务端实现接口。
? ? ? ? 那么客户端如何调用这个服务呢??因为客户端没有接口的具体实现类,也就没办法生成实例对象。所以我们需要在客户端中把参数,接口,方法等信息序列化,通过网络传输给服务端,服务端解析后执行方法得到结果返回客户端,如何简化这一过程:动态代理 动态代理:? ? ? ??为什么要动态代理?假设客户端不用动态代理,而使用硬编码,那对于服务端只有一个服务时还行,只需要写一套封装的代码,如果服务端有多个服务,我们不可能对每个服务写一套硬编码,所以就可以考虑到动态代理。 ? ? ? ? 然后就是动态代理的选型思考。常见的动态代理有两种:JDK反射提供的动态代理,CGLIB动态代理。前者代理的类需要实现接口,后者不需要。一般来说,实现接口的类都直接用JDK动态代理,CGLIB是一个基于ASM的字节码生成库,通过类继承的方式实现动态代理。此处选用JDK动态代理。 ? ? ? ? 动态代理需要知道哪些信息?host,port。需要传递这两个值来指明服务端的位置;同时我们需要发送一个request请求对象,也就是说服务端通过接收到这个对象,来确定调用哪个接口的哪个方法。首先需要interfaceName(接口名称),methodName(方法名),考虑到重载,我们还需要知道,methodParam(方法参数),methodParamType(参数类型)。?最后考虑到健康检测,还需要一个flag表示是否为heartBeat(心跳包)。?同样的,客户端收到消息后需要返回一个response响应对象,需要包含如下信息:statusCode(状态码),message(响应补充信息),data(响应数据)。 request 与 response 通过 requestId来确保对应关系。 客户端发送请求:? ? ? ??对象创建完成之后,我们就需要通过网络传输的方式,将请求对象发送到服务器端。这时候就需要用到网络通信(网络编程)。我这里使用Netty来进行网络通信。首先创建NettyClient
?????????同时考虑到对象传输过程中的序列化,client端从注册中心中查找服务这两方面问题。还需要构造两个字段。具体实现放在后边说。
? ? ? ? 之后声明sendRequest方法,为了提高并发量,我们使用CompletableFuture来封装该方法。首先,想要发送信息,必须知道接收方(服务端)的地址。这里就牵扯到了服务的注册与发现。 服务的注册与发现:? ? ? ??显然,这个功能的实现需要借助注册中心,常见的注册中心(我见过的)有:zookeeper,nacos。这里使用nacos,为啥不用zookeeper?当然是因为我没学过啊。参考官网的nacos Java sdk,创建一个NacosUtil类,由于只需要nacos实现服务中心功能,所以配置中心相关的方法不做实现。 ? ? ? ? 第一步:创建nacos实例(首先要启动nacos客户端这个没啥好说的)
? ? ? ? 第二步:编写注册服务进nacos的方法
? ? ? ? 第三步:编写获取所有服务实例的方法
? ? ? ? 第四步:编写注销服务的方法
? ? ? ? 至此,NacosUtil编写完毕,本项目所需注册中心功能已全部实现。 服务端注册服务:? ? ? ? 客户端想要找到这个服务,首先这个服务必须已经被注册进服务中心,我们必须在服务端启动时执行这个操作。与客户端一样,服务端的实现也使用netty。由于是个人实现,所以需要在启动时先对nacos做一个初始化清理,防止之前的服务没有注销。 ? ? ? ? 服务端在初始化过程中,会扫描所有服务,那么服务的定义是什么呢? 我们这里使用自定义注解@Service和@ServiceScan,凡是被这个注解修饰的Class,将被看作是一个服务,我们通过反射机制,获取到所有服务Class,然后将这些服务注册进nacos中,同时我们在HashMap中存储一份,方便我们快速获得服务实例。初始化结束后,执行start方法,对NioEventLoopGroup进行各种参数设置,配置等。为了实现心跳检测机制,我们在ChildHandler中加入IdleStateHandler。然后绑定端口。至此,服务端启动成功,服务被注册到nacos成功。 客户端sendRequest的实现:? ? ? ? 该方法接受一个request参数,前文已经说到,该对象包含interfaceName字段,因此我们可以通过该字段从nacos中查询是否有该服务,并返回该服务的地址的ip,port等信息,封装成一个InetSocketAddress。接着,我们对这个地址创建通道。参考之前服务实例的存储方式,我们对于通道也使用一个HashMap进行存储,方便快速获取。创建完成后,调用writeAndFlush方法,开始进行数据发送。 服务端接受消息:? ? ? ? channelRead0方法,在收到消息时,先通过heartBeat字段判断是否为心跳包,心跳包则不做处理。如果是有效信息,首先对信息进行处理:从存服务实例的HashMap中找到这个服务,然后通过反射调用目标方法。具体代码如下:
? ? ? ? 将调用成功后的数据通过writeAndFlush发送回客户端。 客户端接受调用成功的数据并检查:? ? ? ? 客户端成功接收到数据response,将response与request进行检查,判断是否调用成功。检查逻辑如下:response == null ? requestId == responseId? response.statusCode == 200?,如果检查无误,返回response响应体中的data字段。 |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 6:22:19- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |