【noob-rpc】⑤扩展版RPC-接口Mock
【noob-rpc】⑤扩展版RPC-接口Mock
扩展核心
【1】Mock概念引入:类似模拟数据(针对一些开发场景下无法调通服务的情况,提供一些模拟服务响应数据模拟远程服务行为,便于开发流程调通)
【2】Mock实现:设定mock属性(确认框架是否开启mock模式),通过代理模式构建MockService
需求分析
什么是Mock?
RPC框架的核心功能是调用其他远程服务。但是在实际开发和测试过程中,有时可能无法直接访问真实的远程服务,或者访问真实的远程服务可能会产生不可控的影响,例如网络延迟、服务不稳定等。在这种情况下,就需要使用mock服务来模拟远程服务的行为,以便进行接口的测试、开发和调试。
mock是指模拟对象,通常用于测试代码中,特别是在单元测试中,便于跑通业务流程。
案例分析
以用户服务调用订单服务为例,参考伪代码如下所示。如果订单服务还没上线则流程无法跑通,一般会将调用订单服务的代码注释掉,以便跑通流程。
class UserServiceImp1 {
void test() {
doSomething();
orderService.order():
doSomething();
}
通过mock概念给orderService设定一个模拟对象,调用order方法时随便返回一个值,以便于后续流程执行。虽然mock服务并不是RPC框架的核心能力,但是它的开发成本并不高。而且给RPC框架支持mock后,开发者就可以轻松调用服务接口、跑通业务流程,不必依赖真实的远程服务,提高使用体验,何乐而不为。可以通过最简单的方式(例如一个配置),就让开发者使用mock服务
动态代理可以实现动态创建对象,因此此处可以通过动态代理创建一个调用方法时返回固定值的对象来实现模拟。
实现步骤
构建步骤说明
【1】RpcConfig设定mock字段(是否开启mock)
【2】在proxy包下创建MockServiceProxy类,生成mock代理服务
【3】在proxy包下新增MockServiceProxyFactory代理服务工厂,提供获取mock代理对象的方法getMockProxy
(此处针对mock的动态代理实现,为了避免可service服务相关的代理实现混淆,此处单独将mock抽出来一套)
【4】修改proxy下ServiceProxyFactory代理逻辑,补充验证mock参数以确认是否调用mock服务
1.RpcConfig
@Data
public class RpcConfig {
/**
* 模拟调用
*/
private boolean mock = false;
}
2.MockServiceProxy
提供MockServiceProxy实现InvocationHandler的invoke方法,方法实现根据服务接口类型返回固定值
/**
* Mock 服务代理(JDK 动态代理)
*/
@Slf4j
public class MockServiceProxy implements InvocationHandler {
/**
* 调用代理
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 根据方法的返回值类型,生成特定的默认值对象
Class<?> methodReturnType = method.getReturnType();
log.info("mock invoke {}", method.getName());
return getDefaultObject(methodReturnType);
}
/**
* 生成指定类型的默认值对象(可自行完善默认值逻辑)
* @param type
* @return
*/
private Object getDefaultObject(Class<?> type) {
// 基本类型
if (type.isPrimitive()) {
if (type == boolean.class) {
return false;
} else if (type == short.class) {
return (short) 0;
} else if (type == int.class) {
return 0;
} else if (type == long.class) {
return 0L;
}
}
// 对象类型
return null;
}
}
3.MockServiceProxyFactory
/**
* 服务代理工厂(用于创建代理对象)
*/
public class ServiceProxyFactory {
/**
* 根据服务类获取 Mock 代理对象
* @param serviceClass
* @param <T>
* @return
*/
public static <T> T getMockProxy(Class<T> serviceClass) {
return (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass},
new MockServiceProxy());
}
}
4.ServiceProxyFactory
修改proxy下ServiceProxyFactory代理逻辑,补充验证mock参数以确认是否调用mock服务
/**
* 服务代理工厂(用于创建代理对象)
*/
public class ServiceProxyFactory {
/**
* 根据服务类获取代理对象
* @param serviceClass
* @param <T>
* @return
*/
public static <T> T getProxy(Class<T> serviceClass) {
// 校验配置参数,如果mock为true则走mock代理
if(RpcApplication.getRpcConfig().isMock()){
return MockServiceProxyFactory.getMockProxy(serviceClass);
}
return (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass},
new ServiceProxy());
}
}
5.测试
构建步骤说明
【1】在sample-common的UserService中新增方法测试
【2】服务消费者模块修改测试:修改application.properties配置文件(mock参数为true:rpc.mock=true
)、编写测试类调用userService.getNumber方法
/**
* 用户服务接口
*/
public interface UserService {
/**
* 获取用户信息
* @param user
* @return
*/
User getUser(User user);
/**
* mock测试
*/
default short getNumber(){
return -1;
}
}
【3】启动sample-provider接口服务、随后启动sample-consumer测试类进行测试
/**
* 消费者调用请求
*/
public class CoreConsumerMockSample {
public static void main(String[] args) {
// 动态代理模式
UserService userService = ServiceProxyFactory.getProxy(UserService.class);
// 调用
short number = userService.getNumber();
System.out.println(number);
}
}
可以看到当配置了mock为true,代理服务走MockServiceProxy,因此返回值是0而不是-1
扩展参考
扩展参考
【1】可进一步完善mock的逻辑,支持更多返回类型的默认值生成
可参考Faker之类的伪造数据生成库,生成默认值