dubbo的服务调用过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Andyzhu_2005/article/details/84572462

在这里插入图片描述服务消费的过程:referenceConfig类的init方法调用Protocol的refer方法,生成invoker实例,然后把Invoker转换为客户端需要的接口。

2、源码解析

dubbo的消费端初始化在ReferenceConfig的get()方法

 public synchronized T get() {
        if (destroyed){
            throw new IllegalStateException("Already destroyed!");
        }
    	if (ref == null) {
    		init();
    	}
    	return ref;
    }

init()方法做了一系列获取url参数的操作后,进行ref = createProxy(map);获取代理。
在createProxy方法中,根据url是做本地引用,直接根据url的ip和端口号做直接引用,还是通过注册中心获取服务地址,进行引用。这里我们直接看最后一种(这也是最常用的)

	private T createProxy(Map<String, String> map) {
		URL tmpUrl = new URL("temp", "localhost", 0, map);
		final boolean isJvmRefer;
        if (isInjvm() == null) {
            if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用
                isJvmRefer = false;
            } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
                //默认情况下如果本地有服务暴露,则引用本地服务.
                isJvmRefer = true;
            } else {
                isJvmRefer = false;
            }
        } else {
            isJvmRefer = isInjvm().booleanValue();
        }
		
		if (isJvmRefer) {
			URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
			invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
		} else {
            if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (url.getPath() == null || url.getPath().length() == 0) {
                            url = url.setPath(interfaceName);
                        }
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // 通过注册中心配置拼装URL
            	List<URL> us = loadRegistries(false);
            	if (us != null && us.size() > 0) {
                	for (URL u : us) {
                	    URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                	    urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
            	}
            	if (urls == null || urls.size() == 0) {
                    throw new IllegalStateException("No such any registry to reference " + interfaceName  + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                }
            }

            if (urls.size() == 1) {
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // 用了最后一个registry url
                    }
                }
                if (registryURL != null) { // 有 注册中心协议的URL
                    // 对有注册中心的Cluster 只用 AvailableCluster
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); 
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                }  else { // 不是 注册中心的URL
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        Boolean c = check;
        if (c == null && consumer != null) {
            c = consumer.isCheck();
        }
        if (c == null) {
            c = true; // default true
        }
        if (c && ! invoker.isAvailable()) {
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        // 创建服务代理
        return (T) proxyFactory.getProxy(invoker);
    }

上面代码中
List us = loadRegistries(false);获取到注册中心的地址

[registry://192.168.25.128:2181/com.alibaba.dubbo.registry.RegistryService?application=dubboTest-test-web&dubbo=2.5.3&pid=10628&registry=zookeeper&timestamp=1543316713611]

然后进入 invoker = refprotocol.refer(interfaceClass, urls.get(0));获取invoker。
这里refprotocol是
Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
需要根据url的名称来加载对应的spi扩展,找到真正的类。根据url,是register,所以会定位到com.alibaba.dubbo.registry.integration.RegistryDirectory类。找到类的refer方法:

	public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
	
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        //根据url找到注册中心,这里用的是zookeeper注册中心
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
        	return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0 ) {
            if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
                    || "*".equals( group ) ) {
                return doRefer( getMergeableCluster(), registry, type, url );
            }
        }
        return doRefer(cluster, registry, type, url);
    }

上面方法中,找到注册中心后,调用doRefer(cluster, registry, type, url),返回invoker

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
                Constants.PROVIDERS_CATEGORY 
                + "," + Constants.CONFIGURATORS_CATEGORY 
                + "," + Constants.ROUTERS_CATEGORY));
        return cluster.join(directory);
    }

上面代码中,subscribeUrl 的值是:

consumer://192.168.86.1/cn.andy.dubbo.DataService?application=dubboTest-test-web&check=false&connections=5&dubbo=2.5.3&interface=cn.andy.dubbo.DataService&methods=dubboTest2,dubboTest,getStringData&pid=10628&retries=0&revision=0.0.1-SNAPSHOT&side=consumer&timestamp=1543316675743

