三Dubbo服务暴露源码分析--3远程暴露-下

三Dubbo服务暴露源码分析--3远程暴露-下

//PTC2.2.2 DubboProtocol.serverMap.put(key, createServer(url))
DubboProtocol
    private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>

    private void openServer(URL url) {
        //192.168.0.101:20880
        String key = url.getAddress();
        //client 也可以暴露一个只有server可以调用的服务。
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
        if (isServer) {
        	ExchangeServer server = serverMap.get(key);
        	if (server == null) {
//PTC2.2.1 createServer 创建ExchangeServer
//PTC2.2.2 DubboProtocol.serverMap.put(key, createServer(url))
        		serverMap.put(key, createServer(url));
        	} else {
        		//server支持reset,配合override功能使用
        		server.reset(url);
        	}
        }
    }

在//PTC2.2.1 createServer 创建ExchangeServer,返回HeaderExchangeServer后,会将其存入serverMap中。

image-20221220094601718

此时,在DubboProtocol中有两个map,分别存储exchangeServer的服务端,和暴露的服务dubboExporter。

//EXP3.1.4 ExporterChangeableWrapper

然后,回到这里

RegistryProtocol    
  //本地缓存,记录providerURL暴露的exporter服务--避免暴露过的服务,不再重新暴露
      private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();

private <T> ExporterChangeableWrapper<T>  doLocalExport(final Invoker<T> originInvoker){
//EXP3.1.1 getCacheKey(获取invoker在bounds中缓存的key)
        String key = getCacheKey(originInvoker);
  		//然后获取bounds内缓存的exporter---避免重复暴露
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
  
/**………………………………………………………………RegistryProtocol中bounds,记录是否暴露过服务………………………………………………………………………………………… */
        if (exporter == null) {
            synchronized (bounds) {//加锁
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                if (exporter == null) {
//EXP3.1.2 InvokerDelegete
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
//EXP3.1.3 protocol.export
//EXP3.1.4 ExporterChangeableWrapper
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return (ExporterChangeableWrapper<T>) exporter;
    }

在//EXP3.1.3 protocol.export中暴露的exporter

image-20221220095630108

然后,此处创建的ExporterChangeableWrapper,是在protocol.export暴露服务exporter成功后,创建exporter的代理类,用来建立originInvoker与protocol.export出的exporter的对应关系。在override时候,可以进行关系修改。

   RegistryProtocol的内部类
   private class ExporterChangeableWrapper<T> implements Exporter<T>{
   			//绑定origin与根据该originInvoker暴露出服务exporter的对应关系,在override协议进行change时,修改对应关系
        private Exporter<T> exporter;
        private final Invoker<T> originInvoker;

        public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker){
            this.exporter = exporter;
            this.originInvoker = originInvoker;
        }
        
        public Invoker<T> getOriginInvoker() {
            return originInvoker;
        }
				//
        public Invoker<T> getInvoker() {
            return exporter.getInvoker();
        }

image-20221220100546042

其中,originInvoker为注册服务registry://的invoker,exporter为被listenerwrapper包过的DubboExporter。

在RegistryProtocol中的bounds,记录providerurl与暴露出的服务exporter,避免被重复暴露。

//用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
    //providerurl <--> exporter
    private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();

image-20221220103222648

/** …………向注册中心注册服务…………………………*/
RegistryProtocol
 
 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//EXP3.1 doLocalExport
  			//对服务dubbo://的暴露,并打开端口等操作
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);

//EXP3.2 getRegistry
  			//根据originInvoker的注册中心zookeeper://,加载registryService的实现类--zookeeperRegistry
        final Registry registry = getRegistry(originInvoker);

  			//获取注册的服务提供者的URL,即dubbo://
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//EXP3.3 registry.register
  			//将provider信息,注册到注册中心
        registry.register(registedProviderUrl);
  
/**…………………………………………………………………………………………………………override信息、overrideListener的订阅……………………………………………………………………………… */
        // 订阅override数据
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
  		//获取订阅的override数据
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
  		//创建override监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
  		//向注册中心订阅override
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
  
  
        //保证每次export都返回一个新的exporter实例(其包裹的exporter是EXP3.1doLocalExport中暴露的服务exporter)
        return new Exporter<T>() {
            public Invoker<T> getInvoker() {
                return exporter.getInvoker();
            }
            public void unexport() {
            	try {
            		exporter.unexport();
            	} catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	registry.unregister(registedProviderUrl);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	overrideListeners.remove(overrideSubscribeUrl);
                	registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
            }
        };
    }
EXP3.2 getRegistry--创建zookeeperRegistry
//EXP3.2 getRegistry
  			//根据originInvoker的注册中心zookeeper://,加载registryService的实现类--zookeeperRegistry
        final Registry registry = getRegistry(originInvoker);

  			//获取注册的服务提供者的URL,即dubbo://
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//EXP3.3 registry.register
  			//将provider信息,注册到注册中心
        registry.register(registedProviderUrl);

此时,originInvoker实例,是registry://localhost:2181,向注册中心registry注册。

image-20221220105526814

RegistryProtocol
    private Registry getRegistry(final Invoker<?> originInvoker){
  //registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D89785%26side%3Dprovider%26timestamp%3D1671504677853&organization=dubbox&owner=programmer&pid=89785&registry=zookeeper&timestamp=1671504677724
        URL registryUrl = originInvoker.getUrl();
        if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
          //registry注册中心协议为zookeeper
            String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
          //将注册URL协议头更换为zookeeper
            registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
        }
//EXP3.2.1 RegistryFactory$Adpative.getRegistry
        return registryFactory.getRegistry(registryUrl);
    }

