Dubbo的服务降级策略剖析

1 服务降级策略概述

设置Dubbo服务降级策略的主要目的,简单来说就是为了实现对指定服务的请求可以不处理或者简单处理。服务降级策略主要有以下两种:

(1)force:return策略。服务消费端不进行远程调用,直接返回mock值。

mock = "force:return mock值" 

用来屏蔽非核心服务不可用时对调用方的影响。

(2)fail:return策略。当调用远程接口失败的时候,返回mock值,不抛异常。

mock = "fail:return mock值"

用来容忍非核心服务不稳定时对调用方的影响。

服务降级主要用在以下场景:

  • 当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务有策略的降低服务级别,以释放服务器资源,保证核心任务的正常运行。
  • 当服务响应超时或连接请求超时,不用继续等下去,而采用降级措施,以防止分布式服务发生雪崩效应。
  • 在大促销之前通过降级开关关闭推荐、评价等对主流程没有影响的功能。大促销完毕后,再进行恢复。
  • 在秒杀这种流量比较集中并且流量特别大的情况下,因为突发访问量特别大可能会导致系统支撑不了。这个时候可以采用限流来限制访问量,当达到阀值时,后续的请求被降级。

总的来说,服务降级策略主要用在保障核心服务的可用性,防止分布式服务发生雪崩效应,以及在服务器压力剧增或服务响应超时等情况下保证服务的正常运行。

2 设置服务降级策略

设置服务降级策略的主要方式有以下两种:

(1)通过dubbo控制台设置指定服务的降级策略。

(2)在服务消费端设置接口级别或方法级别的服务降级策略。举例如下。

设置接口级别的降级策略:

@Reference(mock = "fail:return null")
private TestService testService;

或者

设置方法级别的降级策略:



    

3 服务降级策略过程剖析

在服务消费端发起远程调用的过程中,服务消费端首先调用MockClusterInvoker的invoker()方法使用服务降级策略。当不是“force:return策略”时,将继续调用DubboInvoker的invoker()方法发起远程调用。

MockClusterInvoker的invoker()具体实现如下所示。

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

    String value = getUrl().getMethodParameter(RpcUtils.getMethodName(invocation), MOCK_KEY, Boolean.FALSE.toString()).trim();
    if (ConfigUtils.isEmpty(value)) {
        //no mock
        result = this.invoker.invoke(invocation);
    } else if (value.startsWith(FORCE_KEY)) {
        if (logger.isWarnEnabled()) {
            logger.warn(CLUSTER_FAILED_MOCK_REQUEST,"force mock","","force-mock: " + RpcUtils.getMethodName(invocation) + " 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(CLUSTER_FAILED_MOCK_REQUEST,"failed to mock invoke","","fail-mock: " + RpcUtils.getMethodName(invocation) + " fail-mock enabled , url : " + getUrl(),e);
            }
            result = doMockInvoke(invocation, e);
        }
    }
    return result;
}


private Result doMockInvoke(Invocation invocation, RpcException e) {
    Result result;
    Invoker mockInvoker;

    RpcInvocation rpcInvocation = (RpcInvocation)invocation;
    rpcInvocation.setInvokeMode(RpcUtils.getInvokeMode(getUrl(),invocation));

    List> mockInvokers = selectMockInvoker(invocation);
    if (CollectionUtils.isEmpty(mockInvokers)) {
        mockInvoker = (Invoker) new MockInvoker(getUrl(), directory.getInterface());
    } else {
        mockInvoker = mockInvokers.get(0);
    }
    try {
        result = mockInvoker.invoke(invocation);
    } catch (RpcException mockException) {
        if (mockException.isBiz()) {
            result = AsyncRpcResult.newDefaultAsyncResult(mockException.getCause(), invocation);
        } else {
            throw new RpcException(mockException.getCode(), getMockExceptionMessage(e, mockException), mockException.getCause());
        }
    } catch (Throwable me) {
        throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
    }
    if (setFutureWhenSync || rpcInvocation.getInvokeMode() != InvokeMode.SYNC) {
        // set server context
        RpcContext.getServiceContext().setFuture(new FutureAdapter<>(((AsyncRpcResult)result).getResponseFuture()));
    }
    return result;
}

4 参考文献

(1)https://www.cnblogs.com/studyjobs/p/16390503.html

(2)https://www.cnblogs.com/xfeiyun/p/16070538.html

你可能感兴趣的:(源码研究-Dubbo,3.2.7,dubbo)