13 Mar 2024 |
一、存在的问题
1.重载方法在当前的实现中还不支持,调用了会报错。


2.类型转换也还存在问题。
假设定义的接口如下,参数是float类型。


在Provider端接受到的是一个Double类型,这是因为web应用接收的请求后处理的类型。

在反射调用的时候就会报错。

二、解决方法重载问题
在Provider端创建的时候使用完整的方法签名替换方法全限定名。

方法签名:方法名称+参数个数+参数类型

在Consumer端封装请求参数时,传入方法签名即可。

举个例子:

三、解决参数类型转换
在Provider端进行反射之前,处理请求参数。

processArgs()方法负责处理请求每个请求参数,传入的参数和方法参数类型匹配处理。

同理,在Consumer端需要对返回的结果类型进行参数处理。

参数处理工具类是TypeUtils.cast():
1.兼容的父子类型不需要处理;
2.处理数组类型:是一个什么样的数组,对每个值进行处理;
3.处理Map:使用json序列化;
4.基本类型使用封装类型;

四、各种类型的测试:
1.引用类型参数;
2.int类型参数;
3.重载方法;
4.无参方法;
5.int类型参数,String类型返回值;
6.本地方法;
7.int类型参数,int类型返回值;
8.无参方法,String类型返回值;
9.数组类型返回值;
10.数组参数,数组返回值

工程地址:https://github.com/midnight2104/midnight-rpc/tree/lesson3
11 Mar 2024 |
一、RPC的简化版原理如下图(核心是代理机制)。

1.本地代理存根: Stub
2.本地序列化反序列化
3.网络通信
4.远程序列化反序列化
5.远程服务存根: Skeleton
6.调用实际业务服务
7.原路返回服务结果
8.返回给本地调用方
二、新建一个模块rpc-demo-consumer

主要的依赖是rpc-demo-api,即业务服务定义的api接口,rpc-core是核心依赖包。

三、启动方法
在启动方法中注入业务服务接口UserService和OrderService,使用的注解是@RpcConsumer,本文主要的功能就是如何实现这个注解。
在启动的时候会导入ConsumerConfig的配置类。
使用的ApplicationRunner来测试方法调用。

四、ConsumerConfig用来启动消费者服务。

五、ConsumerBootstrap实现了ApplicationContextAware接口,注入ApplicationContext,即Spring的应用上下文,获取bean。

在start()方法中,
1.获取所有的bean;
2.遍历每个bean,查找bean中是否有字段用了@RpcConsumer注解;
3.对被标记了的@RpcConsumer的字段进行遍历;
4.获取到该字段的类型,全限定名;
5.使用动态代理的方式创建消费者对象;
6.把代理对象赋值给空字段。

查找bean中的字段是否使用了@RpcConsumer注解,通过循环遍历的方式进行判断。不断向父类寻找,是因为bean本身可能被Spring进行了CGLIB增强,形成了一个代理类。

使用Proxy创建代理对象。

六、代理对象RpcInvocationHandler中定义了远程调用方法的逻辑。

在invoke()方法中定义具体实现:1.判断是否本地方法,就不需要走远程调用;
2.封装请求参数;
3.发起远程调用;
4.处理调用结果。

本地方法判断:即Object类中方法不需要被代理,不需要发起远程调用请求。

RpcRequest封装了远程调用消费者端请求的数据结构:接口全限定名称、方法名称、方法参数。

通过post方法发起远程调用,使用的是OkHttpClient。

RpcResponse定义了消费者端的响应体数据结构:状态、响应结果数据、异常对象。

处理调用结果:如果成功,就对调用结果数据进行反序列化;如果失败就封装异常信息。

七、对各种情况的基本测试:
1.类对象参数的远程调用;2.本地方法的远程调用;3.基本参数类型的远程调用;4. String类型参数的远程调用;5. 另一个业务范围(订单服务)的远程调用;6. 嵌套注入的远程调用;7. 异常测试

还有哪些问题?
- 默认使用的Java的动态代理,还有Spring的切面代理,CGLI增强,ByteBuddy的字节码
- 当前还是localhost请求,后续应该是走网络请求。
- 基本类型参数并不完整,如果是Double类型参数可能会报错。
工程地址:https://github.com/midnight2104/midnight-rpc
10 Mar 2024 |
RPC的简化版原理如下图(核心是代理机制)。

1.本地代理存根: Stub
2.本地序列化反序列化
3.网络通信
4.远程序列化反序列化
5.远程服务存根: Skeleton
6.调用实际业务服务
7.原路返回服务结果
8.返回给本地调用方
注意处理异常。
RpcProvider的本地实现
1、工程结构
rpc-core是核心实现类
rpc-demo-api是定义实际业务服务的接口
rpc-demo-provider是业务接口实现类

2、RpcProvider在启动过程中把@RpcProvider标记的方法存到Map中去
在启动时加载配置

在配置中创建启动类

启动类在创建的过程,会将带有@RpcProvider注解的类存放到Map中。
@PostConstruct注解可以在bean的初始化后进行执行,刚好满足我们的常见(存储bean)
使用 ApplicationContextAware接口是为了获取Spring的应用上下文ApplicationContext,从里面获取bean。

RpcProvider是自定义的一个注解,用来表示这个类是一个服务提供者。

一个服务提供者,订单服务实现类。

3、在调用的时候通过方法名称找到应该调用的方法,通过反射完成调用。
发起一个http请求调用,获取订单信息。

RpcRequest封装请求参数,包括:接口名称、方法名、参数。

一个请求入口

请求调用,根据服务全限定名(就是一个类的全名)去map存找对应的类,找到后,通过反射发起方法调用。

响应类RpcResponse统一封装结果,返回状态,响应结果数据。

实际的调用结果,就成功了。

4、还有哪些问题?
如果一个接口有多个实现类怎么办?
错误消息怎么处理?
方法有多个重载方法怎么办?
工程地址:https://github.com/midnight2104/midnight-rpc