上述过程,将registryURL的registry://协议头,更换为zookeeper://,并从registryFactory工厂类中获取zookeeperRegistry注册中心。

//EXP3.2.1 RegistryFactory$Adpative.getRegistry
public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {
    public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
      //zookeeper
        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
      //extension为zookeeperRegistryFactory
        return extension.getRegistry(arg0);
    }
}
AbstractRegistryFactory
    public Registry getRegistry(URL url) {
  	//将registryurl的parameter参数对中export部分(保存dubbo://编码值)删除,然后向其中添加interface为RegistryService
    	url = url.setPath(RegistryService.class.getName())
    			.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
    			.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
  	//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService
    	String key = url.toServiceString();
        // 锁定注册中心获取过程,保证注册中心单一实例
        LOCK.lock();
        try {
            Registry registry = REGISTRIES.get(key);
            if (registry != null) {
                return registry;
            }
/**………………………………………………………………………………………………创建zookeeperRegistry注册中心………………………………………………………………………………………………………… */
//EXP3.2.1.1 createRegistry
            registry = createRegistry(url);
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
          //将注册中心放入缓存
            REGISTRIES.put(key, registry);
            return registry;
        } finally {
            // 释放锁
            LOCK.unlock();
        }
    }
//EXP3.2.1.1 createRegistry

createRegistry由子类实现。

public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
	//dubbo的@SPI自动注入
	private ZookeeperTransporter zookeeperTransporter;

    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
		this.zookeeperTransporter = zookeeperTransporter;
	}

	public Registry createRegistry(URL url) {
//ZKR ZookeeperRegistry
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }

}
//ZKR ZookeeperRegistry初始化

ZookeeperRegistry实现与zookeeper注册中心的交互逻辑。

public class ZookeeperRegistry extends FailbackRegistry {
  	//配置zookeeper客户端端口和根路径
    private final static int DEFAULT_ZOOKEEPER_PORT = 2181;
    private final static String DEFAULT_ROOT = "dubbo";
		//zookeeper上配置的监听器NotifyListener、ChildListener
    private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>();
    
