包头怎样做网站,如何建设网站视频教程,在哪些网站做推广,微网站免费建站系统手动实现简易版RPC(上)
前言
什么是RPC#xff1f;它的原理是什么#xff1f;它有什么特点#xff1f;如果让你实现一个RPC框架#xff0c;你会如何是实现#xff1f;带着这些问题#xff0c;开始今天的学习。
本文主要介绍RPC概述以及一些关于RPC的知识#xff0c;为…手动实现简易版RPC(上)
前言
什么是RPC它的原理是什么它有什么特点如果让你实现一个RPC框架你会如何是实现带着这些问题开始今天的学习。
本文主要介绍RPC概述以及一些关于RPC的知识为后面实现做充足的准备。 1. RPC简述
1.1 什么是RPC
专业定义 RPC是远程过程调用Remote Procedure Call。 RPC 的主要功能目标是让构建分布式计算应用更容易在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标RPC 框架需提供一种透明调用机制让使用者不必显式的区分本地调用和远程调用。
举个 假设你住在一个大社区里社区里有很多居民每个人都有自己的专长。比如有的擅长修理电器有的擅长烹饪美食还有的擅长园艺。有一天你突然发现家里的水龙头坏了你并不会修理于是你想到社区里那位擅长修理电器的居民。 在这个场景下你可以将自己想象成客户端Client那位擅长修理的居民则是服务端Server。你想要调用服务端的“修理水龙头”这个“方法”。但是你和服务端并不在同一个地方不能直接交流于是你需要通过一些方式比如电话发短信或者社区的信息平台来发起这个调用请求。 而达成的效果呢就像你自己修理水龙头一样的丝滑你不需要知道整个修理的过程你只需要知道修理好了这个结果就行 1.2 为什么要用RPC
回到 RPC 的概念RPC 允许一个程序(称为服务消费者)像调用自己程序的方法一样调用另一个程序(称为服务提供者)的接口而不需要了解数据的传输处理过程、底层网络通信的细节等。这些都会由 RPC 框架帮你完成使得开发者可以轻松调用远程服务快速开发分布式系统。
举个
现在有项目A和项目B两个单独的项目项目A提供一系列的关于宠物的服务然后项目B也想使用项目A 的一些服务完成宠物信息的查询。
项目A的查询猫咪信息的服务接口伪代码如下
interface CatService {/**** 获取猫咪信息* return*/Cat getCat(参数1参数2...);/**** 按照id获取猫咪信息* param id* return*/Cat getCatById(int id);//....other
}如果没有RPC项目B会如何调用项目A的服务呢 首先由于项目A和项目 B都是独立的系统不能像 SDK一样作为依赖包引入。 那么就需要项目 A提供 web 服务,并且编写一个点餐接口暴露服务比如访问http:localhost:8088/api/cat 就能调用服务A的猫咪查询服务然后项目B作为服务消费者需要自己构造请求并通过 HttpClient 请求上述地址拿到相关信息。 如果项目B需要调用更多第三方服务每个服务和方法的调用都编写一个 HTTP 请求那么会非常麻烦 示例伪代码如下
urlhttp:localhost:8088/api/cat
reqnew Req(参数1参数2参数3)
reshttpClient.post(url).body(reg).execute()
cat res.data那么如果使用RPC框架对于项目B来说要实现上述调用可能只需要一行代码
catCatService.getCat(参数1参数2参数3)看起来是不是和调用自己的方法一样十分简洁。
2.RPC设计实现思路
基本设计
RPC框架为什么能够简化调用该如何实现一个RPC框架呢带着这两个问题一起往下看
首先呢我们将上述服务A抽象为服务提供者producer服务B抽象为服务消费者consumer 消费者想要调用提供者就需要提供者启动一个 web 服务 然后通过 请求客户端 发送 HTTP 或者其他协议的请求来调用。 比如请求 http:localhost:8088/api/cat 地址后提供者会调用 CatService的 getCat方法: 但如果提供者提供了多个服务和方法每个接口和方法是不是都要单独写一个接口消费者需不需要针对每个接口写一段 HTTP 调用的逻辑么?
其实可以提供一个统一的服务调用接口通过请求处理器,根据客户端的请求参数来进行不同的处理、调用不同的服务和方法。
可以在服务提供者程序维护一个本地服务注册器记录服务和对应实现类的映射。
举个 消费者要调用 CatService服务的 getCat 方法可以发送请求参数为 serviceCatServicemethodgetCat 然后请求处理器会根据 service 从服务注册器中找到对应的服务实现类并且通过 Java 的反射机制调用 method 指定的方法。 但是在数据传输过程中是不支持java实体类进行传输的所以为了达成网络传输需要对传输的参数等实现序列化和反序列化 为了简化消费者发请求的代码实现类似本地调用的体验。可以基于代理模式为消费者要调用的接口生成一个代理对象由代理对象完成请求和响应的过程。所谓代理就是有人帮你做一些事情不用自己操心。 至此一个最简易的 RPC 框架架构图诞生了下图中的虚线部分: 整个简单的调用过程客以参考下图 拓展设计
虽然上述设计已经跑通了基本调用流程但离一个完备的 RPC 框架还有很大的差距让我们带着问题来进一步完善一下架构设计。
1、服务注册发现
问题 1:消费者如何知道提供者的调用地址呢?
继续我们上述的第一个例子社区中有人会修理水龙头那么要想让他帮你来修你们双方得知道双方的地址而这个地址是不是由物业进行保管。因此我们需要一个 注册中心来保存服务提供者的地址。消费者要调用服务时只需从注册中心获取对应服务的提供者地址即可。
架构图如下 主流的注册中心组件Redis、Zookeeper、Consul、Etcd。Dubbo采用的是ZooKeeper提供服务注册与发现功能。
2、负载均衡
问题 2:如果有多个服务提供者消费者应该调用哪个服务提供者呢?
我们可以给服务调用方增加负载均衝能力通过指定不同的算法来决定调用哪一个服务提供者比如轮询、随机、根据性能动态调用等在高并发的场景下需要多个节点或集群来提升整体吞吐能力。。 架构图如下: 3、容错机制
问题 3:如果服务调用失败应该如何处理呢?
为了保证分布式系统的高可用我们通常会给服务的调用增加一定的容错机制比如失败重试、降级调用其他接口等等。 架构图如下: 4、其他
除了上面几个经典设计外如果想要做一个优秀的 RPC 框架还要考虑很多问题。
比如:
服务提供者下线了怎么办需要一个失效节点剔除机制。服务消费者每次都从注册中心拉取信息性能会不会很差?可以使用缓存来优化性能。如何优化 RPC 框架的传输通讯性能?比如选择合适的网络框架、自定义协议头、节约传输体积等如何让整个框架更利于扩展?比如使用 Java 的 SPI 机制、配置化等等。
所以完成 RPC 项目并不难但做一个完美的 RPC 项目却是难于上青天啊!
总结一下我们可以通过做一个 RPC 项目学习到网络、序列化、代理、服务注册发现、负载均衡、容错、可扩展设计等知识相信完成后会收获满满。