Dubbo: in depth understanding how Dubbo source code publishes services to the registry

I. Preface

As mentioned earlier, Dubbo's service discovery mechanism, that is, SPI. Now that Dubbo has implemented a more powerful service discovery mechanism, let's take a look at what Dubbo needs to do to register services in the registry after discovering services.

2, Introduction to Dubbo service registration

The first thing to understand is that Dubbo is dependent on the Spring container (as for why it was introduced in the previous blog), and the Dubbo service registration process also starts from the Spring container publishing refresh events. After receiving the event, Dubbo will register the service. The whole logic is roughly divided into three parts:

1. Check the parameters and assemble the URL: the service consumer gets the service provider through the URL, so we need to configure the corresponding URL for the service provider.

2. Export service to local and remote: the local refers to the JVM, and the remote refers to the implementation of invoke, so that the service consumer can call the service through the invoke.

3. Register service with the registration center: it can let the service consumer know the service provided by the service provider.

3, Receive Spring container refresh event

In the introduction, we mentioned that Dubbo service registration starts from Spring container publishing refresh event, so how does Dubbo receive this event?

When we usually write the provider's interface implementation class, we will mark @ Service annotation, so this annotation belongs to ServiceBean. There is such a method onApplicationEvent in ServiceBean. This method performs the Service registration operation after receiving the Spring context refresh event

 1 public void onApplicationEvent(ContextRefreshedEvent event) {
 2         //Exported or not && Has the export been cancelled
 3         if (!this.isExported() && !this.isUnexported()) {
 4             if (logger.isInfoEnabled()) {
 5                 logger.info("The service ready on spring started. service: " + this.getInterface());
 6             }
 7 
 8             this.export();
 9         }
10 
11     }

Note that here is Dubbo in 2.7.3. It is not necessary to set delay export when receiving Spring context refresh events, but to check configuration and decide whether delay is needed when exporting, so there are only two judgments. In the 2.6.x version of Dubbo, there is a judgment of isDelay. This is to determine whether the service delays the export. Here's a digression: the version of 2.6.x is com.alibaba.dubbo, while the version of 2.7.x is org.apache.dubbo, and 2.7.0 represents Dubbo's graduation from Apache.

Here is the beginning of the Dubbo service export to the registry process. We need to type @Service on the service interface implementation class. ServiceBean is the key to the integration of Dubbo and Spring framework, which can be seen as a bridge between the two frameworks. Another class that does the same is ReferenceBean.

4, Check configuration parameters and URL assembly

1. Check configuration

