手动实现简易版RPC(三)
往期内容
-
手动实现简易版RPC(一):RPC简介及系统架构
-
手动实现简易版RPC(二):简单RPC框架实现
前言
接上两篇博客我们实现了最简易RPC框架,接下来的几期重点在简易版的rpc框架上继续深耕。本文主要介绍简易版RPC 中mock假数据相关的内容。
RPC(Remote Procedure Call)框架的核心在于通过网络实现远程过程调用,使得调用远程服务如同调用本地服务一样方便。但是在实际开发中,可能在调用远程过程中不会特别的顺利,会遇到一些不可控的问题,比如网络不通?服务不稳定等不可预测的问题。
那么为了保证我们这边的开发顺利性,可能会使用mock这一服务来模拟远程返回的数据,进行更好的业务联调。
什么是mock?
简单介绍一下mock
"MOCK"通常指的是创建一个模拟的对象或功能,以替代实际的对象或功能,从而进行测试或其他操作。这样做的主要目的是隔离测试环境,使其只关注被测试的部分,而不受其他依赖项的影响。通过使用MOCK,开发者或测试人员可以模拟外部系统、数据库、网络请求或其他难以直接控制的依赖项,从而更容易地控制测试环境,确保测试的准确性和可靠性。
举个🌰
public class CatServiceImpl {
void test(){
doSomething();
CatService.getCat();
doSomething();
}
}
在上述代码中,如果CatService.getCat()
尚未开发完成或者由于一些不可控原因调不通,那么在测试这段代码的时候,整个流程是走不通的,只能注释掉CatService.getCat()
这个方法。而mock的作用是能够模拟出一个CatService并调用它的getCat()方法。这样一来的话,可以比较顺了的跑通整个流程。
为什么支持mock?
虽然 mock 服务并不是 RPC 框架的核心能力,但是它的开发成本并不高。而且给 RPC 框架支持 mock后,开发者就可以轻松调用服务接口、跑通业务流程,不必依赖真实的远程服务,提高使用体验,何乐而不为呢?
我们希望能够用最简单的方式 – 比如一个配置,就让开发者使用 mock 服务。
设计方案
上文也讲到了mock旨在创建一个模拟对象,供调用方使用。
但是怎么调用产生一个模拟对象呢?
仔细想想,是不是可以通过动态代理的方式,产生一个对象,在调用方调用该对象的时候,返回一个固定的返回值,这样是不是就🆗了?
具体实现
1- 我们实现mock是可配置的,通过读取配置信息来获取用户是否开启mock。在之前的RpcConf中添加一个mock属性
....
/***
* 是否开启模拟数据模式
* 默认不开启mock
*/
private boolean mock = false;
2-在proxy包中添加MockServiceProxy类,用于生成mock模拟服务。其中通过自定义方法getDefaultObject
根据方法返回值的类型进行默认值的返回,比如返回值类型为 int的方法,默认返回0,返回值为boolean的方法返回值默认为false。
/**
* @version 1.0
* @Author jerryLau
* @Date 2024/4/12 15:42
* @注释 MockServiceProxy 类
* 采用JDK动态代理
* 开启Mock服务代理
*/
@Slf4j
public class MockServiceProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> returnType = method.getReturnType();
log.info("MockServiceProxy invoke method: {}, returnType: {}", method.getName(), returnType.getName());
return getDefaultObject(returnType);
}
/***
* 根据返回类型返回默认值
* @param returnType
* @return
*/
public Object getDefaultObject(Class<?> returnType) {
log.info("MockServiceProxy getDefaultObject returnType: {}", returnType.getName());
if (returnType == int.class) {
return 0;
} else if (returnType == long.class) {
return 0L;
} else if (returnType == double.class) { // double 类型默认值是 0.0
return 0.0;
} else if (returnType == boolean.class) {
return false; // boolean 类型默认值是 false
} else if (returnType == Short.class) {
return (short) 0;
}
//对象返回null
return null;
}
}
3-在代理工厂类ServiceProxyFactory
中添加关于mock的判定以及返回一个mock代理对象,通过获取配置管理器中用户是否开启了mock,如果开启了mock,则返回一个mock代理对象。
//配置管理器中是否开启Mock模式
if (RPCGlobalConfHolder.getRpcConfig().isMock()) {
return (T) getMockProxy(serviceClass);
}
/***
* get mock proxy
* @param serviceClass
* @return
* @param <T>
*/
public static <T> T getMockProxy(Class<T> serviceClass) {
return (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass}
, new MockServiceProxy()
);
}
简单测试
上述过程简单实现了mock的自定义配置,接下来我们可以简单的进行测试。
1-将example-common
模块中的获取cat接口中添加一个默认方法getCatCount
,用于获取猫的数量
/**
* 获取猫咪数量
* @return
*/
default int getCatCount() {
return 100;
}
在默认不开启mock的情况下,在example-consumer
下EasyConsumer
简单消费者中调用获取猫咪数量的方法
在默认不开启mock的时候,可以看到获取到的猫咪数量是默认的数量100
修改application.yml文件 配置mock属性为 true,开启mock
RPC:
name: "ape-rpc1"
serverPort: 8080
version: "1.0.0"
serverHost: "localhost233"
mock: true
在此运行消费者程序,发现获取到的猫咪的数量是0,而不是100,说明mock生效。
至此,我们实现了简易版的PRC框架中的mock功能
码字不易,希望大家能够一键三连🌝⭐🌟
代码仓库 ape-rpc: 轮子项目,手动实现rpc github🌐 || ape-rpc: 轮子项目,手动实现rpc gitee🌐