    private final ZookeeperClient zkClient;
    
    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
      	//zookeeper://localhost:2181……………………
//ZKR1 FailbackRegistry
        super(url);
        if (url.isAnyHost()) {
    		throw new IllegalStateException("registry address == null");
    	}
      //group值为dubbo
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (! group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
      	//设置zookeeper存储中的根路径=/dubbo
        this.root = group;
//ZKR2 zookeeperTransporter$Adpative.connect(url) 连接并创建zkclient
        zkClient = zookeeperTransporter.connect(url);
//ZKR3 zkClient.addStateListener 添加监听器
        zkClient.addStateListener(new StateListener() { //2
            public void stateChanged(int state) { //3
            	if (state == RECONNECTED) { //4
	            	try {
						recover();
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
            	}//4
            } //3 
        }); //2
      
    }
//ZKR1 FailbackRegistry--失败重试

FailbackRegistry实现失败重试功能。

// 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
    private final ScheduledFuture<?> retryFuture;
/**……………………………………………………………………………………失败操作的集合………………………………………………………………………………………………………… */
    private final Set<URL> failedRegistered = new ConcurrentHashSet<URL>();//注册失败
    private final Set<URL> failedUnregistered = new ConcurrentHashSet<URL>();//取消注册失败
    private final ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();//订阅失败
    private final ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();//取消订阅失败
    private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, List<URL>>>();//通知失败


public FailbackRegistry(URL url) {
//ZKR1.1 AbstractRegistry
        super(url);
        int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
      //failback的registry,使用定时线程池ScheduledExecutorService,定时执行重试retry连接注册中心
        this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                // 检测并连接注册中心
                try {
/**………………………………………………………………失败重试中,根据失败操作集合中,遍历各个集合,如果有就重新操作……………………………………………………………………………… */
                    retry();
                } catch (Throwable t) { // 防御性容错
                    logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
                }
            }
        }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
    }
//ZKR1.1 AbstractRegistry--缓存

AbstractRegistry实现缓存机制。有内存服务缓存、磁盘文件缓存。

Registry实现需要实现缓存的原因:

1 consumer调用服务,如果每次都要从注册中心实时查询可用服务列表,会增大注册中心的处理压力,其次会增加额外的网络请求,会使系统性能下降;

2 使系统不强依赖于注册中心,就是在注册中心宕机时,仍旧能正常的调用服务。

因此,在AbstractRegistry中提供了连个缓存的服务------内存缓存服务、磁盘文件缓存(注册中心重启,会是内存缓存消失,因此再增加文件缓存)。

/**……………………………………………………………………………………………………磁盘文件缓存…………………………………………………………………………………………………………………………………… */
    // 本地磁盘缓存文件
    private File file;
    // 本地磁盘缓存文件的缓存对象,在初始化时,file中数据读入properties中,后续写入,也先写入该properties中,在定时写入file文件中
		//key(服务接口名称):value(服务url列表)
    private final Properties properties = new Properties();
/**……………………………………………………………………………………………………内存服务缓存…………………………………………………………………………………………………………………………………… */
 private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>(); 
//存储的内容为(zookeeper://,map(providers/routers/configurators/consumers,List(服务URL) ) )
//URL为注册中心的地址

注意缓存文件的写入时机。

    //registry中保存当前注册中心的地址
		//zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=90630&timestamp=1671506059060
		private URL registryUrl;

/**……………………………………………………………………………………………………磁盘文件缓存…………………………………………………………………………………………………………………………………… */
    // 本地磁盘缓存文件
    private File file;
    // 本地磁盘缓存,其中特殊的key值.registies记录注册中心列表,其它均为notified服务提供者列表
    private final Properties properties = new Properties();
    // 文件缓存定时写入的线程
    private final ExecutorService registryCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveRegistryCache", true));
    //是否是同步保存文件
    private final boolean syncSaveFile ;
    
/**……………………………………………………………………………………………………内存服务缓存…………………………………………………………………………………………………………………………………… */
 private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();  

    private final AtomicLong lastCacheChanged = new AtomicLong();
    private final Set<URL> registered = new ConcurrentHashSet<URL>();
		//订阅url的notifyListener的set集合
    private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
 
//初始化
public AbstractRegistry(URL url) {
        setUrl(url);
        // 启动文件保存定时器
        syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
  			//本地缓存文件的全路径名称:/Users/app/.dubbo/dubbo-registry-localhost.cache
        String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
        File file = null;
  			//创建本地缓存文件
        if (ConfigUtils.isNotEmpty(filename)) {
            file = new File(filename);
            if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
                if(! file.getParentFile().mkdirs()){
                    throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
                }
            }
        }
        this.file = file;