At this stage, Dubbo needs to check whether the user's configuration is reasonable or supplement the default configuration for the user. From the refresh event, enter the export() method. The source code is as follows:

 1 public void export() {
 2         super.export();
 3         this.publishExportEvent();
 4     }
 5 
 6 //Enter into ServiceConfig.class Medium export. 
 7 
 8 public synchronized void export() {
 9         //Check and update configuration
10         this.checkAndUpdateSubConfigs();
11         //Export required
12         if (this.shouldExport()) {
13             //Delay required
14             if (this.shouldDelay()) {
15                 DELAY_EXPORT_EXECUTOR.schedule(this::doExport, (long)this.getDelay(), TimeUnit.MILLISECONDS);
16             } else {
17                 //Export immediately
18                 this.doExport();
19             }
20 
21         }
22     }
23 
24 //Get into checkAndUpdateSubConfigs. 
25 
26 public void checkAndUpdateSubConfigs() {
27         //Check configuration items include provider Whether it exists, whether the export port is available, whether the registry can connect, etc
28         this.completeCompoundConfigs();
29         this.startConfigCenter();
30         this.checkDefault();
31         this.checkProtocol();
32         this.checkApplication();
33         if (!this.isOnlyInJvm()) {
34             this.checkRegistry();
35         }
36         //Check whether the interface internal method is not empty
37         this.refresh();
38         this.checkMetadataReport();
39         if (StringUtils.isEmpty(this.interfaceName)) {
40             throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
41         } else {
42             if (this.ref instanceof GenericService) {
43                 this.interfaceClass = GenericService.class;
44                 if (StringUtils.isEmpty(this.generic)) {
45                     this.generic = Boolean.TRUE.toString();
46                 }
47             } else {
48                 try {
49                     this.interfaceClass = Class.forName(this.interfaceName, true, Thread.currentThread().getContextClassLoader());
50                 } catch (ClassNotFoundException var5) {
51                     throw new IllegalStateException(var5.getMessage(), var5);
52                 }
53 
54                 this.checkInterfaceAndMethods(this.interfaceClass, this.methods);
55                 this.checkRef();
56                 this.generic = Boolean.FALSE.toString();
57             }
58             //Whether to export the service or just run the test locally
59             Class stubClass;
60             if (this.local != null) {
61                 if ("true".equals(this.local)) {
62                     this.local = this.interfaceName + "Local";
63                 }
64 
65                 try {
66                     stubClass = ClassUtils.forNameWithThreadContextClassLoader(this.local);
67                 } catch (ClassNotFoundException var4) {
68                     throw new IllegalStateException(var4.getMessage(), var4);
69                 }
70 
71                 if (!this.interfaceClass.isAssignableFrom(stubClass)) {
72                     throw new IllegalStateException("The local implementation class " + stubClass.getName() + " not implement interface " + this.interfaceName);
73                 }
74             }
75 
76             if (this.stub != null) {
77                 if ("true".equals(this.stub)) {
78                     this.stub = this.interfaceName + "Stub";
79                 }
80 
81                 try {
82                     stubClass = ClassUtils.forNameWithThreadContextClassLoader(this.stub);
83                 } catch (ClassNotFoundException var3) {
84                     throw new IllegalStateException(var3.getMessage(), var3);
85                 }
86 
87                 if (!this.interfaceClass.isAssignableFrom(stubClass)) {
88                     throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + this.interfaceName);
89                 }
90             }
91 
92             this.checkStubAndLocal(this.interfaceClass);
93             this.checkMock(this.interfaceClass);
94         }
95     }

The above source code analysis can be seen. The export method mainly checks whether the class whose configuration item has the @ Service tag has legal attributes. Whether the Service provider exists, whether there is a corresponding Application startup, whether the port can be connected, whether there is a corresponding registry and other configurations. After checking these configurations, Dubbo will recognize that we want to start the Service locally for some debugging and expose the Service to others. You don't want to expose it, you can configure it

1 <dubbo:provider export="false" />

2. URL assembly

The URL in Dubbo generally includes the following fields: protocol, host, port, path,parameters. After checking the configuration, you will enter doExport.

Protocol: it is the field at the front of the URL, indicating the protocol. Generally, it is: dubbo thrift http zk

host.port: the corresponding IP address and port

path: interface name

parameters: parameter key value pair

 1 protected synchronized void doExport() {
 2         if (this.unexported) {
 3             throw new IllegalStateException("The service " + this.interfaceClass.getName() + " has already unexported!");
 4         } else if (!this.exported) {
 5             this.exported = true;
 6             if (StringUtils.isEmpty(this.path)) {
 7                 this.path = this.interfaceName;
 8             }
 9 
10             this.doExportUrls();
11         }
12     }
13 
14 //Enter into doExportUrls
15 private void doExportUrls() {
16         //Load registry link
17         List<URL> registryURLs = this.loadRegistries(true);
18         //Traverse with traverser protocols,And export services under each protocol
19         Iterator var2 = this.protocols.iterator();
20 
21         while(var2.hasNext()) {
22             ProtocolConfig protocolConfig = (ProtocolConfig)var2.next();
23             String pathKey = URL.buildKey((String)this.getContextPath(protocolConfig).map((p) -> {
24                 return p + "/" + this.path;
25             }).orElse(this.path), this.group, this.version);
26             ProviderModel providerModel = new ProviderModel(pathKey, this.ref, this.interfaceClass);
27             ApplicationModel.initProviderModel(pathKey, providerModel);
28             this.doExportUrlsFor1Protocol(protocolConfig, registryURLs);
29         }
30 
31     }
32 
33 //Enter the method to load the registry link
34 
35 protected List<URL> loadRegistries(boolean provider) {
36         List<URL> registryList = new ArrayList();
37         if (CollectionUtils.isNotEmpty(this.registries)) {
38             Iterator var3 = this.registries.iterator();
39             //Take the address and configuration from the registration list circularly
40             label47:
41             while(true) {
42                 RegistryConfig config;
43                 String address;
44                 do {
45                     if (!var3.hasNext()) {
46                         return registryList;
47                     }
48 
49                     config = (RegistryConfig)var3.next();
50                     address = config.getAddress();
51                     //address If it is empty, it defaults to 0.0.0.0
52                     if (StringUtils.isEmpty(address)) {
53                         address = "0.0.0.0";
54                     }
55                 } while("N/A".equalsIgnoreCase(address));
56 
57                 Map<String, String> map = new HashMap();
58                 // Add to ApplicationConfig Field information in to map in
59                 appendParameters(map, this.application);
60                 // Add to RegistryConfig Field information to map in
61                 appendParameters(map, config);
62                 // Add to path,protocol Wait until information arrives. map in
63                 map.put("path", RegistryService.class.getName());
64                 appendRuntimeParameters(map);
65                 if (!map.containsKey("protocol")) {
66                     map.put("protocol", "dubbo");
67                 }
68                 // Analytic obtained URL List, address May contain multiple registries ip,
69                 // So what we get is a URL list
70                 List<URL> urls = UrlUtils.parseURLs(address, map);
71                 Iterator var8 = urls.iterator();
72 
73                 while(true) {
74                     URL url;
75                     do {
76                         if (!var8.hasNext()) {
77                             continue label47;
78                         }
79 
80                         url = (URL)var8.next();
81                         //// take URL Protocol header set to registry
82                         url = URLBuilder.from(url).addParameter("registry", url.getProtocol()).setProtocol("registry").build();
83                     // Determine whether to add or not by judging conditions url reach registryList The conditions are as follows:
84                     // (Service provider && register = true or null) || (Non service provider && subscribe = true or null)
85                     } while((!provider || !url.getParameter("register", true)) && (provider || !url.getParameter("subscribe", true)));
86 
87                     //Add to url reach registryList in
88                     registryList.add(url);
89                 }
90             }
91         } else {
92             return registryList;
93         }
94     }

The loadRegistries method mainly contains the following logic:

1. Build parameter mapping set, that is, map

2. Build the link list of the registration center

3. Traverse the link list and decide whether to add it to the registryList according to the conditions

In fact, due to the fact that Dubbo now supports many registries, it is necessary to traverse and build the URLs of some registries. Here is the URL to generate the registry. The URL for the Dubbo service has not been generated. For example, the Zookeeper registration center is used, which may be obtained from the loadRegistries:

registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.7.3&pid=1528&qos.port=22222&registry=zookeeper&timestamp=1530743640901

This type of URL indicates that this is a registration protocol. Now you can locate it in the registration center according to this URL. The service interface is RegistryService, and the type of registry is zookeeper. But we haven't generated the URL of Dubbo service provider yet, so let's look at the following code

