五Dubbo服务引用源码分析--2创建远程调用的代理-2.2RegistryProtocol.refer

五Dubbo服务引用源码分析--2创建远程调用的代理-2.2RegistryProtocol.refer

/**…………RegistryProtocol.refer………………*/
REFER1 qos/filter/listener.refer
QosProtocolWrapper
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        //开启心跳服务器
            startQosServer(url);
          //protocolFilterWrapper.refer
          //type为DemoService,url=registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D46377%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672209755972&registry=zookeeper&timestamp=1672209756422
            return protocol.refer(type, url);
        }
        return protocol.refer(type, url);
    }
ProtocolFilterWrapper
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
  			//url为registry://,直接引用服务
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

ProtocolFilterWrapper.refer根据服务类型、以及url来引用服务,构造invoker时:

1 如果URL 为registry://的注册中心,则直接protocol.refer;

2 如果URL不为registry://,则会在protocol.refer引用服务后,针对获取的invoker构造filter拦截器链。

这里,url为注册中心,直接走前者。

ProtocolListenerWrapper
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
          //
            return protocol.refer(type, url);
        }
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
    }

ProtocolListenerWrapper情况同filter,

1 如果url为注册中心registry://,则直接protocol.refer,传递给包裹的内部protocol;

2 如果不是,首先引用服务构造invoker,然后创建监听;(这里监听过程,和provider端相同,可以参阅)

REFER2 RegistryProtocol.refer--todo
RegistryProtocol.refer
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
  		//设置url协议头为zookeeper
  //zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D46377%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672209755972&timestamp=1672209756422
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
//REFER2.1 registryFactory.getRegistry(url)
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

       // group="a,b" or group="*"
  		 //解码refer引用的参数对
        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);
            }
        }
//REFER2.2. doRefer
        return doRefer(cluster, registry, type, url);
    }

解析后的refer引用服务的参数对,如下:

image-20221229145418727

RegistryProtocol.refer逻辑:

1 将registry://协议头更换为zookeeper://,然后REFER2.1 registryFactory.getRegistry(url)获取注册registry;
2 然后doRefer创建invoker;
REFER2.1 registryFactory.getRegistry(url)

image-20221229110834759

public class RegistryFactory$Adaptive 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);
      //zookeeperRegistryFactory
        return extension.getRegistry(arg0);
    }
}
AbstractRegistryFactory
    @Override
    public Registry getRegistry(URL url) {
  		//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&timestamp=1672209756422
  //这里是构建注册中心的URL,设置path为RegistryService,并且移除refer引用的配置,并增加接口服务为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 the registry access process to ensure a single instance of the registry
        LOCK.lock();
        try {
            Registry registry = REGISTRIES.get(key);
            if (registry != null) {
                return registry;
            }
//创建注册服务类
            registry = createRegistry(url);
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
            REGISTRIES.put(key, registry);//放入缓存
            return registry;
        } finally {
            // Release the lock
            LOCK.unlock();
        }
    }
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
		//zookeeperTransporter$Adaptive spi依赖注入
    private ZookeeperTransporter zookeeperTransporter;

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

    @Override
    public Registry createRegistry(URL url) {
//REFER2.1.1 ZookeeperRegistry(url, zookeeperTransporter) 创建zookeeper注册中心
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }

创建ZookeeperRegistry注册中心。

//REFER2.1.1 ZookeeperRegistry(url, zookeeperTransporter)初始化--同provider

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

此时url=zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.registry.RegistryService&organization=dubbox&owner=programmer&pid=46377&qos.enable=false&timestamp=1672209756422。

这里zookeeperRegistry的初始化过程,和provider的过程相同,可以参考。此处略

初始化后zookeeperRegistry内的属性如下:

image-20230313122957054

在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为注册中心的地址
REFER2.2 doRefer
        return doRefer(cluster, registry, type, url);

方法的参数如下:

cluster:cluster$Adaptive,type:DemoService,

url=zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672287700727&timestamp=1672287701224
RegistryProtocol
    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//REFER2.2.1 RegistryDirectory
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);//zookeeperRegistry
        directory.setProtocol(protocol);//Protocol$Adaptive
  
        // REFER_KEY的全部属性
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
  		//构造consumerURL(订阅注册中心的url)--协议头为consumer,路径名称为demoService,参数对为refer服务的参数
  //consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&side=consumer&timestamp=1672287700727
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
//REFER2.2.2 zookeeperRegistry.register
            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
//REFER2.2.3 registryDirectory.subscribe
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));
//REFER2.2.4 cluster$Adaptive.join(directory)
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

doRefer完成如下任务:

1 创建registryDirectory,并设置registry、protocol;

2 构造consumer消费者的url(consumer://);

3 向zookeeperRegistry上的consumer_category路径下注册消费者url;//REFER2.2.2 zookeeperRegistry.register

4 由directory.subscribe()订阅zookeeper结构树上provider、configuration、router的category路径下的节点;//REFER2.2.3 registryDirectory.subscribe

5 最后,由cluster集群,cluster.join(directory)返回一个invoker。//REFER2.2.4 cluster$Adaptive.join(directory)

REFER2.2.1 RegistryDirectory初始化

初始化registryDirectory,并设置zookeeperRegistry、Protocol$Adaptive。

RegistryProtocol
    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//REFER2.2.1 RegistryDirectory
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);//zookeeperRegistry
        directory.setProtocol(protocol);//Protocol$Adaptive

然后,执行registryDirectory的初始化。

