星辰计划5-深入解析源码(逐行debug)dubbo服务引用,调用

dubbo服务引用

ReferenceConfig的创建初始化之路

构造函数 发现并没有啥特别的

 

java

代码解读

复制代码

public ReferenceConfig() { super(); this.repository = ApplicationModel.getServiceRepository(); }

但是发现这个实例初始化的时候需要构造这些静态属性对象

  1. 协议
  2. 集群(这个没用上不知道为啥不去掉)
  3. 代理工厂 构造代理类使用

来 我们继续跟踪debug

可以看到这一步 我们是要开始创建代理实例了。

来我们继续分析他是如何创建代理类的

 

java

代码解读

复制代码

private T createProxy(Map<String, String> map) { if (shouldJvmRefer(map)) { ..... } else { ..... if (urls.size() == 1) { invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0)); } else { ..... } } ..... // create service proxy return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic)); }

可以看到他调用了协议的引用方法获取到了一个invoker,然后再生成invoker的代理。

Invoker是dubbo 非常重要的实体,他是dubbo非常核心的模型,其他模型可以向他靠拢,或者转换成它,它代表一个可执行体,可以发起Invoke调用 他有集群Invoker (ClusterInvoker) 本地Invoker,远程 Invoker

Protocol也是Dubbo是非常重要的实体 它负责服务的暴露和引用,它管理着 **Invoker的生命周期 **

REF_PROTOCOL这个是AdaptiveExtension,他可以根据传参动态选择Protocol实现 那么他目前是哪一个实现呢?或者说他的源头Protocol实现是哪一个呢?来我们继续debug 看看他到底是啥东西?

我发现 dubbo spi加载的时候去获取所有Protocol的实现 有非常多,那到底是哪个呢?

 

plain

代码解读

复制代码

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper mock=org.apache.dubbo.rpc.support.MockProtocol dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol http=org.apache.dubbo.rpc.protocol.http.HttpProtocol rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol webservice=org.apache.dubbo.rpc.protocol.webservice.WebServiceProtocol thrift=org.apache.dubbo.rpc.protocol.thrift.ThriftProtocol native-thrift=org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol memcached=org.apache.dubbo.rpc.protocol.memcached.MemcachedProtocol redis=org.apache.dubbo.rpc.protocol.redis.RedisProtocol rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol xmlrpc=org.apache.dubbo.xml.rpc.protocol.xmlrpc.XmlRpcProtocol grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

首先我们来看 urls.get(0) 这个拿到的url是啥?

可以看到这个url是注册中心的地址

所以当dubbo spi加载的时候应该加载的是 这个

 

plain

代码解读

复制代码

registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol

因为这个url是注册协议 ,所以会带上 getExtend("registry"),可以通过 org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generateExtNameAssignment 这个来发现

getNameCode = String.format("( url.getProtocol() == null ? "%s" : url.getProtocol() )", defaultExtName);

 

java

代码解读

复制代码

private String generateExtNameAssignment(String[] value, boolean hasInvocation) { // TODO: refactor it String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if (i == value.length - 1) { if (null != defaultExtName) { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); } } else { getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\")", value[i]); } } else { getNameCode = "url.getProtocol()"; } } } else { if (!"protocol".equals(value[i])) { if (hasInvocation) { getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); } else { getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); } } else { getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } } return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode); }

通过debug看 果然是这样子的

而且到这一步的时候我发现 dubbo spi 再包装一下 他的包装实现

那他怎么判断是否是 包装实现呢?

  1. 是否有包含这个类型的的构造函数

例如 QosProtocolWrapper是 Protocol的实现,且他的构造函数包含Protocol

 

plain

代码解读

复制代码

public QosProtocolWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; }

2.@Wrapper 注解修饰

根据顺序 可以看到 最终这个Protocol实例是 QosProtocolWrapper,所以他是我们所有Protocol的调用入口。

来我们继续debug org.apache.dubbo.registry.integration.RegistryProtocol#refer

可以发现 这家伙又包装了一个 MigrationInvoker 无限套娃啊

