设置Dubbo服务降级策略的主要目的,简单来说就是为了实现对指定服务的请求可以不处理或者简单处理。服务降级策略主要有以下两种:
(1)force:return策略。服务消费端不进行远程调用,直接返回mock值。
mock = "force:return mock值"
用来屏蔽非核心服务不可用时对调用方的影响。
(2)fail:return策略。当调用远程接口失败的时候,返回mock值,不抛异常。
mock = "fail:return mock值"
用来容忍非核心服务不稳定时对调用方的影响。
服务降级主要用在以下场景:
总的来说,服务降级策略主要用在保障核心服务的可用性,防止分布式服务发生雪崩效应,以及在服务器压力剧增或服务响应超时等情况下保证服务的正常运行。
设置服务降级策略的主要方式有以下两种:
(1)通过dubbo控制台设置指定服务的降级策略。
(2)在服务消费端设置接口级别或方法级别的服务降级策略。举例如下。
设置接口级别的降级策略:
@Reference(mock = "fail:return null")
private TestService testService;
或者
<dubbo:reference id="testService" interface="com.hn.TestService"
protocol="dubbo" mock="force:return null"/>
设置方法级别的降级策略:
<!-- 对getUserList方法进行降级,其它方法正常调用 -->
<dubbo:reference id="testService" interface="com.hn.TestService" protocol="dubbo">
<dubbo:method name="getUserList" mock="fail:return null"/>
</dubbo:reference>
在服务消费端发起远程调用的过程中,服务消费端首先调用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<T> mockInvoker;
RpcInvocation rpcInvocation = (RpcInvocation)invocation;
rpcInvocation.setInvokeMode(RpcUtils.getInvokeMode(getUrl(),invocation));
List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
if (CollectionUtils.isEmpty(mockInvokers)) {
mockInvoker = (Invoker<T>) 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;
}