Then go to doExportUrlsFor1Protocol (assemble the URL of Dubbo service and publish it)

  1 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
  2         //First, put some information, such as version, timestamp, method name and field information of various configuration objects into the map in
  3         //map Content in will be used as URL The query string for. Build well map Then, we get the context path, host name, port number and other information.
  4        //Finally will map And host name to URL Construction method creation URL Object. It should be noted that the URL Be not java.net.URL,But com.alibaba.dubbo.common.URL. 
  5         String name = protocolConfig.getName();
  6         // If the protocol name is empty, or an empty string, set the protocol name variable to dubbo
  7         if (StringUtils.isEmpty(name)) {
  8             name = "dubbo";
  9         }
 10 
 11         Map<String, String> map = new HashMap();
 12         // Add to side,Version, timestamp, process number and other information to map in
 13         map.put("side", "provider");
 14         appendRuntimeParameters(map);
 15         // Add the object's field information to the map in
 16         appendParameters(map, this.metrics);
 17         appendParameters(map, this.application);
 18         appendParameters(map, this.module);
 19         appendParameters(map, this.provider);
 20         appendParameters(map, protocolConfig);
 21         appendParameters(map, this);
 22         String scope;
 23         Iterator metadataReportService;
 24         // methods by MethodConfig Set, MethodConfig Stored in <dubbo:method> Label configuration information
 25         if (CollectionUtils.isNotEmpty(this.methods)) {
 26             Iterator var5 = this.methods.iterator();
 27             //Testing <dubbo:method> Tag and add the relevant configuration to the map in
 28             label166:
 29             while(true) {
 30                 MethodConfig method;
 31                 List arguments;
 32                 do {
 33                     if (!var5.hasNext()) {
 34                         break label166;
 35                     }
 36 
 37                     method = (MethodConfig)var5.next();
 38                     appendParameters(map, method, method.getName());
 39                     String retryKey = method.getName() + ".retry";
 40                     if (map.containsKey(retryKey)) {
 41                         scope = (String)map.remove(retryKey);
 42                         if ("false".equals(scope)) {
 43                             map.put(method.getName() + ".retries", "0");
 44                         }
 45                     }
 46 
 47                     arguments = method.getArguments();
 48                 } while(!CollectionUtils.isNotEmpty(arguments));
 49 
 50                 metadataReportService = arguments.iterator();
 51 
 52                 while(true) {
 53                     ArgumentConfig argument;
 54                     Method[] methods;
 55                     do {
 56                         do {
 57                             while(true) {
 58                                 if (!metadataReportService.hasNext()) {
 59                                     continue label166;
 60                                 }
 61 
 62                                 argument = (ArgumentConfig)metadataReportService.next();
 63                                 if (argument.getType() != null && argument.getType().length() > 0) {
 64                                     methods = this.interfaceClass.getMethods();
 65                                     break;
 66                                 }
 67 
 68                                 if (argument.getIndex() == -1) {
 69                                     throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
 70                                 }
 71 
 72                                 appendParameters(map, argument, method.getName() + "." + argument.getIndex());
 73                             }
 74                         } while(methods == null);
 75                     } while(methods.length <= 0);
 76 
 77                     for(int i = 0; i < methods.length; ++i) {
 78                         String methodName = methods[i].getName();
 79                         if (methodName.equals(method.getName())) {
 80                             Class<?>[] argtypes = methods[i].getParameterTypes();
 81                             if (argument.getIndex() != -1) {
 82                                 if (!argtypes[argument.getIndex()].getName().equals(argument.getType())) {
 83                                     throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
 84                                 }
 85 
 86                                 appendParameters(map, argument, method.getName() + "." + argument.getIndex());
 87                             } else {
 88                                 for(int j = 0; j < argtypes.length; ++j) {
 89                                     Class<?> argclazz = argtypes[j];
 90                                     if (argclazz.getName().equals(argument.getType())) {
 91                                         appendParameters(map, argument, method.getName() + "." + j);
 92                                         if (argument.getIndex() != -1 && argument.getIndex() != j) {
 93                                             throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
 94                                         }
 95                                     }
 96                                 }
 97                             }
 98                         }
 99                     }
100                 }
101             }
102         }
103 
104         String host;
105          // Testing generic Whether it is "true",According to the test results map Add different information to
106         if (ProtocolUtils.isGeneric(this.generic)) {
107             map.put("generic", this.generic);
108             map.put("methods", "*");
109         } else {
110             host = Version.getVersion(this.interfaceClass, this.version);
111             if (host != null && host.length() > 0) {
112                 map.put("revision", host);
113             }
114              // Generate package class for interface Wrapper,Wrapper It contains interface details, such as interface method name array, field information, etc
115             String[] methods = Wrapper.getWrapper(this.interfaceClass).getMethodNames();
116             if (methods.length == 0) {
117                 logger.warn("No method found in service interface " + this.interfaceClass.getName());
118                 map.put("methods", "*");
119             } else {
120                 // Use comma as separator to connect method name, and put the connected string into map in
121                 map.put("methods", StringUtils.join(new HashSet(Arrays.asList(methods)), ","));
122             }
123         }
124         // Add to token reach map in
125         if (!ConfigUtils.isEmpty(this.token)) {
126             if (ConfigUtils.isDefault(this.token)) {
127                 map.put("token", UUID.randomUUID().toString());
128             } else {
129                 map.put("token", this.token);
130             }
131         }
132         //Obtain host and port
133         host = this.findConfigedHosts(protocolConfig, registryURLs, map);
134         Integer port = this.findConfigedPorts(protocolConfig, name, map);
135         // Get context path and assemble URL
136         URL url = new URL(name, host, port, (String)this.getContextPath(protocolConfig).map((p) -> {
137             return p + "/" + this.path;
138         }).orElse(this.path), map);
139         if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).hasExtension(url.getProtocol())) {
140             // Load ConfiguratorFactory,And generate Configurator Instance, and then configure through the instance url,Using the aforementioned SPI mechanism
141             url = ((ConfiguratorFactory)ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getExtension(url.getProtocol())).getConfigurator(url).configure(url);
142         }
143         //The following logic is mainly divided into three steps
144         // If scope = none,Do nothing
145         // scope != remote,Export to local
146         // scope != local,Export to remote
147         scope = url.getParameter("scope");
148         if (!"none".equalsIgnoreCase(scope)) {
149             if (!"remote".equalsIgnoreCase(scope)) {
150                 this.exportLocal(url);
151             }
152 
153             if (!"local".equalsIgnoreCase(scope)) {
154                 if (!this.isOnlyInJvm() && logger.isInfoEnabled()) {
155                     logger.info("Export dubbo service " + this.interfaceClass.getName() + " to url " + url);
156                 }
157 
158                 if (CollectionUtils.isNotEmpty(registryURLs)) {
159                     metadataReportService = registryURLs.iterator();
160 
161                     while(metadataReportService.hasNext()) {
162                         URL registryURL = (URL)metadataReportService.next();
163                         if (!"injvm".equalsIgnoreCase(url.getProtocol())) {
164                             url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
165                             URL monitorUrl = this.loadMonitor(registryURL);
166                             if (monitorUrl != null) {
167                                 url = url.addParameterAndEncoded("monitor", monitorUrl.toFullString());
168                             }
169 
170                             if (logger.isInfoEnabled()) {
171                                 logger.info("Register dubbo service " + this.interfaceClass.getName() + " url " + url + " to registry " + registryURL);
172                             }
173 
174                             String proxy = url.getParameter("proxy");
175                             if (StringUtils.isNotEmpty(proxy)) {
176                                 registryURL = registryURL.addParameter("proxy", proxy);
177                             }
178                             // Service providing class(ref)generate Invoker
179                             Invoker<?> invoker = PROXY_FACTORY.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
180                             DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
181                             // Export services and generate Exporter
182                             Exporter<?> exporter = protocol.export(wrapperInvoker);
183                             this.exporters.add(exporter);
184                         }
185                     }
186                 // No registry exists, only export services
187                 } else {
188                     Invoker<?> invoker = PROXY_FACTORY.getInvoker(this.ref, this.interfaceClass, url);
189                     DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
190                     Exporter<?> exporter = protocol.export(wrapperInvoker);
191                     this.exporters.add(exporter);
192                 }
193 
194                 metadataReportService = null;
195                 MetadataReportService metadataReportService;
196                 if ((metadataReportService = this.getMetadataReportService()) != null) {
197                     metadataReportService.publishProvider(url);
198                 }
199             }
200         }
201 
202         this.urls.add(url);
203     }

