SpringCloud Alibaba系列——9Dubbo的Mock原理
作者 | 一起撸Java
来源 |今日头条
学习目标
- Dubbo的mock方式有哪几种
- Dubbo的mock原理
第1章 Mock原理分析
1.1 功能描述
在前面的高级用法中我们讲过mock的使用方式以及作用,在这里不再赘述,这里针对mock的集中配置形式来分析一下mock的实现原理。
1.2 mock = "force:return eclipse2019"
1.2.1 使用场景
这种一般不会出现在代码配置中,一般都是在服务治理的时候进行配置的,如果指定后端接口有问题了,可以在dubbo-admin中配置该接口对应的某方法进行强制降级。
1.2.2 源码分析
有关mock的实现逻辑全部都在MockClusterInvoker中,当我们用代理对象调用的时候,代码会走到代理的advice类,也就是InvokerInvocationHandler中,然后走到MigrationInvoker,然后走到了MockClusterInvoker当中,调到了其invoke方法,代码如下:
@Overridepublic Result invoke(Invocation invocation) throws RpcException { Result result = null; //获取url中的mock参数 String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim(); if (value.length() == 0 || "false".equalsIgnoreCase(value)) { //no mock //如果没mock则直接走后面调用逻辑 result = this.invoker.invoke(invocation); } else if (value.startsWith("force")) { //如果是force开头 if (logger.isWarnEnabled()) { logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled, url : " + getUrl()); } //强制降级,调用mock的实现类逻辑 //force:direct mock result = doMockInvoke(invocation, null); } else { //fail-mock try { result = this.invoker.invoke(invocation); //fix:#4585 if(result.getException() != null && result.getException() instanceof RpcException){ RpcException rpcException= (RpcException)result.getException(); if(rpcException.isBiz()){ throw rpcException; }else { result = doMockInvoke(invocation, rpcException); } } } catch (RpcException e) { if (e.isBiz()) { throw e; } if (logger.isWarnEnabled()) { logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + getUrl(), e); } result = doMockInvoke(invocation, e); } } return result;}
从上述代码我们可以看到,MockClusterInvoker当中就是走了三套逻辑:
1、没有配置mock的情况
2、mock="force:"的情况
3、配置了mock的其他情况
我们都知道如果配置了force:就代表要进行强制降级,就不会走后端的rpc调用了,所以这里是直接调用到了
result = doMockInvoke(invocation, null);
private Result doMockInvoke(Invocation invocation, RpcException e) { Result result = null; Invoker<T> minvoker; //选择一个MockInvoker的实例,这里是选不到的 List<Invoker<T>> mockInvokers = selectMockInvoker(invocation); if (CollectionUtils.isEmpty(mockInvokers)) { //所以代码会走这里,创建一个MockInvoker对象 minvoker = (Invoker<T>) new MockInvoker(getUrl(), directory.getInterface()); } else { minvoker = mockInvokers.get(0); } try { //调用mock的实现类方法 result = minvoker.invoke(invocation); } catch (RpcException me) { if (me.isBiz()) { result = AsyncRpcResult.newDefaultAsyncResult(me.getCause(), invocation); } else { throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause()); } } catch (Throwable me) { throw new RpcException(getMockExceptionMessage(e, me), me.getCause()); } return result;}
我们再看看mockInvoker的invoke方法
@Overridepublic Result invoke(Invocation invocation) throws RpcException { if (invocation instanceof RpcInvocation) { ((RpcInvocation) invocation).setInvoker(this); } String mock = null; if (getUrl().hasMethodParameter(invocation.getMethodName())) { mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY); } if (StringUtils.isBlank(mock)) { //获取配置的mock的属性值 mock = getUrl().getParameter(MOCK_KEY); } if (StringUtils.isBlank(mock)) { throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url)); } //把force:前缀去掉,获取后面的值 mock = normalizeMock(URL.decode(mock)); //如果是return开头 if (mock.startsWith(RETURN_PREFIX)) { //获取return后面的值 mock = mock.substring(RETURN_PREFIX.length()).trim(); try { //获取返回值类型 Type[] returnTypes = RpcUtils.getReturnTypes(invocation); //把return后面的值包装成返回值类型 Object value = parseMockValue(mock, returnTypes); //注解把结果返回没走后端rpc调用 return AsyncRpcResult.newDefaultAsyncResult(value, invocation); } catch (Exception ew) { throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: " + url, ew); } //如果是throw } else if (mock.startsWith(THROW_PREFIX)) { mock = mock.substring(THROW_PREFIX.length()).trim(); if (StringUtils.isBlank(mock)) { throw new RpcException("mocked exception for service degradation."); } else { // user customized class //获取异常实例 Throwable t = getThrowable(mock); //直接往上抛异常 throw new RpcException(RpcException.BIZ_EXCEPTION, t); } } else { //impl mock //mock实现类的方式 try { Invoker<T> invoker = getInvoker(mock); //调用mock实例 return invoker.invoke(invocation); } catch (Throwable t) { throw new RpcException("Failed to create mock implementation class " + mock,t); } }}
我们看一下normalizeMock方法
public static String normalizeMock(String mock) { if (mock == null) { return mock; } mock = mock.trim(); if (mock.length() == 0) { return mock; } //如果是只有一个return 则加上一个return null if (RETURN_KEY.equalsIgnoreCase(mock)) { return RETURN_PREFIX + "null"; } if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock) || "force".equalsIgnoreCase(mock)) { return "default"; } if (mock.startsWith(FAIL_PREFIX)) { mock = mock.substring(FAIL_PREFIX.length()).trim(); } //把force:去掉 if (mock.startsWith(FORCE_PREFIX)) { mock = mock.substring(FORCE_PREFIX.length()).trim(); } if (mock.startsWith(RETURN_PREFIX) || mock.startsWith(THROW_PREFIX)) { mock = mock.replace('`', '"'); } return mock;}
我们获取到mock的返回值内容后需要把该返回值包装成方法返回值类型,所以这里必须要有一个返回值类型的包装,我们看一下parseMockValue方法:
public static Object parseMockValue(String mock, Type[] returnTypes) throws Exception { Object value = null; if ("empty".equals(mock)) { value = ReflectUtils.getEmptyObject(returnTypes != null && returnTypes.length > 0 ? (Class<?>) returnTypes[0] : null); } else if ("null".equals(mock)) { value = null; } else if ("true".equals(mock)) { value = true; } else if ("false".equals(mock)) { value = false; } else if (mock.length() >= 2 && (mock.startsWith("\"") && mock.endsWith("\"") || mock.startsWith("\'") && mock.endsWith("\'"))) { value = mock.subSequence(1, mock.length() - 1); } else if (returnTypes != null && returnTypes.length > 0 && returnTypes[0] == String.class) { value = mock; } else if (StringUtils.isNumeric(mock, false)) { value = JSON.parse(mock); } else if (mock.startsWith("{")) { value = JSON.parseObject(mock, Map.class); } else if (mock.startsWith("[")) { value = JSON.parseObject(mock, List.class); } else { value = mock; } if (ArrayUtils.isNotEmpty(returnTypes)) { value = PojoUtils.realize(value, (Class<?>) returnTypes[0], returnTypes.length > 1 ? returnTypes[1] : null); } return value;}
从上面的逻辑来看,如果是force:return 则会不走rpc直接返回一个结果,然后把这个结果包装成方法的返回值类型。
1.3 mock = "true"
1.3.1 使用场景
如果配置的是mock="true"就是一种约定俗成的方式,那么这种方式就代表会走远程调用,然后远程调用如果出现了RpcException的时候就会掉到降级逻辑,这个降级逻辑的定义必须满足两点:
1、类名必须是接口名+"Mock"
2、类必须定义在接口的同包名下
配置如下:
@DubboReference(check = false,mock = "true")
1.3.2 源码分析
在MockClusterInvoker中就会走这个逻辑:
//fail-mocktry { //后端接口调用 result = this.invoker.invoke(invocation); //fix:#4585 if(result.getException() != null && result.getException() instanceof RpcException){ RpcException rpcException= (RpcException)result.getException(); if(rpcException.isBiz()){ throw rpcException; }else { result = doMockInvoke(invocation, rpcException); } }} catch (RpcException e) { if (e.isBiz()) { throw e; } if (logger.isWarnEnabled()) { logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url: " + getUrl(), e); } result = doMockInvoke(invocation, e);}
从上面逻辑来看,会先进行invoke调用,如果后端调用有问题则会被catch捕获然后走doMockInvoke逻辑进行降级处理。我们来看看doMockInvoke中的逻辑;在MockInvoker中就会走到实现类mock逻辑;
//mock实现类的方式try { //获取Invoker对象 Invoker<T> invoker = getInvoker(mock); //调用mock实例 return invoker.invoke(invocation);} catch (Throwable t) { throw new RpcException("Failed to create mock implementation class " + mock, t);}
重点看一下getInvoker方法:
private Invoker<T> getInvoker(String mockService) { Invoker<T> invoker = (Invoker<T>) MOCK_MAP.get(mockService); if (invoker != null) { return invoker; } //接口类型 Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface()); //核心代码 ,获取Mock实现类实例 T mockObject = (T) getMockObject(mockService, serviceType); //获取用于调用Mock实例类的invoker对象 invoker = PROXY_FACTORY.getInvoker(mockObject, serviceType, url); if (MOCK_MAP.size() < 10000) { MOCK_MAP.put(mockService, invoker); } return invoker;}
getMockObject逻辑
public static Object getMockObject(String mockService, Class serviceType) { //如果配置的是true或者default ,则走默认mock实现类 boolean isDefault = ConfigUtils.isDefault(mockService); if (isDefault) { //默认实现类就是 : 接口完整限定名+Mock mockService = serviceType.getName() + "Mock"; } Class<?> mockClass; try { // 如果 mockService不是配置的true,如果是配置的类的完整限定名 mockClass = ReflectUtils.forName(mockService); } catch (Exception e) { if (!isDefault) {// does not check Spring bean if it is default config. ExtensionFactory extensionFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension(); Object obj = extensionFactory.getExtension(serviceType, mockService); if (obj != null) { return obj; } } throw new IllegalStateException("Did not find mock class or instance " + mockService + ", please check if there's mock class or instance implementing interface " + serviceType.getName(), e); } if (mockClass == null || !serviceType.isAssignableFrom(mockClass)) { throw new IllegalStateException("The mock class " + mockClass.getName() + " not implement interface " + serviceType.getName()); } try { return mockClass.newInstance(); } catch (InstantiationException e) { throw new IllegalStateException("No default constructor from mock class " + mockClass.getName(), e); } catch (IllegalAccessException e) { throw new IllegalStateException(e); }}
从上面的代码我们可以看到,获取到实现类的实例的方式就是两种:
1、如果配置的是true,实现类就是:接口名+"Mock"
2、直接配置的类的完整限定名
在看看获取invoker的代码:
invoker = PROXY_FACTORY.getInvoker(mockObject, serviceType, url);
这个代码最终会走到JavassistProxyFactory类的getInvoker方法中,spi的方式获取实例,这里就不再赘述
@Overridepublic <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO Wrapper cannot handle this scenario correctly: the classname contains '$' final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type); return new AbstractProxyInvoker<T>(proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable { return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); } };}
这里是先生成了一个代理类wrapper,这个代理类中有一个invokeMethod方法,只要你传给他要掉的类的实例,方法名称,参数类型和参数列表就可以完成方法的调用,然后AbstractProxyInvoker持有了wrapper的引用,我们如果需要调用一个类的方法,只有用invoker对象调用invoke方法就可以了。这里为什么能调到MockServiceMock就不再赘述了。
1.4 mock = "xx.LocalMockService"
1.4.1 使用场景
同mock=“true”
配置:
@DubboReference(check = false,mock = "cn.enjoy.mock.LocalMockService")
1.4.2 源码分析
同mock=“true”
1.5 mock = "throw xx"
1.5.1 使用场景
当调用接口出现问题后需要直接抛出异常的情况可以实现它
配置如下:
@DubboReference(check = false,mock = "throw java.lang.RuntimeException")
1.5.2 源码分析
else if (mock.startsWith(THROW_PREFIX)) { mock = mock.substring(THROW_PREFIX.length()).trim(); if (StringUtils.isBlank(mock)) { throw new RpcException("mocked exception for service degradation."); } else { // user customized class //获取异常实例 Throwable t = getThrowable(mock); //直接往上抛异常 throw new RpcException(RpcException.BIZ_EXCEPTION, t); }}
public static Throwable getThrowable(String throwstr) { Throwable throwable = THROWABLE_MAP.get(throwstr); if (throwable != null) { return throwable; } try { Throwable t; //反射异常类 Class<?> bizException = ReflectUtils.forName(throwstr); Constructor<?> constructor; constructor = ReflectUtils.findConstructor(bizException, String.class); //异常实例化 t = (Throwable) constructor.newInstance(new Object[]{"mocked exception for service degradation."}); if (THROWABLE_MAP.size() < 1000) { THROWABLE_MAP.put(throwstr, t); } return t; } catch (Exception e) { throw new RpcException("mock throw error :" + throwstr + " argument error.", e); }}
从源码分析来看,这里是注解获取到了配置的异常类的字符串,然后反射实例化,然后把这个异常直接往上抛了。