dubbo 服务消费者初始化

本文详细解析了 Dubbo 框架中消费者初始化的过程,包括如何监听注册中心、连接服务提供者以及创建消费端服务代理。此外还介绍了消费端如何缓存远程服务提供者信息,并解释了注册中心在服务发现中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dubbo:reference解析成一个ReferenceBean,它是一个FactoryBean,消费者的初始化在它的init方法中执行,这个方法在两种情况下会被调用:

(1)消费者设置了立即初始(init属性设置成true),那么bean加载时会立即调用消费者初始化。

(2)消费者bean被使用者调用时,调用getObject->get->init。

消费者初始化是主要做的事情就是引用对应的远程服务,执行以步骤:

(1)监听注册中心

(2)连接服务提供端

(3)创建消费端服务代理

监听注册中心

消费者在初始化时也会生成一个registryUrl,消费端的信息:side=consumer、dubbo版本、时间戳、应用名等拼成query串放在registryUrl的refer参数中,消费者初始化时引用远程服务也由Protocol组件来处理,加载自适应的Protocol实现。

com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map<String, String>)

 

if (urls.size() == 1) {
            	invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (int i = 0 ; i < urls.size(); i++) {
                	URL curUrl = urls.get(i);
                	try {
                		invokers.add(refprotocol.refer(interfaceClass, curUrl));
					} catch (Exception e) {
	                    if(i == urls.size() - 1 && registryURL == null){
	                    	throw new IllegalStateException("Unable to connect to zookeeper server, please check socket connection or zookeeper machine.");
	                    }
						// 尝试连接下一个地址
						continue;
					}
                    if (Constants.REGISTRY_PROTOCOL.equals(curUrl.getProtocol())) {
                        registryURL = curUrl; // 优先使用第一个可用的registry url
                        break;
                    }
                }
                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));
                }
            }

 

 

传入的url参数是注册中心URL,然后依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol实现的refer方法。

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    	if(logger.isDebugEnabled()){
    		logger.debug("start RegistryProtocol refer ");
    	}
    	//生成消费者URL
    	//从url中registry中获得注册时使用的协议,默认是dubbo协议,设置url的协议,并移去这个registry参数
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        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);
    

 

 

 

registry协议的url两个wrapper不会进行任何直接进入RegistryProtocol.refer,在这个方法中先把消费者注册到注册中心,zookeeper注册中心由ZookeeperRegistry来处理,在zookeeper服务器上生成一个节点,节点路径是/dubbo/interfaceName/consumers/{consumerUrl},存储了服务消费方ip、group、接口名称、版本、应用名称等。

 

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
//        String ipAddr = NetUtils.getLocalHost();
//        if(!ConfUtil.getParamInEnv("HOST").isEmpty()){
//        	ipAddr = ConfUtil.getParamInEnv("HOST");
//        }
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
//        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, ipAddr, 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)));
        }
        /** 订阅此url*/
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
                Constants.PROVIDERS_CATEGORY 
                + "," + Constants.CONFIGURATORS_CATEGORY 
                + "," + Constants.ROUTERS_CATEGORY));//category (providers,configurators,routers)
        return cluster.join(directory);
    }

 

 

消费端本地会缓存远程服务提供者(每个提供者对应一个Invoker对象)、注册中心配置、路由配置信息。监听注册中心路径是/dubbo/interfaceClass/providers和/dubbo/interfaceClass/configurators、/dubbo/interfaceClass/routers的节点,当提供者、配置、路由信息发生变化之后注册中心会通知消费者刷新本地缓存 。Dubbo框架通过在消费端缓存提供者的信息消除对注册中心的强依赖,即使注册中心挂了服务依然可用。

 

框架为每个接口消费者创建一个RegistryDirectory对象,缓存接口的有的提供端Invoker以及注册中心接口相关的接口配置configurators,服务提供者Invoker保存在RegistryDirectory的methodInvokerMap中,key是方法名称或者,因为大系统中同一个服务一般会由多台服务器提供,所以value是一个Invoker列表存储该服务接口的所有提供者。服务调用时会根据方法名称从这个map中查找提供者对应的Invoker,当providers节点发生变化时通知消费端更新缓存 providers对应的Invoker列表。

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()));
                this.available = true;
                return ;
            }
            this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
            this.urlInvokerMap = newUrlInvokerMap;
            try{
                destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
            }catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

 

接下来要组每一个提供者创建一个对应的Invoker,refreshInvoker方法调用toInvokers,在toInovkers中加载自适应Protocol实现,并且调用它的refer方法,此时url参数是人注册中心获取到的携带提供者信息的providerUrl,根据扩展点加载规则,会依次调用ProtocolListenerWrapper->ProtocolFilterWrapper->DubboProtocol的refer方法,在这两个Wrapper中添加对应的InvokerListener并且构建Invoker-Filter链,最后在DubboProtocol.refer中创建一个DubboInvoker对象,该Invoker对象持有服务class、providerUrl、负责和提供端通信的ExchangeClient、Invoker对象保存在DubboProtocol的invokers集合中。

连接服务提供端

构建DubboInvoker时,会构建一个或多个ExchangeClient用来处理和提供端的连接,默认情况下一下url只会创建一个ExchangeClient负责和对应的提供端建立连接,如果应用方配了多个connections会创建多个,如果lazy属性没有设置成true(默认false),此时ExchangeClient会马上和服务端建立连接,此进组件调用顺序:Exchanger.connect->Transporter.connect->Client,最终会调用Netty框架和提供端建立连接,创建NettyClient对象。

 

 

和NettyServer一样,NettyClient在也会注册IO事件链NettyCodecAdapter.decoder->NettyCodecAdapter.encoder->NettyHandler到Netty框架,分别负责请求响应解码、响应处理。

 

创建消费端服务代理

回到RegistryProtocol.doRefer方法的最后,由Cluster组件来创建一个Invoker并返回,此处的Cluster也是自适应的实现,示例配置的容错模式是failfast,返回扩展链MockClusterWrapper->FailfastCluster,经过扩展链处理创建MockClusterInvoker->FailfastClusterInvoker对象链,FailfastClusterInvoker的directory属性引用上面创建 的RegistryDirectory对象。

返回生成的Invoker实例之后,由ProxyFactory生成一个持有该Invoker实例的代理,代理回调时会激活该Invoker的invoke方法。

该代理会被注册到Spring IO容器中,之后业务中从容器中获取 消费者bean时容器会返回这个代理。

 

转自:http://blog.youkuaiyun.com/tolihaifeng/article/details/60970753

 

最后欢迎大家访问我的个人网站:1024s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值