欢迎大家关注 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. 源码分析
整体调用图: 我们直接看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熔断
来解决,这个后面我们详细分析。