RegistryDirectory
		//$adaptive类
    private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
    private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
    private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();

   public RegistryDirectory(Class<T> serviceType, URL url) {
//REFER2.2.1.1 AbstractDirectory
     	//初始化AbstractDirectory,以及其内router属性
        super(url);
        if (serviceType == null)
            throw new IllegalArgumentException("service type is null.");
        if (url.getServiceKey() == null || url.getServiceKey().length() == 0)
            throw new IllegalArgumentException("registry serviceKey is null.");
        this.serviceType = serviceType; //DemoService
        this.serviceKey = url.getServiceKey();//com.alibaba.dubbo.registry.RegistryService
     //是url中refer引用服务的参数对
        this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
     //zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&register.ip=192.168.0.101&side=consumer&timestamp=1672287700727
     //将原来注册中心url内参数全部删除,把refer服务的参数对map存入
        this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
        String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");//group=""
        this.multiGroup = group != null && ("*".equals(group) || group.contains(",")); //false
        String methods = queryMap.get(Constants.METHODS_KEY);//getPermissions
        this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);//getPermissions
    }

image-20221229182426727

//REFER2.2.1.1 AbstractDirectory

AbstractDirectory//目录directory的抽象实现:从此directory的list方法返回的list<> invokers,都是已经被router过滤过的。所以AbstractDirectory重要的属性是router路由器。

   public AbstractDirectory(URL url) {
        this(url, null);
    }
     public AbstractDirectory(URL url, List<Router> routers) {
        this(url, url, routers);
    }
       public AbstractDirectory(URL url, URL consumerUrl, List<Router> routers) {
        if (url == null)
            throw new IllegalArgumentException("url == null");
         //此时,url、consumerUrl同为zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-consumer&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&refer=application%3Ddemotest-consumer%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26register.ip%3D192.168.0.101%26side%3Dconsumer%26timestamp%3D1672287700727&timestamp=1672287701224
        this.url = url;
        this.consumerUrl = consumerUrl;
        setRouters(routers);
    }
    
        protected void setRouters(List<Router> routers) {
        // copy list
        routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
        // append url router
        String routerkey = url.getParameter(Constants.ROUTER_KEY);
        if (routerkey != null && routerkey.length() > 0) {
            RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
            routers.add(routerFactory.getRouter(url));
        }
        // 在routers路由列表中,追加 mock invoker selector
        routers.add(new MockInvokersSelector());
        Collections.sort(routers);
        this.routers = routers;
    }
		
		//AbstractDirectory.list方法,会返回经过router过滤过的可用的invoker的list集合
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
//略

MockInvokersSelector是Router的实现类,其是个旨在实现mock模拟功能的特定的路由器。(例如,如果request请求配置使用mock,那么mockInvokersSelector保证只有protocol协议为mock的invoker,会最终出现在List invokers列表中,其他invoker会被排除掉)。

REFER2.2.2 zookeeperRegistry.register
   registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));

其中,subscribeurl的参数对中添加category:consumer,check=false的参数对。

image-20221229185202323

FailbackRegistry
   public void register(URL url) {
        super.register(url);
  		//failback中保存了失败的订阅、取消订阅的url
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
            // Sending a registration request to the server side
//REFER2.2.2.1 doRegister 由子类实现
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // If the startup detection is opened, the Exception is thrown directly.
            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);
            }

            // Record a failed registration request to a failed list, retry regularly
            failedRegistered.add(url);
        }
    }
AbstractRegistry
    @Override
    public void register(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("register url == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Register: " + url);
        }
        registered.add(url);//将consumer消费者的url,添加到abstractRegistry的registered上
    }
//REFER2.2.2.1 ZookeeperRegistry.doRegister
ZookeeperRegistry
   protected void doRegister(URL url) {
        try {
            zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));//默认为true,临时节点
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

其中,toUrlPath(url)转换为zookeeper存储上的路径

  private String toUrlPath(URL url) {
        return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
    }

/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F192.168.0.101%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemotest-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D57858%26qos.enable%3Dfalse%26side%3Dconsumer%26timestamp%3D1672287700727

这里是通过zkclient,向zookeeper上添加临时节点,完成consumer者注册。

zookeeperClient添加节点过程略:

AbstractZookeeperClient
   @Override
    public void create(String path, boolean ephemeral) {
        if (!ephemeral) {
            if (checkExists(path)) {
                return;
            }
        }
        int i = path.lastIndexOf('/');
        if (i > 0) {
            create(path.substring(0, i), false);
        }
        if (ephemeral) {
            createEphemeral(path);
        } else {
            createPersistent(path);
        }
    }
    
    CuratorZookeeperClient
        public void createEphemeral(String path) {
        try {
            client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
        } catch (NodeExistsException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
REFER2.2.3 registryDirectory.subscribe
RegistryProtocol
//REFER2.2.3 registryDirectory.subscribe
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));

在订阅时候,会向url中添加category=providers、configurators、routers的参数对。

RegistryDirectory
   public void subscribe(URL url) {
  //consumer://192.168.0.101/com.alibaba.dubbo.demo.DemoService?application=demotest-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=57858&qos.enable=false&side=consumer&timestamp=1672287700727
        setConsumerUrl(url);
//REFER2.2.3.1 zookeeperRegistry.subscribe 订阅
        registry.subscribe(url, this);
    }

在registry.subscribe(url,this)方法中,第二个参数是NotifyListener,即传入RegistryDirectory,可知directory是注册中心订阅--通知机制的关键部分。

image-20221230104416211

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