上一篇文章主要分析了消费者通过 获取代理对象的详细过程,以及对象内部原理,本文将从具体调用出发,一步一步深入Dubbo 调用内部细节。
前期铺垫
博主这些文章对Dubbo 分析中,都是以API 调用为例子进行,而本文分析消费者调用逻辑中,仍然是这样,下面是相应代码:
public static void main(String[] args) {
ReferenceConfig<HelloService> reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig("dubbo-consumer"));
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
reference.setInterface(HelloService.class);
HelloService service = reference.get();
String message = service.hello("dubbo I am anla7856");
System.out.println(message);
}
而上篇文章中,详细的分析了 reference.get()
返回代理对象的构成:
InvokerInvocationHandler
在代理对象中,执行 hello
方法则是执行 proxy0.hello
,即执行 InvokerInvocationHandler
的 invoke
方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
// 判断是否为toString
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
// 判断是否为hashCode
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
// 判断是否为equals
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
// 都不是则往下执行
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
以上方法依次判断了以下几个条件:
- 该方法对应的类如果是
Object
类型,则直接执行该方法。 - 判断是否为
toString
、hashCode
、equals
,是则直接执行invoker
的对应方法 - 构造一个
RpcInvocation
,用于执行invoker
的 invoker 方法,而RpcInvocation
的生层过程就是填充一些比如methodName
,parameterTypes
,arguments
,attachments
,invoker
等信息。此invoker
为MockClusterInvoker
。
为啥此 invoker
为 MockClusterInvoker
,可以看这篇:
Dubbo 消费者中 代理对象 初始化详解
MockClusterInvoker
而后则会执行 MockClusterInvoker
的invoker
方法:
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
// 判断是否配置了 mock
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
// 没有mock 则直接往下调用
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
// 有强制 mock配置,所以直接调用本地mock实现。
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
result = doMockInvoke(invocation, null);
} else {
//当远程调用失败才调用的mock,即一种熔断兜底的功能
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
if (e.isBiz()) {
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
result = doMockInvoke(invocation, e);
}
}
return result;
}
上面 MockClusterInvoker
中的invoker 方法主要是对配置方法中mock
进行 区分性判断。
- 当没有配置mock,则直接往下调用 服务提供者暴露接口。
- 有配置强制mock 的化,那么不进行远端调用,而直接调用本地mock实现
- 有mock,则当调用服务提供者失败时候,会使用本地mock实现兜底
本文将以无mock配置往下继续分析
AbstractClusterInvoker
往后将会执行到AbstractClusterInvoker
的 invoke方法,即在上文代码第一个if
就进入。
public Result invoke(final Invocation invocation) throws RpcException {
// 判断是否destroy
checkWhetherDestroyed();
// 从RpcContext中,查询当前请求有没有携带其他信息
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
// 有则加入进去
((RpcInvocation) invocation).addAttachments(contextAttachments);
}
// 通过Directory 获取到所有可用的invoker
List<Invoker<T>> invokers = list(invocation);
// 初始化LoadBalance 的SPI ,默认使用的是 RandomLoadBanlance
LoadBalance loadbalance = initLoadBalance(invokers, invocation);
// 如果是异步调用,则需要 在Invocation 中增加一个id,用于标识这次Invocation
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
// 执行子类doInvoke方法
return doInvoke(invocation, invokers, loadbalance);
}
在AbstractClusterInvoker
中,主要是做一些准备性工作:
- 判断是否被销毁
- 判断在
RpcContext.getContext()
是否有attachments
,有则需要加入到invocation中 - 从
Directory
中 获取 所有 Invoker,初始化LoadBalance
(负载均衡) - 如果指明是异步调用,则需要记录下该次id做相应处理
- 执行子类的
doInvoker
方法
FailoverClusterInvoker
FailoverClusterInvoker
是一种轮询的集群策略,即当一个调用失败时,会去请求另一个,Dubbo 中默认使用的就是这种集群策略。
现在看看其doInvoke
方法:
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
List<Invoker<T>> copyInvokers = invokers;
// 检查invokers,即检查是否有可用的invokers
checkInvokers(copyInvokers, invocation);
// 获取方法名
String methodName = RpcUtils.getMethodName(invocation);
// 获取重试次数,默认有三次机会,最少有一次。
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
if (len <= 0) {
len = 1;
}
// le 为记录最后一次错误。
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
Set<String> providers = new HashSet<String>(len);
// 循环重试
for (int i = 0; i < len; i++) {
//Reselect before retry to avoid a change of candidate `invokers`.
//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
// 为了保证准确性,除第一次外,每次循环,都需要重新检查下invokers的活性
// 一旦某个invoker失活,会被检测到。
if (i > 0) {
checkWhetherDestroyed();
copyInvokers = list(invocation);
// check again
checkInvokers(copyInvokers, invocation);
}
// 调用负载均衡,选择出某一个invokers
Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
// 将选出的invoker加入,并存储到上下文中。
invoked.add(invoker);
RpcContext.getContext().setInvokers((List) invoked);
try {
// 使用选出的invoker,执行invoke方法。
Result result = invoker.invoke(invocation);
if (le != null && logger.isWarnEnabled()) {
logger.warn("Although retry the method " + methodName
+ " in the service " + getInterface().getName()
+ " was successful by the provider " + invoker.getUrl().getAddress()
+ ", but there have been failed providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost()
+ " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ le.getMessage(), le);
}
return result;
} catch (RpcException e) {
if (e.isBiz()) { // biz exception.
throw e;
}
le = e;
} catch (Throwable e) {
le = new RpcException(e.getMessage(), e);
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException(le.getCode(), "Failed to invoke the method "
+ methodName + " in the service " + getInterface().getName()
+ ". Tried " + len + " times of the providers " + providers
+ " (" + providers.size() + "/" + copyInvokers.size()
+ ") from the registry " + directory.getUrl().getAddress()
+ " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ Version.getVersion() + ". Last error is: "
+ le.getMessage(), le.getCause() != null ? le.getCause() : le);
}
上面代码比较长,总结起来有以下几步:
- 检查 传入的
invokers
活性,并根据其确定重试次数 - 第二次及以后循环,都需要重新检查
invokers
的最新活性,从而保证精准性 - 执行负载均衡逻辑选出invoker,并保存到上下文
- 使用选出的 invoker,执行其invoke方法,并获取返回结果。
invoker.invoke(invocation)
整个就只有这一句了,使用 invoker去执行,包括超时,异步,网络通信都在里面,下面仔细分析。
InvokerWrapper
的invoke
方法
首先进入的是InvokerWrapper
的invoke方法,里面则仅仅包装一层,是直接执行invoker.invoke 方法。
@Override
public Result invoke(Invocation invocation) throws RpcException {
return invoker.invoke(invocation);
}
下面结合 invoker 图来分析。
2. ProtocolFilterWrapper
的 invoke
方法:
@Override
public Result invoke(Invocation invocation) throws RpcException {
// 异步使用filterInvoker 执行invoke
Result asyncResult = filterInvoker.invoke(invocation);
asyncResult.thenApplyWithContext(r -> {
for (int i = filters.size() - 1; i >= 0; i--) {
Filter filter = filters.get(i);
// onResponse callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onResponse(r, filterInvoker, invocation);
}
} else {
filter.onResponse(r, filterInvoker, invocation);
}
}
return r;
});
return asyncResult;
}
ProtocolFilterWrapper
的invoke方法其实逻辑比较复杂,Result asyncResult = filterInvoker.invoke(invocation);
里面执行了ProtocolFilterWraper
中所有相关链式的 Filter
。
主要有哪些Filter
呢?又是如何初始化的呢?
ProtocolFilterWrapper 中 Filter 构建
在 ProtocolFilterWrapper
执行 refer 初始化时,会执行 buildInvokerChain
方法,而 ProtocolFilterWrapper
的 Filter
就是在这里面构建的:
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 获取所有符合要求的Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult;
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return new CallbackRegistrationInvoker<>(last, filters);
}
初始化逻辑也简单,主要关心下有哪些Filter
被选中,并且组件顺序(类加载本就无法保证顺序,但是可以通过order 参数排序),而一大块实现,则只是执行Filter
的invoke
方法
在 dubbo filter中,有以下一些SPI 类:
echo=org.apache.dubbo.rpc.filter.EchoFilter # provider -110000
generic=org.apache.dubbo.rpc.filter.GenericFilter # provider -20000
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter # consumer 20000
token=org.apache.dubbo.rpc.filter.TokenFilter
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter # provider
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter # consumer
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter # provider -30000
context=org.apache.dubbo.rpc.filter.ContextFilter # provider -10000
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter # consumer -10000
exception=org.apache.dubbo.rpc.filter.ExceptionFilter # provider
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter # provider
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter # consumer
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter # provider,consumer
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter # provider
...
上述Filter 中,选中有两个条件:
- 有
@Activate
修饰 - url中有指定,或者
@Activate
中 value 为空的 - 是
group
这边,ProtocolFilterWrapper
主要体现为consumer
- 如果有传入 names,则需要对names进行进一层筛选,此处如果是加入该name对应的spi,如果是default类型,则需要将该SPI 放到最前面。
最终,有三个Filter 脱颖而出,顺序依次是:ConsumerContextFilter
、MonitorFilter
、FutureFilter
(排序顺序,order 越小月往前)
下文
如今已经知道了ProtocolFilterWrapper
里面 filter 逻辑了,下一篇文章则继续验证 ProtocolFilterWrapper
的filter 调用逻辑走。
觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路: