Dubbo Operating Principle

Posted by bslevin on Mon, 26 Aug 2019 08:23:42 +0200

I. SOA Patterns
A brief introduction to the SOA pattern is helpful for us to understand Dubbo later.

What is the SOA model?

SQA (Service-Oriented Architecture) is a service-oriented architecture that splits different functional units of an application (understood here as services). Under this architecture, the project does not interact directly with the database, but accesses the database by calling interfaces of different services.

What are the advantages of the model?

The most direct benefit of this approach is to address code redundancy if multiple projects access the same table in the database at the same time. For example, access to user tables. We can directly invoke the interface in the user service for development, without requiring each item to write a user list for addition, deletion and modification. In addition to this, the benefits of SOA are that it enables developers to architect the entire business system in a faster, more reliable and more reusable manner. Compared with the previous MVC development model, the system based on SOA architecture can more calmly face the rapid changes of business.

SOA schematic diagram:

II. Basic Composition of Dubbo
After talking about SOA, let's take a look at Dubbo's internal architecture. I believe you have seen the following picture more or less:

@Override
    protected void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {//According to the URL, the service interface is *, that is, all
                String root = toRootPath();
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);//Get the listener under the URL
                if (listeners == null) {//Create if nonexistent
                    zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
                    listeners = zkListeners.get(url);
                }
                ChildListener zkListener = listeners.get(listener);//A listener to get subdirectories
                if (zkListener == null) {//If a subdirectory listener is not available, a new one will be created.
                    listeners.putIfAbsent(listener, new ChildListener() {
                        @Override
                        public void childChanged(String parentPath, List<String> currentChilds) {
                            for (String child : currentChilds) {
                                child = URL.decode(child);
                                if (!anyServices.contains(child)) {
                                    anyServices.add(child);
                                    //If the consumer's interface is *, it subscribes to each url, triggering the logic of another branch.
                                    //Here's a callback for new providers under / dubbo, equivalent to increments
                                    subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
                                            Constants.CHECK_KEY, String.valueOf(false)), listener);
                                }
                            }
                        }
                    });
                    zkListener = listeners.get(listener);
                }
                zkClient.create(root, false);
                //Adding listeners returns a collection of child nodes
                List<String> services = zkClient.addChildListener(root, zkListener);//Subscribe to the child elements in the root directory, such as: / dubbo/com.learnDubbo.demo.DemoService/providers
                if (services != null && !services.isEmpty()) {
                    for (String service : services) {
                        service = URL.decode(service);
                        anyServices.add(service);
                        subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
                                Constants.CHECK_KEY, String.valueOf(false)), listener);
                    }
                }
            } else {
                //Here's the subscription logic for explicit interface
                List<URL> urls = new ArrayList<URL>();
                //Listen for each category path
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                        //Encapsulation callback logic
                        listeners.putIfAbsent(listener, new ChildListener() {
                            @Override
                            public void childChanged(String parentPath, List<String> currentChilds) {
                                ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
                            }
                        });
                        zkListener = listeners.get(listener);
                    }
                    //Create Nodes
                    zkClient.create(path, false);
                    //Increase callback
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                //It also monitors services under subscribed URL s and updates invoke lists in Consumer s in real time to enable calls. This method will not be expanded.
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

4. invoke: Actually call the functions in Provider according to the obtained Provider address. This is the only way to synchronize, because consumers need to get the data from producers to do the next step, but Dubbo is an RPC framework, the core of RPC is only to know the interface can not know the internal implementation. So the agent design pattern is used in the Consumer side to create a proxy object of the Provider square class. The real function of the Provider can be obtained through the proxy object, which can protect the real function of the Provider.

Inveoke partial source analysis

If you're interested, look at the invoke invocation process

Monitor: Consumer and Provider send statistics to Monitor every 1 minute, including the number of visits, frequency, etc.

IV. SUMMARY
Here is just a slight analysis of the principle of Dubbo, to understand the Dubbo also need to look at the source code. Although many times we are only a user, but to understand the internal operation principle can not only make us better use, but also the internal programming ideas, design patterns are what we need to learn. More analysis of Dubbo's principles will be done later.

Topics: Java Dubbo Database less Zookeeper