The first half of the source code above is URL assembly, which is the URL of Dubbo service, roughly as follows:

dubbo://192.168.1.6:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.1.6&bind.port=20880&dubbo=2.7.3&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=5744&qos.port=22222&side=provider&timestamp=1530746052546

This URL indicates that it is a dubbo protocol. The address is the ip of the current server, and the port is the port number of the service to be exposed. You can configure it from dubbo:protocol, and the service interface is the published interface for dubbo:service.

The second half is mainly to judge the scope variable to determine whether to export the service to remote or local. In fact, it is very simple to export to local only to generate the invoker. When exporting to remote, you need to add monitor and generate invoker. The monitor allows Dubbo to check whether the registration center has been hung. The specified exception is thrown, and invoker enables the service consumer to call the service remotely. And we will register to the registration center. Let's take a look at the service release. Because invoker is more important in both consumers and providers, it will be discussed separately later.

5, Service publishing local and remote

1. Publish the service locally

1 private void exportLocal(URL url) {
2         //Carry out local URL Construction
3         URL local = URLBuilder.from(url).setProtocol("injvm").setHost("127.0.0.1").setPort(0).build();
4         //According to local URL To achieve the corresponding Invoker
5         Exporter<?> exporter = protocol.export(PROXY_FACTORY.getInvoker(this.ref, this.interfaceClass, local));
6         this.exporters.add(exporter);
7         logger.info("Export dubbo service " + this.interfaceClass.getName() + " to local registry url : " + local);
8     }

