SpringCloud Alibaba系列——9Dubbo的Mock原理

老老老JR老北
发布于 2022-8-24 16:34
浏览
0收藏

作者 | 一起撸Java
来源 |今日头条

学习目标

  1. Dubbo的mock方式有哪几种
  2. Dubbo的mock原理
    第1章 Mock原理分析
    SpringCloud Alibaba系列——9Dubbo的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);    }}

从源码分析来看,这里是注解获取到了配置的异常类的字符串,然后反射实例化,然后把这个异常直接往上抛了。

分类
已于2022-8-24 16:34:44修改
收藏
回复
举报
回复
    相关推荐