这个url就是向注册中心注册的消费端的地址。其中注册中心的地址分为永久地址和临时地址。在这个例子中,永久地址就是cn.andy.dubbo.DataService,其下分为provider和consumer以及monitor等。其中,服务提供端的ip地址和端口号作为临时地址放在provider下,消费端的ip地址和端口号放在consumer下。同时,消费端会对provider下的地址进行订阅,当其有变化时,会通知消费端,这样,消费端和服务端就对应起来了。

上面代码中directory可以理解为服务端invoker的list集合。
RegistryDirectory directory = new RegistryDirectory(type, url);
里面的成员
private volatile Map<String, Invoker> urlInvokerMap,存储了服务端的invoker集合。
direct通过订阅注册中心的相关路径:
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ “,” + Constants.CONFIGURATORS_CATEGORY
+ “,” + Constants.ROUTERS_CATEGORY));
当服务端的invoker集合发生变化,或者路由,配置等发生变化时,都会通知这个director。以invoker发生变化为例(即服务端的提供者数量发生变化)。

    public void subscribe(URL url) {
       setConsumerUrl(url);
       registry.subscribe(url, this);
   }

里面的this,就是RegistryDirectory,那么查看RegistryDirectory的notify方法:


       List<URL> invokerUrls = new ArrayList<URL>();
       List<URL> routerUrls = new ArrayList<URL>();
       List<URL> configuratorUrls = new ArrayList<URL>();
       for (URL url : urls) {
           String protocol = url.getProtocol();
           String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
           if (Constants.ROUTERS_CATEGORY.equals(category) 
                   || Constants.ROUTE_PROTOCOL.equals(protocol)) {
               routerUrls.add(url);
           } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) 
                   || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
               configuratorUrls.add(url);
           } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
               invokerUrls.add(url);
           } else {
               logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
           }
       }
       // configurators 
       if (configuratorUrls != null && configuratorUrls.size() >0 ){
           this.configurators = toConfigurators(configuratorUrls);
       }
       // routers
       if (routerUrls != null && routerUrls.size() >0 ){
           List<Router> routers = toRouters(routerUrls);
           if(routers != null){ // null - do nothing
               setRouters(routers);
           }
       }
       List<Configurator> localConfigurators = this.configurators; // local reference
       // 合并override参数
       this.overrideDirectoryUrl = directoryUrl;
       if (localConfigurators != null && localConfigurators.size() > 0) {
           for (Configurator configurator : localConfigurators) {
               this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
           }
       }
       // providers,根据url刷新invoker列表
       refreshInvoker(invokerUrls);
   
   /**
    * 根据invokerURL列表转换为invoker列表。转换规则如下:
    * 1.如果url已经被转换为invoker,则不在重新引用,直接从缓存中获取,注意如果url中任何一个参数变更也会重新引用
    * 2.如果传入的invoker列表不为空,则表示最新的invoker列表
    * 3.如果传入的invokerUrl列表是空,则表示只是下发的override规则或route规则,需要重新交叉对比,决定是否需要重新引用。
    * @param invokerUrls 传入的参数不能为null
    */
   private void refreshInvoker(List<URL> invokerUrls){
       if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
               && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
           this.forbidden = true; // 禁止访问
           this.methodInvokerMap = null; // 置空列表
           destroyAllInvokers(); // 关闭所有Invoker
       } else {
           this.forbidden = false; // 允许访问
           Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
           if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
               invokerUrls.addAll(this.cachedInvokerUrls);
           } else {
               this.cachedInvokerUrls = new HashSet<URL>();
               this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
           }
           if (invokerUrls.size() ==0 ){
           	return;
           }
           Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
           Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
           // state change
           //如果计算错误,则不进行处理.
           if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
               logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
               return ;
           }
           this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
           this.urlInvokerMap = newUrlInvokerMap;
           try{
               destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
           }catch (Exception e) {
               logger.warn("destroyUnusedInvokers error. ", e);
           }
       }
   }