这个MigrationInvoker 是一个迁移Invoker是负责根据迁移规则,迁移服务使用的。

 

java

代码解读

复制代码

protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url, Map<String, String> parameters) { URL consumerUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters); ClusterInvoker<T> migrationInvoker = getMigrationInvoker(this, cluster, registry, type, url, consumerUrl); return interceptInvoker(migrationInvoker, url, consumerUrl); }

我们可以看下他的Invoker实现

最终这个 还是调用了 org.apache.dubbo.registry.integration.RegistryProtocol#getInvoker 来获取Invoker来赋值给 currentAvailableInvoker

来我们继续debug org.apache.dubbo.registry.integration.RegistryProtocol#getInvoker

可以看到是 Cluster 构建了一个 ClusterInvoker。并且 Cluster最顶层是一个包装Cluster(MockClusterInvoker),最基础的还是默认的FailoverCluster,并且 FailoverCluster会构建一个 FailoverClusterInvoker,到此我们发现这个Invoker就到底了。

这个时候我们再理一理 这个完整的Protocol refer链路

再理一理 Invoker的链路

所以ReferenceConfig 最终拿到的这个MigrationInvoker,再进行代理的。我们可以debug来验证一下。可以看到非常正确啊。不容易啊

dubbo服务调用invoker

聪明的朋友们是不是发现了什么 感觉不对啊 我们dubbo协议的最终的协议 Invoker呢?是不是少了点什么?

我们看下他的实现

 

java

代码解读

复制代码