It can be seen that the protocol is rebuilt when it is published locally, and the injvm is represented in the local JVM. The default is 127.0.0.1:0 for both host and port.

2. Service publishing to remote

 1 public <T> Exporter<T> export(Invoker<T> originInvoker) throws RpcException {
 2         //Get the URL,For example: zookeeper://127.0.0.1:2181/......
 3         URL registryUrl = this.getRegistryUrl(originInvoker);
 4         //Get the URL,For example: dubbo://192.168.1.6:20880/.......
 5         URL providerUrl = this.getProviderUrl(originInvoker);
 6         //Get subscriptions URL,For example: provider://192.168.1.6:20880/......
 7         URL overrideSubscribeUrl = this.getSubscribedOverrideUrl(providerUrl);
 8         //Create listener
 9         RegistryProtocol.OverrideListener overrideSubscribeListener = new RegistryProtocol.OverrideListener(overrideSubscribeUrl, originInvoker);
10         //Push listener to subscription Center
11         this.overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
12         providerUrl = this.overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
13         //Export service
14         RegistryProtocol.ExporterChangeableWrapper<T> exporter = this.doLocalExport(originInvoker, providerUrl);
15         Registry registry = this.getRegistry(originInvoker);
16         //Get the URL,such as dubbo://192.168.1.6:20880/.......
17         URL registeredProviderUrl = this.getRegisteredProviderUrl(providerUrl, registryUrl);
18         // Register service providers with the service provider and consumer registry
19         ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
20         // Obtain register parameter
21         boolean register = registeredProviderUrl.getParameter("register", true);
22         // according to register The value of determines whether to register the service
23         if (register) {
24             this.register(registryUrl, registeredProviderUrl);
25             providerInvokerWrapper.setReg(true);
26         }
27         // Subscribe to the registry override data
28         registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
29         exporter.setRegisterUrl(registeredProviderUrl);
30         exporter.setSubscribeUrl(overrideSubscribeUrl);
31         // Create and return DestroyableExporter
32         return new RegistryProtocol.DestroyableExporter(exporter);
33     }

The above source code is mainly used to publish and register services according to the generated URL (registration is expanded in the next section). When you execute doLocalExport, that is, when you publish a local service to a remote location, you will call DubboProtocol's export method, which roughly goes through the following steps to export the service

    • Get the providerUrl from the Invoker, build the service key (Group / service: Version: port), build the DubboExporter, and put the serviceKey into the local map cache
    • Handle local stubs and callback callbacks carried by URLs
    • Open the server port according to the URL to expose the local service. First, query the local cache serverMap with url.getAddress as the key to get the exchange server. If it does not exist, create it through createServer.
    • createServer method, set the heartbeat time, determine whether the transport method (key=server, corresponding to the Transporter Service) in the url supports, set codec=dubbo, and finally bind the server to return according to the url and the ExchangeHandler object. The ExchangeHandler here is very important. It is the Handler called back by the bottom communication layer when the consumer calls, so as to obtain the invoke including the actual Service implementation R executor, which is the inner class of ExchangeHandlerAdapter defined in DubboProtocol class.
    • Return DubboExporter object

