dubbo client registration listening

Posted by jammyjames on Thu, 04 Nov 2021 07:21:06 +0100

background

When dubbo uses zookeeper(zk) as the registry, it updates the latest list of service providers by subscribing to ZK's watch listening mechanism. The general process is as follows.

Today, we mainly analyze the red subscription and the processing logic.

subscribe

In the consumer startup phase, the protocol will be executed layer by layer to RegistryProtocol.refer(),

org.apache.dubbo.registry.integration.RegistryProtocol#doRefer()
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);
        //Note 1
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));//
        return invoker;
    }
Copy code

In note 1, call the subscribe of the directory object to listen for subscriptions, and then click this method.

org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe()
public void doSubscribe(final URL url, final NotifyListener listener) {
    List<URL> urls = new ArrayList<>();
    for (String path : toCategoriesPath(url)) {
        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
        if (listeners == null) {
            zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
            listeners = zkListeners.get(url);
        }
        ChildListener zkListener = listeners.get(listener);
        if (zkListener == null) {
        //Note 1
            listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
            zkListener = listeners.get(listener);
        }
        zkClient.create(path, false);
        List<String> children = zkClient.addChildListener(path, zkListener);
        if (children != null) {
            urls.addAll(toUrlsWithEmpty(url, path, children));
        }
    }
    notify(url, listener, urls);//Note 2
}
Copy code

Note 1 is the lambda writing method of jdk1.8. Here is the callback method for handling zk node changes, and then register with zk server through zkClient (CuratorFramework client). Note 2 pushes the added node to the callback method when subscribing for the first time by default. However, if the producer does not start at this time, the consumer's call list will not be updated, Let's see what the callback method does.

Callback

The callback method will be called from EventThread#processEvent() of zk client to RegistryDirectory#notify() method

//org....RegistryDirectory#notify()
//.. Omitted
refreshOverrideAndInvoker(providerURLs);//Note 1
 Copy code

This is the entrance to refresh the call list.

//org.xx.RegistryDirectory#refreshInvoker()
private void refreshInvoker(List<URL> invokerUrls) {
	//...
 Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);//Note 1

    List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));

    routerChain.setInvokers(newInvokers);
    this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;//Note 2
    this.urlInvokerMap = newUrlInvokerMap;

    destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap);//Note 3
}
Copy code

Node URL returned with zk at comment 1( dubbo://ip:20880/com.poizon.study.api.service... )Generate the invoker service call object. In Note 2, the latest call list is assigned to invokers. Let's write it down first. Then come back and look at this variable. Note 3 will clear the expired call list. For example, a provider machine is offline (if you are interested in going offline, you can see the source code, not going offline immediately. There is a strategy). You need to remove it from the caller list.

The generation method of invoker is not described here. The general process is to call protocol.refer() and wrap it layer by layer, and finally wrap it as invokerdelegate (listener invoker wrapper (callbackregistrationinvoker (filter (asynctosyncinvoker (dubboinvoker())).

invoker usage

When the consumer initiates a request, the call link is mockclusterinvoker - > failoverclusterinvoker #doinvoke() - > failoverclusterinvoker #list(), which means to list the available producers in Chinese. Let's go to the method.

//org.apache.dubbo.rpc.cluster.directory.AbstractDirectory#list
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    if (destroyed) {
        throw new RpcException("Directory already destroyed .url: " + getUrl());
    }
    return doList(invocation);//Note 1
}
//org.apache.dubbo.registry.integration.RegistryDirectory#doList
public List<Invoker<T>> doList(Invocation invocation) {
    if (forbidden) {
        throw new RpcException("No provider available");//Note 2
    }
    if (multiGroup) {
        return this.invokers == null ? Collections.emptyList() : this.invokers;//Note 3
    }
}
Copy code

Everyone should have seen the error exception in Note 2. When there is no producer to call, an error will be thrown. In note 3, the invokers variable is returned. This variable is the new invoekrs assignment generated after our subscription processing.

summary

The author follows the process of zk as the registration center. You can also change to nacos.


Link: https://juejin.cn/post/6925674293980299277

Topics: Java Dubbo Back-end