一. 基本概念
区别于传统的增删改查型的业务项目,本项目侧重于开发框架,并且涉及架构方面的技术知识点。
1. 什么是RPC?
远程过程调用(Remote Procedure Call),是一种计算机通信协议,它允许程序在不同的计算机之间交互通信,以实现本地调用的效果。
类似于如今的外卖平台,外卖平台出现之前消费者需要到线下店铺购买,耗时耗力。现在通过外卖平台,只需要下单选择商品,无需关心数据在网络中如何传输的、外卖平台是怎么转发的、骑手是如何配送的等事情,等待商品即可。
进一步说明:可以让客户端在不清楚调用细节的情况下,实现对远程计算机上某个方法或服务的调用,就像调用本地方法一样。
RPC既然是一种计算机协议,那么就需要开发者去遵循这套规范进行实现,目前市面上常见的RPC框架有:Dubbo、GRPC等。
2. 为什么需要RPC?
在现实场景中,随着企业中业务量和应用功能模块的增多,单机部署运行已经无法满足需求,可能需要将业务或功能模块拆分到不同的机器和服务器上,以减轻压力,但有些功能是通用的,因此可以将这些通用的模块划分成公共模块,组成独立的一部分。
RPC就允许一个程序(服务消费者)像调用自己的本地方法一样去调用这个公共模块的接口(服务提供者),不需要了解数据的传输处理过程、底层网络通信的细节等。RPC已经帮助做完了这些事情。
举例:
项目A提供了点餐服务,项目B需要调用点餐服务完成下单。
//伪代码
interface OrderService{
//点餐,返回orderId
long order(参数1,参数2,参数3);
}
如果没有RPC,并且由于项目A和项目B都是独立的系统,所以不能像SDK一样作为依赖包直接引入。那么就需要项目A提供Web服务,同时编写一个点餐服务接口提供给外界,比如访问http://xxx.com 就能调用点餐服务。之后项目B作为服务消费者,需要自己构造请求,并通过HttpClient请求上述地址。理论上来说,如果B需要多个第三方服务,那么每个服务和方法的调用都需要编写一个HTTP请求,耗时耗力。
//伪代码
url = "http://xxx.com"
req = new Req(参数1,参数2,参数3)
res = httpClient.post(url).body(req).execute()
orderId = res.data.orderId
而通过RPC框架,项目B可以像调用本地方法一样完成调用,一行代码即可解决。
//伪代码
orderId = orderService.order(参数1,参数2,参数3)
二. 简易版RPC框架
1. 设计流程
(1)现在有一个消费者和一个服务提供者。

(2)消费者想要调用服务,需要提供者启动一个web服务,然后消费者通过请求客户端发送HTTP请求或其它协议的请求来调用。比如请求xxx.com/order地址后,就会调用提供者的order方法。

(3) 但如果提供者提供了多个服务和方法,每个服务和方法都要单独写一个接口,消费者想调用的话需要对每个接口都写一段HTTP调用逻辑。效率很低。
因此,可以提供一个统一的服务调用接口,通过请求处理器根据客户端的请求参数来调用不同的方法。同时在服务提供者程序中维护一个本地服务注册器,用于记录服务和对应实现类的映射。
此外,由于Java对象无法直接在网络中传输,因此需要对传递的参数进行序列化和反序列化。
例如:消费者想要调用orderService接口的order方法,发送请求,参数为service=orderService,method=order,然后请求处理器会根据serv从服务注册器中找到对应的服务实现类,并通过Java的反射机制调用method指定的方法。

(4)再进一步,为了简化消费者发送请求的代码,实现类似本地的一行调用。可以基于代理模式,为消费者要调用的接口生成一个代理对象,由代理对象完成请求和响应的过程。
至此,简易版RPC框架完成:

网上的一个关于RPC响应流程的图(参考):

2. 构造初始项目
(1)项目初始化
简易版RPC框架目录:

- common模块包含示例代码的公共依赖,比如接口、Model等。
- consumer模块包含示例消费者代码。
- provider模块包含例服务提供者代码。
- khr-rpc-easy模块是简易版RPC框架。
简易版RPC框架侧重于整个调用流程的实现。
(2)common模块
公共模块需要同时被消费者和服务提供者引入,主要包含和服务相关的接口和数据模型代码。

用户实体类User:
package com.khr.example.common.model;
import java.io.Serializable;
/**
* 用户
*/
// 继承Serializable,用于指示该类实例可以被序列化和反序列化。
// 这意味着User对象可以在程序之间进行持久化存储或在网络通信中传输。
public class User implements Serializable{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
用户接口服务UserService,提供一个获取用户的方法:
package com.khr.example.common.service;
import com.khr.example.common.model.User;
/**
* 用户服务
*/
public interface UserService {
/**
* 获取用户
*
* @param user
* @return
*/
User getUser(User user);
}
(3)provider模块
服务提供者是真正实现了接口的模块。

先引入hutool、lombok依赖。
服务实现类UserServiceImpl:
实现公共模块中定义的用户接口服务UserService,功能是打印用户名称并返回参数中的用户对象。
package com.khr.example.provider;
import com.khr.example.common.model.User;
import com.khr.example.common.service.UserService;
/**
* 用户服务实现类
*/
public class UserServiceImpl implements UserService {
public User getUser(User user){
System.out.println("用户名:"+ user.getName());
return user;
}
}
服务提供者启动类EasyProviderExample:
提供服务的代码之后再补充。
package com.khr.example.provider;
import com.khr.example.common.service.UserService;
import com.khr.krpc.server.HttpServer;
import com.khr.krpc.server.VertxHttpServer;
import com.khr.krpc.registry.LocalRegistry;
/**
* 简易服务提供者示例
*/
public class EasyProviderExample {
public static void main(String[] args){
//提供服务
}
}
(4)consumer模块
消费者模块是需

最低0.47元/天 解锁文章
1248

被折叠的 条评论
为什么被折叠?



