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