//ZKR1.1.1 loadProperties
        loadProperties();
//ZKR1.1.2 AbstractRegistry.notify
        notify(url.getBackupUrls());
    }

AbstractRegistry的初始化过程中,负责创建缓存文件(内存缓存和文件缓存),并将文件缓存加载到当前类的properteis的键值对对象中。

//ZKR1.1.1 loadProperties

加载文件缓存内容到properties的键值对对象中。

  private void loadProperties() {
        if (file != null && file.exists()) {
            InputStream in = null;
            try {
                in = new FileInputStream(file);
             //加载入properties
                properties.load(in);
                if (logger.isInfoEnabled()) {
                    logger.info("Load registry store file " + file + ", data: " + properties);
                }
            } catch (Throwable e) {
                logger.warn("Failed to load registry store file " + file, e);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
            }
        }
    }
//ZKR1.1.2 AbstractRegistry.notify---???consumer端???

image-20221220163247258

这里不执行通知(consumer端,在订阅时候,会在AbstractRegistry中注册NotifyListener,并加入subscribed中)

    protected void notify(List<URL> urls) {
        if(urls == null || urls.isEmpty()) return;
        //遍历    private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>(); 这里是保存url与订阅关注该url的notifyListener监听器的set集合
      //这里尚未添加NotifyListener通知监听器,所以不处理
        for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
            URL url = entry.getKey();//被关注的url
            //传入的url匹配,看是否与abstractRegistry中保存的url匹配、相同
            if(! UrlUtils.isMatch(url, urls.get(0))) {
                continue;
            }
            
            Set<NotifyListener> listeners = entry.getValue();
            if (listeners != null) {
                for (NotifyListener listener : listeners) {
                    try {
                      //通知
                        notify(url, listener, filterEmpty(url, urls));
                    } catch (Throwable t) {
                        logger.error("Failed to notify registry event, urls: " +  urls + ", cause: " + t.getMessage(), t);
                    }
                }
            }
        }
    }
//ZKR2 zookeeperTransporter$Adpative.connect(url) 连接并创建zkclient
public class ZookeeperTransporter$Adpative implements com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter {
    public com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient connect(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
      //客户端为zkclient
        String extName = url.getParameter("client", url.getParameter("transporter", "zkclient"));
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter) name from url(" + url.toString() + ") use keys([client, transporter])");
        com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter extension = (com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter.class).getExtension(extName);
      //ZkclientZookeeperTransporter
        return extension.connect(arg0);
    }
public class ZkclientZookeeperTransporter implements ZookeeperTransporter {

	public ZookeeperClient connect(URL url) {
//ZKR2.1 ZkclientZookeeperClient
		return new ZkclientZookeeperClient(url);
	}
//ZKR2.1 ZkclientZookeeperClient初始化
	public ZkclientZookeeperClient(URL url) {
    //zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=2163&timestamp=1671526419537
		super(url);
//ZKR2.1.1 ZkClient
    //localhost:2181,客户端连接
		client = new ZkClient(url.getBackupAddress());
//ZKR2.1.2 client.subscribeStateChanges
		client.subscribeStateChanges(new IZkStateListener() {
			public void handleStateChanged(KeeperState state) throws Exception {
				ZkclientZookeeperClient.this.state = state;
				if (state == KeeperState.Disconnected) {
					stateChanged(StateListener.DISCONNECTED);
				} else if (state == KeeperState.SyncConnected) {
					stateChanged(StateListener.CONNECTED);
				}
			}
			public void handleNewSession() throws Exception {
				stateChanged(StateListener.RECONNECTED);
			}
		});
	}
//ZKR2.1.1 ZkClient--连接zk并设置当前zkclient为watcher
   public ZkClient(final IZkConnection zkConnection, final int connectionTimeout, final ZkSerializer zkSerializer, final long operationRetryTimeout) {
        if (zkConnection == null) {
            throw new NullPointerException("Zookeeper connection is null!");
        }
        _connection = zkConnection;
        _zkSerializer = zkSerializer;
        _operationRetryTimeoutInMillis = operationRetryTimeout;
        _isZkSaslEnabled = isZkSaslEnabled();
     //连接zkserver,并将当前zkclient当做watcher
        connect(connectionTimeout, this);
    }
    public void connect(final long maxMsToWaitUntilConnected, Watcher watcher) throws ZkInterruptedException, ZkTimeoutException, IllegalStateException {
        boolean started = false;
        acquireEventLock();
        try {
            setShutdownTrigger(false);
            _eventThread = new ZkEventThread(_connection.getServers());
            _eventThread.start();
          //zk客户端连接zkserver
            _connection.connect(watcher);

            LOG.debug("Awaiting connection to Zookeeper server");
            boolean waitSuccessful = waitUntilConnected(maxMsToWaitUntilConnected, TimeUnit.MILLISECONDS);
            if (!waitSuccessful) {
                throw new ZkTimeoutException("Unable to connect to zookeeper server '" + _connection.getServers() + "' with timeout of " + maxMsToWaitUntilConnected + " ms");
            }
            started = true;
        } finally {
            getEventLock().unlock();

            // we should close the zookeeper instance, otherwise it would keep
            // on trying to connect
            if (!started) {
                close();
            }
        }
    }
    
