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