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®istry=zookeeper×tamp=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×tamp=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.