       @Override
    public void connect(Watcher watcher) {
        _zookeeperLock.lock();
        try {
            if (_zk != null) {
                throw new IllegalStateException("zk client has already been started");
            }
            try {
                LOG.debug("Creating new ZookKeeper instance to connect to " + _servers + ".");
              //创建zookeeper,在初始化中,执行this.cnxn.start(),打开zookeeper的客户端连接zkserver
                _zk = new ZooKeeper(_servers, _sessionTimeOut, watcher);
            } catch (IOException e) {
                throw new ZkException("Unable to connect to " + _servers, e);
            }
        } finally {
            _zookeeperLock.unlock();
        }
    }
//ZKR2.1.2 client.subscribeStateChanges
	public ZkclientZookeeperClient(URL url) {
//ZKR2.1.1 ZkClient
    //localhost:2181,客户端连接
		client = new ZkClient(url.getBackupAddress());
//ZKR2.1.2 client.subscribeStateChanges
		client.subscribeStateChanges(new IZkStateListener() {
      //这个监听器,监听zkclient客户单的连接状态
			public void handleStateChanged(KeeperState state) throws Exception {
				ZkclientZookeeperClient.this.state = state;
				if (state == KeeperState.Disconnected) {
					stateChanged(StateListener.DISCONNECTED);
				} else if (state == KeeperState.SyncConnected) {
					stateChanged(StateListener.CONNECTED);
				}
			}
			public void handleNewSession() throws Exception {
				stateChanged(StateListener.RECONNECTED);
			}
		});
//ZKR3 zkClient.addStateListener 添加监听器

返回到ZookeeperRegistry的初始化过程。

 public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
      	//zookeeper://localhost:2181……………………
//ZKR1 FailbackRegistry
        super(url);
        if (url.isAnyHost()) {
    		throw new IllegalStateException("registry address == null");
    	}
      //group值为dubbo
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (! group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
      	//设置zookeeper存储中的根路径=/dubbo
        this.root = group;
//ZKR2 zookeeperTransporter$Adpative.connect(url) 连接并创建zkclient
        zkClient = zookeeperTransporter.connect(url);
//ZKR3 zkClient.addStateListener 添加监听器
        zkClient.addStateListener(new StateListener() { //2
            public void stateChanged(int state) { //3
            	if (state == RECONNECTED) { //4
	            	try {
						recover();
					} catch (Exception e) {
						logger.error(e.getMessage(), e);
					}
            	}//4
            } //3 
        }); //2
      
    }

此时,返回的zkclient如下:

image-20221220171514177

然后,对zk客户端,添加重新连接的状态监听。在重新连接时,把recoverd的加入失败队列failedRegistered、FailedSubscribed。

EXP3.3 registry.register

回到EXP3 RegistryProtocol.export的过程

RegistryProtocol
 
 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//EXP3.1 doLocalExport
  			//对服务dubbo://的暴露,并打开端口等操作
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//EXP3.2 getRegistry
  			//根据originInvoker的注册中心zookeeper://,加载registryService的实现类--zookeeperRegistry
        final Registry registry = getRegistry(originInvoker);
  			//获取注册的服务提供者的URL,即dubbo://
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//EXP3.3 registry.register
  			//将provider信息,注册到注册中心
        registry.register(registedProviderUrl);
  
/**…………………………………………………………………………………………………………override信息、overrideListener的订阅………????……………………………………………………………………… */
        // 订阅override数据
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
  		//获取订阅的override数据
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
  		//创建override监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
  		//向注册中心订阅override
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
  
/**…………………………………………从RegistryProtocol返回Exporter匿名类………………………………………… */
        //保证每次export都返回一个新的exporter实例(其包裹的exporter是EXP3.1doLocalExport中暴露的服务exporter)
        return new Exporter<T>() {
            public Invoker<T> getInvoker() {
                return exporter.getInvoker();
            }
            public void unexport() {
            	try {
            		exporter.unexport();
            	} catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	registry.unregister(registedProviderUrl);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	overrideListeners.remove(overrideSubscribeUrl);
                	registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
            }
        };
    }

然后,从originInvoker中获取要注册的服务providerURL

originInvoker的url如下:
registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D2163%26side%3Dprovider%26timestamp%3D1671526419741&organization=dubbox&owner=programmer&pid=2163&registry=zookeeper&timestamp=1671526419537

在originInvoker中仍旧存有export=…………要暴露的服务。

//从originInvoker中获取注册的服务提供者的URL,即dubbo://,该url就是要注册到zookeeper上的服务
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//EXP3.3 registry.register
  			//将provider信息,注册到注册中心
        registry.register(registedProviderUrl);
FailbackRegistry
  public void register(URL url) {
//EXP3.3.1 AbstractRegistry.register
        super.register(url);
  			//可能是重试过程的register,因此需要删除注册、取消注册失败的任务URL
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
//EXP3.3.2 ZookeeperRegistry.doRegister
            // 向服务器端发送注册请求---由子类zookeeperRegistry实现逻辑
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // 如果开启了启动时检测,则直接抛出异常
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if(skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 将失败的注册请求记录到失败列表,定时重试
            failedRegistered.add(url);
        }
    }
EXP3.3.1 AbstractRegistry.register
   //记录注册过的服务URL
   private final Set<URL> registered = new ConcurrentHashSet<URL>();

