接口Mock
通过给RPC框架支持mock后,开发者就可以轻松调用服务接口、跑通业务流程,不必依赖真实的远程服务,提高使用体验,通过动态代理创建一个调用方法时返回固定值的对象
执行流程
1.加载全局配置并初始化RPC框架
2.注册服务
3.启动HTTP服务器,等待请求
4.通过加载全局配置并初始化rpc框架获取代理对象并调用代理对象的方法,mock为true执行mockserviceproxy代理,mock为false,执行正常代理
5.指定序列化器,构造RPC Request
6.将请求序列化为字节数组,发送HTTP请求
7.等待接收HTTP响应
8.通过web服务器监听并接收consumer的请求
9.经过请求处理器将请求反序列化RPC Request
10.使用本地服务注册器查询 provider 中的对应服务并进行反射调用
11.将 provider 返回的结果构造为RPC Response,并将RPC Response序列化为字节数组
12.rpc发送HTTP响应
13.consumer接收响应
14.consumer将响应反序列化为RPC Request
15.consumer获取响应中的结果,mock为true返回默认结果,为false返回实际结果
1.开发实现
1.1修改RpcConfig
/**
* 模拟调用
*/
private boolean mock = false;
1.2 创建Mock 服务代理(MockServiceProxy)
/**
* Mock 代理
*/
@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 methodReturnType
* @return
*/
private Object getDefaultObject(Class<?> methodReturnType) {
if(methodReturnType.isPrimitive()){
// 基本类型
if(methodReturnType == boolean.class){
return false;
}else if(methodReturnType == short.class){
return (short)0;
}else if(methodReturnType == int.class){
return 0;
}else if(methodReturnType == long.class){
return 0L;
}else if(methodReturnType == float.class){
return 0.0f;
}else if(methodReturnType == double.class){
return 0.0d;
}else if(methodReturnType == byte.class){
return (byte)0;
}else if(methodReturnType == char.class){
return '\u0000';
}else if(methodReturnType == String.class){
return "";
}
}
return null;
}
}
1.3 创建Mock 服务代理工厂(MockServiceProxyFactory)
/**
* Mock 代理工厂
*/
public class MockServiceProxyFactory {
/**
* 根据服务类获取代理对象
*
* @param serviceClass
* @param <T>
* @return
*/
public static <T> T getProxy(Class<T> serviceClass) {
if (RpcApplication.getRpcConfig().isMock()) {
return getMockProxy(serviceClass);
}
return (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass},
new JdkServiceProxy());
}
/**
* 根据服务类获取代理对象
* @param serviceClass
* @return
* @param <T>
*/
public static<T> T getMockProxy(Class<T> serviceClass){
T t = (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass},
new MockServiceProxy());
return t;
}
}
1.4 测试-修改common 接口
/**
* 新方法 - 获取数字
*/
default short getNumber() {
return 1;
}
1.5 测试-修改properties文档
rpc.name=todaysaturdayrpc
rpc.version=2.0
rpc.serverPort=8081
rpc.mock = true
1.6 创建ConsumerExample类
/**
* 服务消费者启动类
*
*/
public class MockConsumerExample {
public static void main( String[] args ){
// 创建静态代理
// UserService userService = new UserServiceProxy();
// 创建JDK动态代理
// UserService userService = JdkServiceProxyFactory.getProxy(UserService.class);
// 创建CGlib动态代理
// UserService userService = CGlibServiceProxyFactory.getProxy(UserService.class);
// 创建Mock代理
UserService userService = MockServiceProxyFactory.getProxy(UserService.class);
User user = new User();
user.setName("todaysaturday");
User newUser = userService.getUser(user);
if (newUser != null) {
System.out.println(newUser.getName());
} else {
System.out.println("user == null");
}
long number = userService.getNumber();
System.out.println(number);
}
}
2.扩展
2.1 引入faker依赖
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
2.2 随机生成
/**
* 生成指定类型的默认值对象
* @param methodReturnType
* @return
*/
private Object getDefaultObject(Class<?> methodReturnType) {
if(methodReturnType.isPrimitive()){
Faker faker = new Faker();
// 基本类型
if(methodReturnType == boolean.class){
return faker.bool().bool();
}else if(methodReturnType == short.class){
return (short)faker.number().numberBetween(1, 1000);
}else if(methodReturnType == int.class){
return faker.number().numberBetween(1, 100000);
}else if(methodReturnType == long.class){
return faker.number().numberBetween(1L, 1000000L);
}else if(methodReturnType == float.class){
return (float) faker.number().randomDouble(5, Long.MIN_VALUE, Long.MAX_VALUE);
}else if(methodReturnType == double.class){
return faker.number().randomDouble(4, 1, 10000);
}else if(methodReturnType == byte.class){
return (byte)faker.number().numberBetween(1, 127);
}else if(methodReturnType == char.class){
return faker.lorem().character();
}else if(methodReturnType == String.class){
return faker.lorem().sentence();
}
}
return null;
}