1. Dubbo configuration parsing
1.1 Configuration Resolution
First understand the meaning of elements in a configuration file:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:protocol name="dubbo"/>
<bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
<beams/>
Copy the code
- XMLNS :dubbo: Declares a namespace prefixed with dubbo, followed by a link to give dubbo a unique name. His value with xsi: schemaLocation specified in the second value dubbo.apache.org/schema/dubb…
Caused by: org.xml.sax.SAXParseException; systemId: http://dubbo.apache.org/schema/dubbo/dubbo.xsd; lineNumber: 6; columnNumber: 68; TargetNamespace.1: Should be namespace'dubbo', but the target namespace of the schema document is'http://dubbo.apache.org/schema/dubbo'.Copy the code
In addition, also need to under the meta-inf/spring. The key corresponding handler, specify when meet this kind of namespace, spring using the specified handler to load the configuration, if inconsistent throws an exception:
Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://dubbo.apache.org/schema/dubbo]
Copy the code
- XMLNS: What is the default namespace that all elements without prefixes use by default
- Xsi :schemaLocation: This is used with a namespace. The previous dubbo.apache.org/schema/dubb… The location of the schema
- For the inside of the elements, such as < dubbo: protocol / > will be used to dubbo corresponding XSD – > dubbo.apache.org/schema/dubb… The element type corresponds to the value specified in the type attribute.
1.1.1 Design Analysis based on Schema
Under Dubo-config-spring, there is a dubo.xsd to constrain tags and corresponding attributes when configured with XML. When Spring parses to a namespace, it goes to the spring.handlers and spring.schemas files to find a corresponding Handler for parsing the corresponding XML configuration information. For example,
http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
Copy the code
When spring dubbo.apache.org/schema/dubb…
1.1.2 XmL-based Configuration Principle Parsing
As you can see from above, the configuration of XML constraints the role of dubo.xsd, as well as its parsing Handler.
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
Copy the code
This part is mainly the PARSING logic of Spring’S XML. It will send the corresponding element to a specified Parser for parsing and save it in a map. When it encounters the element in the future, it will use the corresponding Parser for parsing. Parsing steps
- Replace the placeholder information in the Id to get an Id
- If the ID is empty, the ID is generated by the name, and if the name is also empty, the name of the class is used
- Check whether there is an identical ID in the current container. If so, throw an exception. If not, register the bean information
- Set the value of propertyValue and add the ID information
- Depending on the type of the element, different configuration information is parsed
- Find all the parameters configured in the XML and place them in the beanDefinition property
1.1.3 Annotation-based Configuration Principle Analysis
1.1.3.1 @ EnableDubbo
The EnableDubbo annotation is used to enable the Dubbo service. When Spring starts initialization, the annotation information in the corresponding configuration header is obtained. In the header of the @enableDubbo annotation, there are two important @enableDubboConfig and @DubBoComponentScan annotations. Each of these annotations contains an @import annotation.
- @EnableDubboConfig
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
/**
* It indicates whether binding to multiple Spring Beans.
*
* @return the default value is <code>true</code>
* @revised2.5.9 * /
boolean multiple(a) default true;
}
Copy the code
@ Import the values in the annotations DubboConfigConfigurationRegistrar ImportBeanDefinitionRegistrar is achieved, the Spring container load at the time of loading to @ Import annotations, And executes the registerBeanDefinitions method in the class specified by the value.
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if(! annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); }}Copy the code
All annotations are recursively iterated, if the annotation name is not Import and does not start with Java. The registerBeanDefinitions method is then executed.
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
// Single Config Bindings
registerBeans(registry, DubboConfigConfiguration.Single.class);
// The configured value starts with dubo.xxxs, which can be judged from Multiple annotations. Its main function is annotations in Multiple
if (multiple) { / / Since 2.6.6 at https://github.com/apache/dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}
// Register some configuration related beans with the Spring container
/ / Since 2.7.6registerCommonBeans(registry); }}Copy the code
When set to multiple, such as dubbo applations. Name1. Test1 = test1Value will at the time of loading ApplicationConfig, name1 will be the name of the bean, injected into the container, Test1 /test1Value is saved to the attribute in BeanDefination as the keyValue value. This parameter is available when there are multiple configuration centers.
- @DubboComponentScan
A scan of Dubbo’s annotations.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
* {@code @DubboComponentScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan
*/
String[] value() default {};
/**
* Base packages to scan for annotated @Service classes. {@link #value()} is an
* alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the base packages to scan
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated @Service classes. The package of each class specified will be
* scanned.
*
* @return classes from the base packages to scan
*/Class<? >[] basePackageClasses()default {};
}
Copy the code
He will also go to perform @ Import of DubboComponentScanRegistrar. RegisterBeanDefinitions under class. The main steps are divided into three parts:
- Get the path that the annotation writes to all scannable packages
- Create a ServiceAnnotationBeanPostProcessor Bean, and set his constructor parameters for all packages can scan path
- Inject the beans generated in 2 into the container, and let Spring do the rest.
The key code for dealing with beans in 2 is as follows:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
/ / @ since 2.7.5
registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
// Resolve the path of the package to be scanned. Replace placeholders that may exist in it, etc
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if(! CollectionUtils.isEmpty(resolvedPackagesToScan)) {// Scan packet information
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!"); }}}/**
* Registers Beans whose classes was annotated {@link Service}
*
* @param packagesToScan The base packages to scan
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
/ / refactor @ since 2.7.7
// Specify to scan annotation types under serviceAnnotationTypes
serviceAnnotationTypes.forEach(annotationType -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
// It scans the package for each resource under the package, loads its Metadata information, and determines if it is an annotation marked with serviceAnnotationTypes
// And inject into the container
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
// Generates a BeanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if(! CollectionUtils.isEmpty(beanDefinitionHolders)) {for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// Inject into the Bean container
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
beanDefinitionHolders +
" } were scanned under package[" + packageToScan + "]"); }}else {
if (logger.isWarnEnabled()) {
logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
+ packageToScan + "]"); }}}}Copy the code
2. Dubbo service exposure principle
Configuration carries initialization
Configuring priorities (from high to low)
- -d has the highest priority for parameters passed to the JVM
- Code or XML configuration takes precedence
- The configuration file has the lowest priority
For run-time property values (Dubbo configuration is affected by the provider), follow these rules:
- If the configuration rules are specified on the provider end, the configuration rules are automatically transmitted to the client transparently
- If the client is configured with the corresponding properties, the server is overwritten
Exposure mechanisms for remote services
At the time of the Spring container startup, will go to get all the ApplicationListener, then execute the onApplicationEvent OneTimeExecutionApplicationContextEventListener method. Will call into the DubboBootstrapApplicationListener onApplicationContextEvent method, detect is container startup event, she will go DubboBootstrap start method to carry on some of the initialization tasks, Then start exposing the service.
The overall process
According to the figure above, the exposure process is divided into six steps:
- According to ServiceConfig, configure the parameter information required for exposure
- Create a ProxyFactory using Dubbo’s SPI mechanism (using JavAssist by default)
- Based on the ProxyFactory created, a proxy object is created using bytecode technology
- Create an Invoker object with the proxy object and the parameter information generated in step 1
- Using the SPI mechanism, take a Protocol and convert it into an Exportor based on the specific Protocol
- Exposed services
When performing service exposures, be aware that the first exposed entry is in the ProtocolFilterWrapper exporter. The specific logic is as follows:
- When the SPI mechanism is used to obtain the Protocol, the method is adaptive extension point, which is selected according to the Protocol line in the url (adaptive generated code is as follows 🙂
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException(
"Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
+ url.toString()
+ ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension =
(org.apache.dubbo.rpc.Protocol)
ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.export(arg0);
}
public void destroy(a) {
throw new UnsupportedOperationException(
"The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort(a) {
throw new UnsupportedOperationException(
"The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException(
"Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
+ url.toString()
+ ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension =
(org.apache.dubbo.rpc.Protocol)
ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.refer(arg0, arg1);
}
public java.util.List getServers(a) {
throw new UnsupportedOperationException(
"The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); }}Copy the code
The key is to be a friend
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException(
"Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
+ url.toString()
+ ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension =
(org.apache.dubbo.rpc.Protocol)
ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.export(arg0);
Copy the code
Therefore, the extension point with the name “Registry” is eventually selected as follows
So will use the org. Apache. Dubbo. Registry. Integration. RegistryProtocol. But since Protocol has two wrapper classes, the configuration is as follows,
filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
Copy the code
So when you retrieve an extension point, you end up returning information about the wrapper class. The following methods are in ExtensionLoader
// Determine if it is a wrapper class. If so, initialize the wrapper class and inject the instance
if(wrap) { List<Class<? >> wrapperClassesList =new ArrayList<>();
// Sort the wrapper classes first
if(cachedWrapperClasses ! =null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for(Class<? > wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);if (wrapper == null|| (ArrayUtils.contains(wrapper.matches(), name) && ! ArrayUtils.contains(wrapper.mismatches(), name))) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}}}Copy the code
- Export is called once inside the wrapper class until the export method of the RegistryProtocol class is called
- Exposed services
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// Get the parameter of registry in the URL, and set the protocol of the current URL to the value of this parameter, if the parameter does not exist, return directly. Replace it with the registration protocol, for example, ZooKeeper
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally, grabs the export parameter in the URL. If it is null, an exception will be raised, otherwise a new URL will be created
URL providerUrl = getProviderUrl(originInvoker);
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
// TODO service override for the console. Save it for now, and continue later
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//export Invoker creates a Netty service that listens for addresses and ports
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
// The SPI mechanism is used to obtain a Registry. The default is dubbo, which is selected according to the protocol parameter in the URL
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// Write the service to ZooKeeper
register(registryUrl, registeredProviderUrl);
}
// register stated url on provider model
// Is written to providers in the ServiceRepository to record information about the URL of the service provider and the exposed state
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
// Deprecated! Subscribe to override rules in 2.6.x or before.
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
// A user-definable listener
notifyExport(exporter);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<>(exporter);
}
Copy the code
Inside is a final ExporterChangeableWrapper exporter = doLocalExport (originInvoker providerUrl); A DubboExporter(if you are using the Dubbo protocol) is first created and exporterMap is exported. Key is a exporter consisting of Group, Version, port, and Path, and value is a exporter that contains invoker. It then goes and opens a network server (netty by default) and listens on port 5. Write the registeredProviderUrl to the corresponding service (zooKeeper is recommended) according to registryUrl. 6. Modify the state and return to finish exposing the service
Creating a server (default netty)
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
// Get a server according to Address
ProtocolServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
// Create a servce if it is not found
if (server == null) {
// Address is used as the key to ensure that each address has only one nettyserverMap.put(key, createServer(url)); }}}else {
// server supports reset, use together with overrideserver.reset(url); }}}Copy the code
If there is an Address, it will not create a server. If there is no Address, it will create a server and put it in the cache.
private ProtocolServer createServer(URL url) {
// Add some server parameters
url = URLBuilder.from(url)
// send readonly event when server closes, it's enabled by default
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// enable heartbeat by default
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
// Set the codec, default is dubbo
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
// Check whether there is an extension point for the network server. The default is netty
if(str ! =null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
ExchangeServer server;
try {
// Create a Netty service and bind and listen to host
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ")" + e.getMessage(), e);
}
str = url.getParameter(CLIENT_KEY);
if(str ! =null && str.length() > 0) {
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if(! supportedTypes.contains(str)) {throw new RpcException("Unsupported client type: "+ str); }}return new DubboProtocolServer(server);
}
Copy the code
The default parameters for the network server will be set first. Codec is the codec for web services, which defaults to Dubbo. The default HEADER (SPI mechanism) is used. If the user has configured the HEADER in the ProviderConfig, the specified RECOVERY is used. The next step is to use the Transporter to bind the address to the server. During this process, the ProtocolConfig handler is wrapped as follows:Then, a Netty server is created based on the prepared data and listens for the address.
Write to registry
- Start by getting a registry creation factory
- Using the factory, get an instance of the registry
- Calling the Registry method
private void register(URL registryUrl, URL registeredProviderUrl) {
// registryFactory creates a factory using adaptive
// getRegistry will get a Registry based on protocol first, if not, use dubbo
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registeredProviderUrl);
}
Copy the code
RegistryFactory is the value injected using the set method when instantiating the RegistryProtocol to get the extension point. RegistryFactory has a wrapper class org. Apache. Dubbo. Registry. RegistryFactoryWrapper, the instantiation is returns the wrapper classes. In the call registryFactory. GetRegistry (registryUrl), will be called to getRegistry methods of wrapper classes, will use registryFactory according to the URL to get a Registry instance, It is then wrapped as ListenerRegistryWrapper and returned, so it is ListenerRegistryWrapper that ends up calling the Registry method. RegistryFactory When instantiating the RegistryProtocol, the Set method is used to inject the RegistryFactory, and when fetching the extension point, the implementation class tagged @Adaptive under the RegistryFactory is fetched. Since the implementation class could not be found, javassist technology was used to generate and load an Adaptive class. The @adaptive method marked under the interface would be implemented and the extension point would be obtained according to protocol in the URL. If protocol is empty, The default DubboProtocolFactory is used. This is followed by the Registry method called above, which will eventually call ZookeeperRegister for service exposure because it contains ZookeeperRegistry inside ListenerRegistryWrapper. Let’s do another pictureZookeeperRefistry inherits from FailbackRegistry, which is invoked because it has not been overridden.
if(! acceptable(url)) { logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
return;
}
// Call the parent class's method to write the URL to the cache of the registered URL
super.register(url);
// If the URL fails to register, it is removed from the failed queue first
removeFailedRegistered(url);
// If the URL is unregistered, the unregistered URL is removed from the queue
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
// Call a concrete instance to write data. Then there's zK
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& !CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
}
Copy the code
This remote service exposure is complete
Exposure mechanisms for local services
Local service exposure is much simpler than remote service exposure, mainly divided into two steps:
- Create a proxy object invoker using the proxy project.
- Create an InjvmExporter object and place invoker into the locally exposed map to complete the exposure
private void exportLocal(URL url) {
// Set protocol to injvm, which will be used later to obtain extension points
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
// getInvoker creates a proxy object
// Export creates an InjvmExporter and puts invoker into the cache map to expose itExporter<? > exporter = PROTOCOL.export( PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
Copy the code