   public void register(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("register url == null");
        }
        if (logger.isInfoEnabled()){
            logger.info("Register: " + url);
        }
        registered.add(url);
    }
EXP3.3.2 ZookeeperRegistry.doRegister
   ZookeeperRegistry
protected void doRegister(URL url) {
        try {
          //利用zkclient创建节点;第二个参数,是否创建临时节点,默认为true(providerurl临时节点,会再服务下线时自动删除)
        	zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

这里是拼接providerURL在zookeeper中注册的完全路径名。

	//将providerURL转换为path
   private String toUrlPath(URL url) {
     		//URL.encode(url.toFullString()将dubbo://编码
        return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
    }

		//将providerURL转换为带category的path路径(zookeeper上存储的路径)
    private String toCategoryPath(URL url) {
      //servicePath/category(provider端默认为provider)
        return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
    }

   private String toServicePath(URL url) {
        String name = url.getServiceInterface();
        if (Constants.ANY_VALUE.equals(name)) {
            return toRootPath();
        }
        return toRootDir() + URL.encode(name);
    }

在zookeeper上注册的完整路径如下,provider端的注册,只向category=providers注册。

/dubbo/com.alibaba.dubbo.demo.DemoService/providers/dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D2163%26side%3Dprovider%26timestamp%3D1671526419741

然后,在zkclient客户端中创建节点

AbstractZookeeperClient	
public void create(String path, boolean ephemeral) {
		int i = path.lastIndexOf('/');
		if (i > 0) {
      //递归创建路径上的父节点
			create(path.substring(0, i), false);
		}
  	//创建临时节点
		if (ephemeral) {
			createEphemeral(path);
		} else {
			createPersistent(path);
		}
	}

后续过程省略,此处完成了provider端服务的暴露。

posted @ 2023-03-13 14:43  LeasonXue  阅读(65)  评论(0编辑  收藏  举报