In the previous article, we looked at the process of publishing a dubbo service, and this article outlined the process of analyzing dubbo service references.
1. What should service consumers do?
- Generate proxy objects (help us achieve communication details)
- Set up a communication connection (netty)
- Get the service provider address (subscription provider) from zk
- load balancing
- fault-tolerant
- serialize
- ...
2. Two Steps
The logic above can be roughly divided into two steps
-
Service startup phase
Build communication connection, create proxy object
-
Remote Call Phase
Remote invocation of service provider methods
3. Service Startup Phase
The Dubbo service references two times:
- The first is to reference the service when the Spring container calls the afterPropertiesSet method of the ReferenceBean
- The second is referenced when the service corresponding to the ReferenceBean is injected into another class
The difference between the two referencing services is that the first is hungry and the second is lazy.
By default, Dubbo uses lazy reference services.If you need to use Hungry Han style, you can configure dubbo:reference The init property of is turned on.
Typically, we use the dubbo service in our code as follows:
@Reference private ISayHelloService iSayHelloService; ISayHelloService.hello();
By annotating @Reference or the configuration file dubbo-consumer.xml, I'm using annotations here.
Let's debug first to see what this ISayHelloService object is.
As you can see, ISayHelloService is a proxy object and a wrapper class that contains many layers.
ReferenceAnnotationBeanPostProcessor -> ReferenceBean -> InvokerHandler -> mockClusterInvoker -> RegistryDirectory
Classes like BeanPostProcessor will execute when spring starts, so let's start with this class.
//ReferenceAnnotationBeanPostProcessor.java private static class ReferenceBeanInvocationHandler implements InvocationHandler { private final ReferenceBean referenceBean; private Object bean; private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) { this.referenceBean = referenceBean; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(bean, args); } //Initialization Method private void init() { this.bean = referenceBean.get(); } }
ReferenceBean.get()
Continue tracking referenceBean.get():
//ReferenceConfig.java //Get Service Reference public synchronized T get() { if (destroyed) { throw new IllegalStateException("Already destroyed!"); } // Detect if ref is empty, or create it by init method if (ref == null) { // The init method is primarily used to handle configurations and to call createProxy to generate proxy classes init(); } return ref; }
Continue tracking init():
//ReferenceConfig.java private void init() { if (initialized) { return; } initialized = true; if (interfaceName == null || interfaceName.length() == 0) { throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!"); } // get consumer's global configuration checkDefault(); //Fill ConsumerConfig appendProperties(this); //...omitted //Assembly URL Map<String, String> map = new HashMap<String, String>(); Map<Object, Object> attributes = new HashMap<Object, Object>(); map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE); map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion()); map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis())); //...omitted //Get registry host String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY); if (hostToRegistry == null || hostToRegistry.length() == 0) { hostToRegistry = NetUtils.getLocalHost(); } else if (isInvalidLocalHost(hostToRegistry)) { throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry); } map.put(Constants.REGISTER_IP_KEY, hostToRegistry); //attributes are stored by system context. StaticContext.getSystemContext().putAll(attributes); //A key!!Create Proxy Object ref = createProxy(map); ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods()); ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel); }
There is a lot of code here, I've omitted a few, let's focus on the general process.
The first is to populate the ConsumerConfig field with a system variable or dubbo.properties configuration file.
The various configurations are then collected and stored in a map to assemble the URL.
Finally, a proxy object is created.
createProxy()
Let's focus on how to create proxy objects:
//ReferenceConfig.java private T createProxy(Map<String, String> map) { URL tmpUrl = new URL("temp", "localhost", 0, map); final boolean isJvmRefer; //...omitted //Local Reference if (isJvmRefer) { //...omitted } else { //Remote Reference-Direct Connection if (url != null && url.length() > 0) { //...omitted } else { //Remote Reference-Walk Registry // Load registry url List<URL> us = loadRegistries(false); if (us != null && !us.isEmpty()) { for (URL u : us) { URL monitorUrl = loadMonitor(u); if (monitorUrl != null) { map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString())); } // Add refer parameter to url and Add url to urls urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } } if (urls.isEmpty()) { throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config."); } } // Individual registry or service provider (direct service connection, same below) if (urls.size() == 1) { //Adaptive - > wrapper (filter (RegisterProtocol). refer // Call RegistryProtocol's refer to build an Invoker instance invoker = refprotocol.refer(interfaceClass, urls.get(0)); } else { //...omitted } } //Generate Proxy Class // create service proxy return (T) proxyFactory.getProxy(invoker); }
The logic here is simpler, the way service calls are processed logically, and we look directly at the most important refprotocol.refer(interfaceClass, urls.get(0)) method.
The entry urls.get(0) of this method is the url of the registry and should be parsed out like registry://registry-host/org.apache.dubbo.registry.RegistryService?refer=URL.encode("consumer://consumer-host/com.foo.FooService?version=1.0.0")
Based on the extension point adaptive mechanism, the RegistryProtocol.refer() method is invoked when the URL is identified by the registry://protocol header. Based on the conditions in the refer parameter, the provider URL is queried, such as dubbo://service-host/com.foo.FooService version=1.0.0?
Then, identified by the dubbo://protocol header of the provider URL, the DubboProtocol.refer() method is called to get the provider reference.
RegistryProtocol.refer()
So let's move on to the refer() method:
//RegistryProtocol.java public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY); //Get Registry Object Registry registry = registryFactory.getRegistry(url); if (RegistryService.class.equals(type)) { return proxyFactory.getInvoker((T) registry, type, url); } //...omitted // Call doRefer to continue executing service reference logic return doRefer(cluster, registry, type, url); }
Continue tracking doRefer()
//RegistryProtocol.java /** * * @param cluster * @param registry Registry Object * @param type * @param url Registry url * @param <T> * @return */ private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { //Get the RegistryDirectory object, the service directory //A service directory is similar to a registry, managing producers ip, port s, and so on, and is actually a collection of invoker s RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url); directory.setRegistry(registry); //Set up a registry directory.setProtocol(protocol); //Setting up a service provider // Create a subscription url, that is, a consumer url Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters()); URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters); // Register yourself with the registry (service consumer) //Write yourself to the/dubbo/com.foo.BarService/consumers directory in zk if (!Constants.ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(Constants.REGISTER_KEY, true)) { registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false))); } // Subscribe to node data such as providers, configurators, routers directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY + "," + Constants.ROUTERS_CATEGORY)); // A registry may have multiple service providers, so you need to merge multiple service providers into one //MockClusterWrapper(FailoverCluster) Invoker invoker = cluster.join(directory); // Register consumer with local registry ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory); return invoker; }
Here are the main things to do:
- Connect Registry
- Register yourself with the registry (service consumer)
- Subscribe to nodes such as registry providers
subscribe()
Let's look at the most important directory.subscribe() method, Subscribe:
//ZookeeperRegistry.java /** * * @param url Consumer url consumer:// * @param listener RegistryDirectory */ @Override protected void doSubscribe(final URL url, final NotifyListener listener) { try { // interface = *, that is, subscribe to global if (Constants.ANY_VALUE.equals(url.getServiceInterface())) { //...omitted } else { // Interface = a specific interface, which triggers a callback only if its child nodes change // For example: interface = com.lol.test.SayFacade // All URL s under the Service layer List<URL> urls = new ArrayList<URL>(); //Path is the directory of producers in zk: path -> /dubbo/com.foo.BarService/providers 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); } // Get ChildListener Object ChildListener zkListener = listeners.get(listener); if (zkListener == null) { listeners.putIfAbsent(listener, new ChildListener() { @Override public void childChanged(String parentPath, List<String> currentChilds) { // When the service provider url changes, call the `#notify(...)'method and call back through the listener ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)); } }); zkListener = listeners.get(listener); } zkClient.create(path, false); //children is a child node - > service provider List<String> children = zkClient.addChildListener(path, zkListener); if (children != null) { //urls are the true service provider urls urls.addAll(toUrlsWithEmpty(url, path, children)); } } //First subscription, full notification, create corresponding invoker notify(url, listener, urls); } } }
The general logic here is:
- Get the service provider urls for the corresponding service through zk
- These urls are then monitored through RegistryDirectory, and if there are changes, the notify() method is called
- The notify() method is called on all urls for the first time
notify()
Next we'll focus on notify():
//AbstractRegistry.java /** * * @param url Service consumer url consumer:// * @param listener RegistryDirectory * @param urls Service provider urls */ protected void notify(URL url, NotifyListener listener, List<URL> urls) { //...omitted for (Map.Entry<String, List<URL>> entry : result.entrySet()) { String category = entry.getKey(); //categoryList service provider url List<URL> categoryList = entry.getValue(); categoryNotified.put(category, categoryList); saveProperties(url); listener.notify(categoryList); } }
Continue tracking
//RegistryDirectory.java /** * Receive service change notifications * @param urls Service Provider url Collection */ @Override public synchronized void notify(List<URL> urls) { // Define three collections for service provider url, routing url, and configurator URL List<URL> invokerUrls = new ArrayList<URL>(); List<URL> routerUrls = new ArrayList<URL>(); List<URL> configuratorUrls = new ArrayList<URL>(); for (URL url : urls) { String protocol = url.getProtocol(); // Get the category parameter String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); // Put URLs in different lists according to the category parameter if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) { routerUrls.add(url); } else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) { configuratorUrls.add(url); } else if (Constants.PROVIDERS_CATEGORY.equals(category)) { // Add service provider url invokerUrls.add(url); } else { logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()); } } // Convert url to Configurator if (configuratorUrls != null && !configuratorUrls.isEmpty()) { this.configurators = toConfigurators(configuratorUrls); } // Convert url to Router if (routerUrls != null && !routerUrls.isEmpty()) { List<Router> routers = toRouters(routerUrls); if (routers != null) { // null - do nothing setRouters(routers); } } List<Configurator> localConfigurators = this.configurators; // local reference // merge override parameters this.overrideDirectoryUrl = directoryUrl; if (localConfigurators != null && !localConfigurators.isEmpty()) { for (Configurator configurator : localConfigurators) { this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl); } } //A key!!Refresh Invoker List refreshInvoker(invokerUrls); }
Here we just need to look at the last line of code, refreshInvoker(invokerUrls), which refreshes invokers.
This approach is key to ensuring that the collection of service providers, methodInvokerMap, in RegistryDirectory changes with the registry.
refreshInvoker()
//RegistryDirectory.java /** * * @param invokerUrls Service provider url */ private void refreshInvoker(List<URL> invokerUrls) { // If invokerUrls has only one element and the url protocol header is empty, then all services are disabled if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null && Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) { // Set forbidden to true this.forbidden = true; this.methodInvokerMap = null; // Destroy all Invoker s destroyAllInvokers(); } else { this.forbidden = false; Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) { // Add cache url to invokerUrls invokerUrls.addAll(this.cachedInvokerUrls); } else { this.cachedInvokerUrls = new HashSet<URL>(); // Cache invokerUrls this.cachedInvokerUrls.addAll(invokerUrls); } if (invokerUrls.isEmpty()) { return; } // Convert url to Invoker Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map // Mapping newUrlInvokerMap to method name to Invoker list Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // state change if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) { logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls.toString())); return; } // Invoker merging multiple groups this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap; this.urlInvokerMap = newUrlInvokerMap; try { // Destroy unused Invoker destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker } catch (Exception e) { logger.warn("destroyUnusedInvokers error. ", e); } } }
The general logic here is:
- Determine whether services are disabled based on the protocol header
- Convert url to invoker
- Destroy useless Invoker
First, when the url protocol header is empty://, this means that all services are disabled and all Invoker s are destroyed.
Then convert URL to invoker to get the mapping relationship of <url, Invoker>.Further conversion is performed to get the <methodName, Invoker List>mapping relationship.
Group Invoker merge operations are then performed and the merge results are assigned to the methodInvokerMap.
Finally, destroy the useless Invoker to prevent service consumers from invoking services that are offline
Invoker is the core model of Dubbo and represents an executable entity.At the service provider, Invoker is used to invoke the service provider class.On the service consumer side, Invoker is used to perform remote calls.
At this point, the focus is on the process of url to invoker, because dubbo remote calls are made through invoker, there must be a lot of important content in the process of transformation.
toInvokers()
Let's go on to toInvokers:
//RegistryDirectory.java private Map<String, Invoker<T>> toInvokers(List<URL> urls) { Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>(); if (urls == null || urls.isEmpty()) { return newUrlInvokerMap; } Set<String> keys = new HashSet<String>(); // Protocol for getting service consumer configuration String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY); for (URL providerUrl : urls) { if (queryProtocols != null && queryProtocols.length() > 0) { boolean accept = false; String[] acceptProtocols = queryProtocols.split(","); // Detect whether service provider agreements are supported by service consumers for (String acceptProtocol : acceptProtocols) { if (providerUrl.getProtocol().equals(acceptProtocol)) { accept = true; break; } } if (!accept) { // Ignore the current providerUrl if the service consumer agreement header is not supported by the consumer continue; } } // Ignore empty protocol if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) { continue; } // SPI detects whether the server-side protocol is supported by the consumer and throws an exception if it is not supported if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) { logger.error(new IllegalStateException("Unsupported protocol...")); continue; } // Merge URLs URL url = mergeUrl(providerUrl); String key = url.toFullString(); if (keys.contains(key)) { // Ignore duplicate URLs continue; } keys.add(key); // Assign local Invoker cache to localUrlInvokerMap Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // Get Invoker corresponding to url Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key); // Cache Miss if (invoker == null) { try { boolean enabled = true; if (url.hasParameter(Constants.DISABLED_KEY)) { // Get the disable configuration, reverse it, and assign it to the enable variable enabled = !url.getParameter(Constants.DISABLED_KEY, false); } else { // Gets the enable configuration and assigns it to the enable variable enabled = url.getParameter(Constants.ENABLED_KEY, true); } if (enabled) { // Call refer to get Invoker invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl); } } catch (Throwable t) { logger.error("Failed to refer invoker for interface..."); } if (invoker != null) { // Cache Invoker Instances newUrlInvokerMap.put(key, invoker); } // Cache Hit } else { // Store invoker in newUrlInvokerMap newUrlInvokerMap.put(key, invoker); } } keys.clear(); return newUrlInvokerMap; }
The logic here is simple:
-
The service provider url is first detected, and toInvokers ignore the service provider url if either the service consumer's configuration does not support the service-side protocol or the service-side url protocol header is empty.
-
Merge the urls, then access the cache and try to get the invoker corresponding to the url.
-
If the cache hits, store the Invoker directly in the newUrlInvokerMap.
-
If you missed, you need to create a new Invoker.
DubboProtocol.refer()
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
This is the code for creating invoker, where you get the DubboProtocol adaptively.
So let's take a closer look at DubboProtocol.refer():
//DubboProtocol.java //Client Instance private final ExchangeClient[] clients; private final AtomicPositiveInteger index = new AtomicPositiveInteger(); private final String version; private final ReentrantLock destroyLock = new ReentrantLock(); private final Set<Invoker<?>> invokers; /** * url Service provider url */ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { optimizeSerialization(url); // Create DubboInvoker DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); invokers.add(invoker); return invoker; }
The code here is simple, just new an Invoker.The focus is on populating the properties of DubboInvoker.
Let's first look at the ExchangeClient[] clients property, which is a collection of client instances obtained by the getClients(url) method.
ExchangeClient does not actually have communication capabilities; it needs to communicate based on lower-level client instances.For example, NettyClient, MinaClient, and so on, by default, Dubbo uses NettyClient to communicate.Next, let's take a brief look at the logic of the getClients method.
getClients()
//DubboProtocol.java private ExchangeClient[] getClients(URL url) { // Whether to share the connection boolean service_share_connect = false; // Gets the number of connections, defaulting to 0, indicating no configuration int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0); // Shared connection if connections are not configured if (connections == 0) { service_share_connect = true; connections = 1; } ExchangeClient[] clients = new ExchangeClient[connections]; for (int i = 0; i < clients.length; i++) { if (service_share_connect) { // Get Shared Clients clients[i] = getSharedClient(url); } else { // Initialize a new client clients[i] = initClient(url); } } return clients; }
This determines whether to get a shared client or create a new client instance based on the number of connections, which is used by default.The initClient method is also called in the getSharedClient method, so let's look at these two methods together.
//DubboProtocol.java private ExchangeClient getSharedClient(URL url) { String key = url.getAddress(); // Get ExchangeClient with Reference Counting ReferenceCountExchangeClient client = referenceClientMap.get(key); if (client != null) { if (!client.isClosed()) { // Increase Reference Count client.incrementAndGetCount(); return client; } else { referenceClientMap.remove(key); } } locks.putIfAbsent(key, new Object()); synchronized (locks.get(key)) { if (referenceClientMap.containsKey(key)) { return referenceClientMap.get(key); } // Create ExchangeClient Client Client ExchangeClient exchangeClient = initClient(url); // Pass the ExchangeClient instance to the ReferenceCountExchangeClient, using decoration mode client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap); referenceClientMap.put(key, client); ghostClientMap.remove(key); locks.remove(key); return client; } }
First try to get a shared instance with reference counting, and if not, create a new one with initClient(url).
initClient()
//DubboProtocol.java private ExchangeClient initClient(URL url) { // Gets the client type, defaulting to netty4 String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT)); // Add codec and heartbeat package parameters to url url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME); url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT)); // Detect if client type exists, throw exception if none exists if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) { throw new RpcException("Unsupported client type: ..."); } ExchangeClient client; try { // Get the lazy configuration and determine the type of client to create based on the configuration value if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) { // Create lazy-loading ExchangeClient instance client = new LazyConnectExchangeClient(url, requestHandler); } else { // Create a normal ExchangeClient instance client = Exchangers.connect(url, requestHandler); } } catch (RemotingException e) { throw new RpcException("Fail to create remoting client for service..."); } return client; }
The initClient method first gets the client type configured by the user, defaulting to netty4.It then detects if the client type configured by the user exists and throws an exception if it does not exist.Finally, depending on the lazy configuration, you decide what type of client to create.
Next, look at the Exchangers.connect(url, requestHandler) method for creating the client
//Exchangers.java public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange"); // Get an Exchanger instance, defaulting to HeaderExchangeClient return getExchanger(url).connect(url, handler); }
Here, by adapting, HeaderExchanger.connect() is called
//HeaderExchanger.java public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { // There are several calls, as follows: // 1. Create the HeaderExchangeHandler object // 2. Create DecodeHandler object // 3. Building Client Instances with Transporters // 4. Create the HeaderExchangeClient object return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true); }
There are many calls here, so let's focus on Transporters'connect method.The following:
//Transporters.java public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } ChannelHandler handler; if (handlers == null || handlers.length == 0) { handler = new ChannelHandlerAdapter(); } else if (handlers.length == 1) { handler = handlers[0]; } else { // Create a ChannelHandler Distributor if the number of handler s is greater than 1 handler = new ChannelHandlerDispatcher(handlers); } // Get the Transporter adaptive extension class and call the connect method to generate a Client instance return getTransporter().connect(url, handler); }
The getTransporter() method returns an adaptive extension class that loads the specified Transporter implementation class at run time based on the client type.If the user does not configure the client type, NettyTransporter is loaded by default and the connect method of that class is called.The following:
connect()
//NettyTransporter.java public Client connect(URL url, ChannelHandler listener) throws RemotingException { // Create NettyClient object return new NettyClient(url, listener); }
You won't be able to keep up with this anymore. The next step is to build Netty clients through the API s provided by Netty. You are interested to see them for yourself.
createProxy()
At this point, the service provider's Invoker is created, and the next step is to create the proxy object.
This is the ReferenceConfig.createProxy() method, which is not covered here.
summary
After the proxy object has been created, the service startup phase referenced by the dubbo service has been completed. To review what we have done:
- Connect Registry
- Register yourself with the registry (service consumer)
- Subscribe to the registry service provider and obtain the service provider url
- Create Invoker
- Establish communication to connect netty
- Create Proxy Object
After the service starts, the next step is the remote invocation phase, which we will analyze in more detail in the next article.
Finally, draw a simple flowchart for easy understanding.
Reference resources:
Dubbo website