学习目标
-
Dubbo的mock方式有哪几种
-
Dubbo的mock原理
第1章 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方法,代码如下:
@Override
public 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方法
@Override
public 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-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);
}
从上面逻辑来看,会先进行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的方式获取实例,这里就不再赘述
@Override
public <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);
}
}
从源码分析来看,这里是注解获取到了配置的异常类的字符串,然后反射实例化,然后把这个异常直接往上抛了。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76700.html