@Override public Result invoke(final Invocation invocation) throws RpcException { checkWhetherDestroyed(); // binding attachments into invocation. Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments(); if (contextAttachments != null && contextAttachments.size() != 0) { ((RpcInvocation) invocation).addObjectAttachments(contextAttachments); } //获取所有可用的invoker列表 List<Invoker<T>> invokers = list(invocation); //通过负载均衡来获取到最终的一个Invoker最终调用invoke 进行rpc调用 LoadBalance loadbalance = initLoadBalance(invokers, invocation); RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation); return doInvoke(invocation, invokers, loadbalance); }

那如何进行获取所有可用的Invoker列表呢?

那当然是根据注册中心啊,去注册中心获取可用的服务提供者列表。

来来来我们继续跟踪一下 debug FailoverClusterInvoker

可以看到最终经过了Directory=>到达了 RouterChain ,RouterChain他有一个Invokers,那他这个Invokers是从哪里来的呢?我发现他下面有个 org.apache.dubbo.rpc.cluster.RouterChain#setInvokers,通过这个来设置的

并且我们可以看到 这个 Invokers里面的Invoker 可以看到 我们心心念念的DubboInvoker,他也是包装了很多层的Invoker,

来来来我们在org.apache.dubbo.rpc.cluster.RouterChain#setInvokers 这里打个断点看看 他是如何设置值的。

可以看到有几个重要节点

1.创建ClusterInvoker时候,通过订阅注册中心,然后注册中心通知我们有哪些可用列表。

 

java

代码解读

复制代码

protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) { directory.setRegistry(registry); directory.setProtocol(protocol); // all attributes of REFER_KEY Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters()); URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters); if (directory.isShouldRegister()) { directory.setRegisteredConsumerUrl(urlToRegistry); registry.register(directory.getRegisteredConsumerUrl()); } directory.buildRouterChain(urlToRegistry); //服务订阅 directory.subscribe(toSubscribeUrl(urlToRegistry)); return (ClusterInvoker<T>) cluster.join(directory); }

org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe 来们来看看这个方法

 

java

代码解读

复制代码

@Override public void doSubscribe(final URL url, final NotifyListener listener) { try { if (ANY_VALUE.equals(url.getServiceInterface())) { ....... } else { CountDownLatch latch = new CountDownLatch(1); List<URL> urls = new ArrayList<>(); for (String path : toCategoriesPath(url)) { ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>()); //创建zk监听这个监听实现最终也会调用 notify(url, listener, urls); ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, k, latch)); if (zkListener instanceof RegistryChildListenerImpl) { ((RegistryChildListenerImpl) zkListener).setLatch(latch); } zkClient.create(path, false); List<String> children = zkClient.addChildListener(path, zkListener); //发现子节点不为空 那么说明已有可用服务 if (children != null) { urls.addAll(toUrlsWithEmpty(url, path, children)); } } notify(url, listener, urls); // tells the listener to run only after the sync notification of main thread finishes. latch.countDown(); } } catch (Throwable e) { throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } }

两个重要信息

1.创建监听,如果有服务更新 就通知服务刷新

2.如果常见监听的时候发现已有可用列表那么就直接通知 可用服务列表

来来来 我们继续追踪我们的invoker

根据我们上面的debug截图 我们跳转 debug 到了 org.apache.dubbo.registry.integration.RegistryDirectory#refreshInvoker

 

java

代码解读

复制代码

private void refreshInvoker(List<URL> invokerUrls) { Assert.notNull(invokerUrls, "invokerUrls should not be null"); if (invokerUrls.size() == 1 && invokerUrls.get(0) != null && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { ........ } else { ...... //将注册中心获取到的可用列表转成Invoker Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map ....... List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values())); // pre-route and build cache, notice that route cache should build on original Invoker list. // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed. //将转成的Invoker 写入到 routeChain routerChain.setInvokers(newInvokers); ...... } ....................... }

至此我们完全看到了 routeChain setInvokers的链路。来我们继续看下 url 可用列表转成Invoker的实现

 

java

代码解读

复制代码

private Map<URL, Invoker<T>> toInvokers(List<URL> urls) { Map<URL, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<>(); if (CollectionUtils.isEmpty(urls)) { return newUrlInvokerMap; } Set<URL> keys = new HashSet<>(); String queryProtocols = this.queryMap.get(PROTOCOL_KEY); for (URL providerUrl : urls) { .......... URL url = mergeUrl(providerUrl); if (keys.contains(url)) { // Repeated url continue; } keys.add(url); // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(url); if (invoker == null) { // Not in the cache, refer again try { boolean enabled = true; if (url.hasParameter(DISABLED_KEY)) { enabled = !url.getParameter(DISABLED_KEY, false); } else { enabled = url.getParameter(ENABLED_KEY, true); } if (enabled) { //可以看到这行 就会发现 这个这个又引用了一次 这个应该是引用DubboInvoker了吧 我们debug验证一下 invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl); } } catch (Throwable t) { logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t); } if (invoker != null) { // Put new invoker in cache newUrlInvokerMap.put(url, invoker); } } else { newUrlInvokerMap.put(url, invoker); } } keys.clear(); return newUrlInvokerMap; }

可以看到这个是dubbo协议,同样的他也会经过 那三个ProtocolWrapper

QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper

并且ProtocolFilterWrapper 会为Invoker构建一个FilterChain,ProtocolListenerWrapper 会 为Invoker包装一个Listener

最终会调用到DubboProtocol,通过调用DubboProtocol.refer

可以看到 DubboProtocol没有refer实现,所以他是调用了父类的refer,可以看到 AsyncToSyncInvoker 这个异步同步调用Invoker ,并且 DubboProtocol实现了 protocolBindingRefer 来返回 DubboInvoker,至此我们终于梳理出来的链路。

 

java

代码解读

复制代码

@Override public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException { optimizeSerialization(url); // create rpc invoker. DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); invokers.add(invoker); return invoker; }

所以最终形成一个这样的Invoker链 ,来我们来总结一下。

总结一下

简单总结一下 Invoker和Protocol是非常重要的Dubbo模型,他们计划贯穿了dubbo非常核心的服务暴露和引用。Protocol是Dubbo是非常重要的实体 它负责服务的暴露和引用,它管理着 Invoker的生命周期,Invoker是dubbo 非常重要的实体,他是dubbo非常核心的模型,其他模型可以向他靠拢,或者转换成它,它代表一个可执行体,可以发起Invoke调用 他有集群Invoker (ClusterInvoker) 本地Invoker,远程 Invoker

再附上两个的核心图

Protocol链路

Invoker链路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值