Dubbo源码篇5-消费端服务mock和服务降级策略分析

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. 服务降级策略常见设置

  • mock=”force:return null” 表示消费方对该服务的方法调用都直接强制性返回 null 值,不发起远程调用,即使远程的提供者没有出现问题。不过需要注意,这里的 return null,并不一定必须是 null,可以是任意想看到的内容。 用来屏蔽不重要服务。
  • mock=”fail:return null” 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。不过需要注意,这里的 return null,并不一定必须是 null,可以是任意想看到的内容。例如, 123456,例如某个字符串等。 用来容忍不重要服务不稳定时的影响。
  • mock=”true”表示消费方对该服务的方法调用在失败后,会调用消费方定义的服务降级Mock 类实例的相应方法。而该 Mock 类的类名为“业务接口名+Mock”,且放在与接口相同的包中。
  • mock=降级类的全限定性类名 与 mock=”true”功能类似,不同的是,该方式中的降级类名可以是任意名称,在任何包中。

举例:

<dubbo:reference version="1.0.0" group="greeting" id="greetingService" check="false"
                 interface="org.apache.dubbo.demo.GreetingService" mock = "org.apache.dubbo.demo.GreetingServiceMock">
</dubbo:reference>

2. 源码分析

整体调用图: image.png 我们直接看org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker#invoke:

public Result invoke(Invocation invocation) throws RpcException {
    Result result = null;

    // 获取mock属性值
    String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();

    // 若没有设置mock属性,或mock属性设置为false,则不进行服务降级
    if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
        //no mock  远程调用
        result = this.invoker.invoke(invocation);

    // 若mock属性值以force开头,则进行强制降级(无论是否有可用的invoker,都不管,直接降级)
    } else if (value.startsWith("force")) {
        if (logger.isWarnEnabled()) {
            logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
        }
        //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;
}

doMockInvoke就是直接降级的逻辑:

private Result doMockInvoke(Invocation invocation, RpcException e) {
    Result result = null;
    Invoker<T> minvoker;

    // 获取最新的invoker列表
    List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
    if (CollectionUtils.isEmpty(mockInvokers)) {
        // 若仍没有可用的invoker,则创建一个降级invoker
        minvoker = (Invoker<T>) new MockInvoker(getUrl(), directory.getInterface());
    } else {
        // 若存在可用的invoker,则直接获取第一个可用的invoker
        minvoker = mockInvokers.get(0);
    }
    try {
        // 若minvoker为可用invoker,那么这里的调用就是正常的远程调用
        // 否则这里走的是降级处理
        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;
}

org.apache.dubbo.rpc.support.MockInvoker#invoke:

public Result invoke(Invocation invocation) throws RpcException {
    if (invocation instanceof RpcInvocation) {
        ((RpcInvocation) invocation).setInvoker(this);
    }
    String mock = null;
    // 获取<dubbo:method/>中的mock
    if (getUrl().hasMethodParameter(invocation.getMethodName())) {
        mock = getUrl().getParameter(invocation.getMethodName() + "." + MOCK_KEY);
    }

    // 若没有,则获取<dubbo:reference/>中的mock
    if (StringUtils.isBlank(mock)) {
        mock = getUrl().getParameter(MOCK_KEY);
    }

    // 若仍没有,则直接抛出异常
    if (StringUtils.isBlank(mock)) {
        throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
    }

    // 规范化mock取值
    mock = normalizeMock(URL.decode(mock));

    // 处理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);
            // 将结果封装为异步结果
            return AsyncRpcResult.newDefaultAsyncResult(value, invocation);
        } catch (Exception ew) {
            throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
                    + ", mock:" + mock + ", url: " + url, ew);
        }
    } 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  调用本地降级类
        try {
            Invoker<T> invoker = getInvoker(mock);
            return invoker.invoke(invocation);
        } catch (Throwable t) {
            throw new RpcException("Failed to create mock implementation class " + mock, t);
        }
    }
}
public static String normalizeMock(String mock) {
    if (mock == null) {
        return mock;
    }

    mock = mock.trim();

    if (mock.length() == 0) {
        return mock;
    }

    // 若mock为return,则返回returnnull
    if (RETURN_KEY.equalsIgnoreCase(mock)) {
        return RETURN_PREFIX + "null";
    }

    if (ConfigUtils.isDefault(mock) || "fail".equalsIgnoreCase(mock) || "force".equalsIgnoreCase(mock)) {
        return "default";
    }

    // 若mock以fail:开头,则mock取其子串:将前面的fail:去掉
    if (mock.startsWith(FAIL_PREFIX)) {
        mock = mock.substring(FAIL_PREFIX.length()).trim();
    }

    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;
}

private Invoker<T> getInvoker(String mockService) {
    // 从缓存map中获取invoker
    Invoker<T> invoker = (Invoker<T>) MOCK_MAP.get(mockService);
    if (invoker != null) {
        return invoker;
    }

    // 获取业务接口class
    Class<T> serviceType = (Class<T>) ReflectUtils.forName(url.getServiceInterface());
    // 创建本地降级类实例
    T mockObject = (T) getMockObject(mockService, serviceType);
    // 将实例封装为invoker
    invoker = PROXY_FACTORY.getInvoker(mockObject, serviceType, url);
    if (MOCK_MAP.size() < 10000) {
        MOCK_MAP.put(mockService, invoker);
    }
    return invoker;
}

getInvoker()获取invoker对象,然后执行Invoker.invoke()方法直接到降级类中的方法: 如下org.apache.dubbo.demo.GreetingServiceMock:

public class GreetingServiceMock implements GreetingService {
    @Override
    public String hello() {
        return "This is mock result";
    }
}

接下来就可以执行,降级类处理逻辑。

但是这里有一个问题,如果提供者对应的方法出现问题,那么消费者一直都会去远程调用提供者,失败后才走降级逻辑,这样会耗费大量的资源。这种问题可以使用Sentinel熔断来解决,这个后面我们详细分析。

参考文章

Dubbo3.0源码注释github地址
dubbo源码系列
dubbo源码分析专栏

猜你喜欢

转载自juejin.im/post/7118922294147481630