The service release diagram is as follows:

 

Vi. service registration

The service registration operation is not necessary for Dubbo, and the registration center can be bypassed by direct service connection. But usually we don't do this. Direct connection is not conducive to service governance, and is only recommended for testing services. For Dubbo, a registry is not necessary, but it is. The source code is as follows:

 1 public void register(URL url) {
 2     super.register(url);
 3     failedRegistered.remove(url);
 4     failedUnregistered.remove(url);
 5     try {
 6         // Template method, implemented by subclass
 7         doRegister(url);
 8     } catch (Exception e) {
 9         Throwable t = e;
10 
11         // Obtain check Parameters, if check = true Exception will be thrown directly
12         boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
13                 && url.getParameter(Constants.CHECK_KEY, true)
14                 && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
15         boolean skipFailback = t instanceof SkipFailbackWrapperException;
16         if (check || skipFailback) {
17             if (skipFailback) {
18                 t = t.getCause();
19             }
20             throw new IllegalStateException("Failed to register");
21         } else {
22             logger.error("Failed to register");
23         }
24 
25         // Record links that failed to register
26         failedRegistered.add(url);
27     }
28 }
29 
30 //Get into doRegister Method
31 
32 protected void doRegister(URL url) {
33     try {
34         // adopt Zookeeper The client creates a node whose path is determined by toUrlPath Method, the path format is as follows:
35         //   /${group}/${serviceInterface}/providers/${url}
36         // such as
37         //   /dubbo/org.apache.dubbo.DemoService/providers/dubbo%3A%2F%2F127.0.0.1......
38         zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
39     } catch (Throwable e) {
40         throw new RpcException("Failed to register...");
41     }
42 }
43 
44 //Get into create Method
45 
46 public void create(String path, boolean ephemeral) {
47     if (!ephemeral) {
48         // If the node type to be created is not a temporary node, check whether the node exists here
49         if (checkExists(path)) {
50             return;
51         }
52     }
53     int i = path.lastIndexOf('/');
54     if (i > 0) {
55         // Recursively create the upper level path
56         create(path.substring(0, i), false);
57     }
58     
59     // according to ephemeral Creates a temporary or persistent node
60     if (ephemeral) {
61         createEphemeral(path);
62     } else {
63         createPersistent(path);
64     }
65 }
66 
67 //Get into createEphemeral
68 
69 public void createEphemeral(String path) {
70     try {
71         // adopt Curator Frame create node
72         client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
73     } catch (NodeExistsException e) {
74     } catch (Exception e) {
75         throw new IllegalStateException(e.getMessage(), e);
76     }
77 }

According to the above method, the configuration information corresponding to the current service (stored in the URL) can be registered in the registry / dubbo/org.apache.dubbo.demo.DemoService/providers /. It directly uses the cursor to create nodes (the cursor is a set of open-source zookeeper client framework of Netflix)

Seven, summary

Here, Dubbo's service registration process is finally explained. The core is that Dubbo uses the specified URL+SPI to find and discover services, locates the registration center through the URL, and then publishes the service URL to the registration center so that consumers can know what the services are, which can see the complex objects such as URL and need to be changed frequently, usually using the builder mode. The Dubbo source code of version 2.7.3 also uses Lambda expressions, a new feature after Java 8, to build implicit functions. And a whole set of processes can be ZooInspector The zk visualization client sees the node we created, provided the registry is zk.

Keywords: Java Dubbo Spring Apache Zookeeper

Added by forsooth on Tue, 11 Feb 2020 14:54:46 +0200