生成directory之后,通过return cluster.join(directory)返回invoker。这里的cluster可以是failoverCluster、failfastCluster等。在我们后面消费端进行方法调用时,cluster会根据规则将invoker集合返回一个invoker给消费端。
在上面代码中,查看 Map<String, Invoker> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表。在这个方法中,消费端打开netty客户端。

    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
        if(urls == null || urls.size() == 0){
            return newUrlInvokerMap;
        }
        Set<String> keys = new HashSet<String>();
        String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
        for (URL providerUrl : urls) {
        	//如果reference端配置了protocol,则只选择匹配的protocol
        	if (queryProtocols != null && queryProtocols.length() >0) {
        		boolean accept = false;
        		String[] acceptProtocols = queryProtocols.split(",");
        		for (String acceptProtocol : acceptProtocols) {
        			if (providerUrl.getProtocol().equals(acceptProtocol)) {
        				accept = true;
        				break;
        			}
        		}
        		if (!accept) {
        			continue;
        		}
        	}
            if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
                continue;
            }
            if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
                logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() + " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost() 
                        + ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
                continue;
            }
            URL url = mergeUrl(providerUrl);
            
            String key = url.toFullString(); // URL参数是排序的
            if (keys.contains(key)) { // 重复URL
                continue;
            }
            keys.add(key);
            // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
            Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            if (invoker == null) { // 缓存中没有,重新refer
                try {
                	boolean enabled = true;
                	if (url.hasParameter(Constants.DISABLED_KEY)) {
                		enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
                	} else {
                		enabled = url.getParameter(Constants.ENABLED_KEY, true);
                	}
                	if (enabled) {
                		invoker = new InvokerDelegete<T>(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) { // 将新的引用放入缓存
                    newUrlInvokerMap.put(key, invoker);
                }
            }else {
                newUrlInvokerMap.put(key, invoker);
            }
        }
        keys.clear();
        return newUrlInvokerMap;
    }

上面的 URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // URL参数是排序的
是将privoderurl和消费端的url合并,相当于有多少个provider,就有多少个key(因为消费端只有一个),其格式如下:

dubbo://192.168.86.1:20880/cn.andy.dubbo.DataService?anyhost=true&application=dubboTest-test-web&check=false&connections=5&dispatcher=all&dubbo=2.5.3&interface=cn.andy.dubbo.DataService&methods=dubboTest2,dubboTest,getStringData&mock=true&pid=55972&retries=0&revision=0.0.1-SNAPSHOT&service.filter=andyFilter&side=consumer&timeout=60000&timestamp=1543369485194&token=1234567

前面的是服务提供端的ip和端口号等,但是后面的side=consumer。相当于url是providerurl合并了消费端的参数。
然后根据这个url,生成invoker
invoker = new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);
其中,serviceType=interface cn.andy.dubbo.DataService,url就是上面的key。
在protocol.refer(serviceType, url)中完成netty的客户端打开。
而requestHandler 作为一个channel,是netty的处理channel。

    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
        
        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                Invocation inv = (Invocation) message;
                Invoker<?> invoker = getInvoker(channel, inv);
                //如果是callback 需要处理高版本调用低版本的问题
                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
                    String methodsStr = invoker.getUrl().getParameters().get("methods");
                    boolean hasMethod = false;
                    if (methodsStr == null || methodsStr.indexOf(",") == -1){
                        hasMethod = inv.getMethodName().equals(methodsStr);
                    } else {
                        String[] methods = methodsStr.split(",");
                        for (String method : methods){
                            if (inv.getMethodName().equals(method)){
                                hasMethod = true;
                                break;
                            }
                        }
                    }
                    if (!hasMethod){
                        logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );
                        return null;
                    }
                }
                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
                return invoker.invoke(inv);
            }
            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
        }

猜你喜欢

转载自blog.csdn.net/Andyzhu_2005/article/details/84572462