Write in front
The Service provided by the Service provider is marked with a class annotated with @ Service. If you want to be used by the Service consumer, you must expose the Service, that is, let the Service consumer get the com that encapsulates the Service information alibaba. dubbo. common. URL object string. The current Service is exposed in the following three ways:
Remote exposure: register the service information to the remote registry, such as configuration<dubbo:service scope="remote" />. Local exposure: JVM Internal call. Because the information is already in memory, the call information can be obtained directly through memory, so it is called local exposure, such as configuration<dubbo:service scope="local">. Do not expose: do not expose services. This method can be ignored, such as configuration<dubbo:service scope="none">.
This article shares the local exposure. The relevant source code is in the module Dubbo RPC injmv, as shown in the following figure:
stay dubbo service provider configuration In this article, we actually analyzed the contents exposed by some services. You can see that in order to undertake this article, there will be some overlapping contents. Let's start with method com alibaba. dubbo. config. ServiceConfig. Doexporturls to start analysis.
1: doExportUrls
The source code is as follows:
class FakeCls { private void doExportUrls() { // 2022-01-21 18:25:43 List<URL> registryURLs = loadRegistries(true); // Loop all protocol exposed services to all registry addresses // Protocol: protocols, i.e. < Dubbo: Protocol > Settings // Service: set through < Dubbo: Service > // Registry address: registryURLs, specified by < Dubbo: Registry > for (ProtocolConfig protocolConfig : protocols) { // 2022-01-21 18:34:22 doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } }
Get the addresses of all configured registries at 18:25:43 on January 21, 2022. Refer to 1.1: loadRegistries for details. At 18:34:22 on January 21, 2022, the service is registered in the registry according to the specified agreement. For details, refer to 1.2: doExportUrlsFor1Protocol.
1.1: loadRegistries
The local service is leaked during the explanation of this article, and the information here will not be used. However, for the sake of integrity, it is put here. Friends who are interested in this part can refer to it dubbo's service remote exposure Article analysis.
1.2: doExportUrlsFor1Protocol
The service is registered in the registry according to the specified protocol, which is divided into remote disclosure and local disclosure. The local disclosure will not register the service in the registry. Because it is the same JVM, the information can be obtained directly from the JVM. Because this paper focuses on the local service disclosure, the relevant source code of remote disclosure will be selectively ignored. For the analysis of this part, Can only refer to dubbo's service remote exposure Article analysis. The source code is as follows:
class FakeCls { private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { // The name of the protocol, such as < dubbo: protocol name = "dubbo" port = "20826" >, here is dubbo // Agreement: is to reveal your own way String name = protocolConfig.getName(); // If not, dubbo is used by default if (name == null || name.length() == 0) { name = "dubbo"; } //***Omit code related to building URL***// // Get the scope. If it is a local burst, configure it as follows: < Dubbo: service interface = "Dongshi. Daddy. Service. Scopelocal. Scopelocal service" ref = "scopelocal service" scope = "local" / > // It is also mentioned at the beginning of the article that it can be configured as remote, which represents remote disclosure, and none represents non disclosure String scope = url.getParameter(Constants.SCOPE_KEY); // If scope="none" is configured, no operation will be carried out. At this time, no burst leakage will be carried out, that is, it will not be used externally if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { // If the scope is not remote, the local service is used if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { // 2022-01-22 12:25:51 exportLocal(url); } // If the scope is not local, the remote service is used if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) { // ***Omit remote service burst logic * * *// } } // Add leak service url this.urls.add(url); } }
At 12:25:51 on January 22, 2022, there was a local service leak. Refer to 1.3: exportLocal for details.
1.3: exportLocal
The source code is as follows:
class FakeCls { private void exportLocal(URL url) { // url.getProtocol(): usually dubbo // Constants.LOCAL_PROTOCOL: injvm // Why do you make this judgment??? if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { // Build the URL of local, such as // injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?accesslog=true&anyhost=true&application=dongshidaddy-provider&bean.name=dongshi.daddy.service.scopelocal.ScopeLocalService&bind.ip=192.168.2.107&bind.port=20826&dubbo=2.0.2&generic=false&interface=dongshi.daddy.service.scopelocal.ScopeLocalService&methods=sayHi&owner=dongshidaddy&pid=6324&scope=local&side=provider×tamp=1642823993714 URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(LOCALHOST) .setPort(0); // public static final String SERVICE_IMPL_CLASS = "service.classimpl"; // url.getServiceKey(): dongshi.daddy.service.scopelocal.ScopeLocalService // getServiceClass(ref: class dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl // Store the information of the service class in the StaticContext StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref)); // 2022-01-22 17:21:22 Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } } }
The following part of this section is a little around. Everyone is patient. If you don't understand, read it several times!!!
At 17:21:22 on January 22, 2022, the protocol is defined as private static final protocol protocol = extensionloader getExtensionLoader(Protocol.class). getAdaptiveExtension();, You can see that it is acquisition Adaptive extension class , which can also be seen from the Protocol interface. The source code is as follows:
/** * Protocol. (API/SPI, Singleton, ThreadSafe) */ @SPI("dubbo") public interface Protocol { int getDefaultPort(); @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; void destroy(); }
You can see that the export method is labeled @Adaptive Annotated, here 'protocol'
It is Protocol#Adaptive, which is easy to understand, because acquisition is a dynamically generated adaptive subclass through which the final call is implemented by a real extension class. If you want to know who the call is, you need to know what the generated code looks like. We can obtain its contents through the following steps:
stay com.alibaba.dubbo.common.extension.ExtensionLoader.createAdaptiveExtensionClass Code in ClassLoader classLoader = findClassLoader();Add condition variable"code.contains("Protocol$Adaptive")",Then run the program again, you can stop here and get code The content of.
Here's what I got:
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } }
We focus on the export method. We can see that it calls url Getprotocol () is the name of the target extension class, so what is the value? Our url is injvm://127.0.0.1/... You can see that the protocol is injvm. Who is the corresponding extension class? You can find it from the file meta-inf / Dubbo / internal / com alibaba. dubbo. rpc. Find the answer in the protocol, where the key is injvm, and the configuration item content is injvm = com alibaba. dubbo. rpc. protocol. injvm. Injvmprotocol, so the extension class finally called is com alibaba. dubbo. rpc. protocol. injvm. Injvmprotocol, but is that true? Let's take a look at debug, as shown below:
As can be seen from the figure, QosProtocolWrapper, protocollistenerwrapper and protocolfilterwrapper are also called respectively. This is the Wrapper class of Protocol. We start from meta-inf / Dubbo / internal / com alibaba. dubbo. rpc. It can be seen from the Protocol, as shown in the following figure:
For more information about Wrapper, please refer to SPI Wrapper analysis of dubbo .
The final calling procedure is Protocol $adaptive - > qosprotocolwrapper - > protocollistenerwrapper - > protocolfilterwrapper - > injvmprotocol. Specifically, we analyze it in 2: Protocol.
2: Protocol
The source code is as follows:
@SPI("dubbo") public interface Protocol { // Gets the default port number of the current protocol when no port is configured int getDefaultPort(); // The burst service can be called remotely // 1: The protocol object needs to record the address of the remote source after receiving a request through API rpccontext getContext(). setRemoteAddress() // 2: The method must be idempotent [a ɪ' demp ə t ə nt]), that is, there is no difference in exposing a URL through one or more calls of this method // 3: The Invoker instance needs to be passed in by the framework, and the protoco l extension class needs to be used, such as when adaptive // Return value: exporter < T >, which refers to the leaked service. Later, if you need to cancel the disclosure, you need to use it @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; void destroy(); }
2.1: Protocol$Adaptive
Refer to 1.3: exportLocal for how to obtain such information.
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { // Because there is no @ Adaptive annotation, Java. XML is thrown directly lang.UnsupportedOperationException throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } // Because there is no @ Adaptive annotation, Java. XML is thrown directly lang.UnsupportedOperationException public int getDefaultPort() { throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } }
2.2: ProtocolListenerWrapper
The source code is as follows:
class FakeCls { @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // The following remote bursts will be executed, which can be ignored here, because the url starts with injvm: / / if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } // 2022-01-23 19:29:28 return new ListenerExporterWrapper<T>(protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class) .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY))); } }
2022-01-23 19:29:28 protocol Export (invoker) continues to call the decorated protocol class. What is called here is ProtocolFilterWrapper. For this class, refer to 2.3: ProtocolFilterWrapper. Collections. unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class). getActivateExtension(invoker.getUrl(), Constants. EXPORTER_ LISTENER_ Key) is to use keyexporter Listener, get the value from the url to get the exporterlistener extension class to activate. The source code of the ListenerExporterWrapper constructor is as follows:
class FakeCls { public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {} }
The function of this listener is to monitor the completion of the Exporter's burst and cancel the burst.
2.3: ProtocolFilterWrapper
It is mainly used to add a Filter chain to the Invoker. The logic of the Filter will be called before calling the real service method. For details, refer to 2.3.1: export.
2.3.1: export
The source code is as follows:
class FakeCls { @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // The protocol required here is registry: / /, that is, remote exposure, and injvm: / /, so it can be ignored if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } // 2022-01-24 16:02:53 // The protocol here is InJvmProtocol return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)); } }
Add Filter chain at buildInvokerChain at 16:02:53 on January 24, 2022. Refer to 2.3.2: buildInvokerChain for details.
2.3.2: buildInvokerChain
The source code is as follows:
class FakeCls { private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; // Gets the collection of activated Filter extension classes List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (!filters.isEmpty()) { for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); final Invoker<T> next = last; // Encapsulate the Filter into an Invoker. When invoking the invoke method, the next Invoker will be called in an internal chain. The last Invoker is the Invoker of the target service class method last = new Invoker<T>() { @Override public Class<T> getInterface() { return invoker.getInterface(); } @Override public URL getUrl() { return invoker.getUrl(); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { // This line of code is key. Call the Filter class method with next as a parameter. Inside the Filter class method, we can use invoker Invoke to continue to call down, and finally call to the real service class method return filter.invoke(next, invocation); } @Override public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; } }
The later filters are executed first and first, such as Filter1 - > filte2 - > Filter3 - >... - > Service class methods.
2.4: InjvmProtocol
This class is the implementation class of the Injvm protocol. Let's start with the entry method export.
2.4.1: export
The source code is as follows:
class FakeCls { @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap); } }
Mainly created the InjvmExporter object. For details about this object, refer to 3: Exporter.
3: Exporter
The interface is used to expose services based on relevant protocols. The interface source code is as follows:
public interface Exporter<T> { // Get internal Invoker Invoker<T> getInvoker(); // Cancel exposure void unexport(); }
The main class diagrams are as follows:
Next, let's start with the class AbstractExporter and look at the following.
3.1: AbstractExporter
The source code is as follows:
public abstract class AbstractExporter<T> implements Exporter<T> { protected final Logger logger = LoggerFactory.getLogger(getClass()); // Internal Invoker private final Invoker<T> invoker; // Are there no exposed marks private volatile boolean unexported = false; public AbstractExporter(Invoker<T> invoker) { if (invoker == null) throw new IllegalStateException("service invoker == null"); // Must be an interface if (invoker.getInterface() == null) throw new IllegalStateException("service type == null"); // There must be an exposed URL if (invoker.getUrl() == null) throw new IllegalStateException("service url == null"); this.invoker = invoker; } @Override public Invoker<T> getInvoker() { return invoker; } // To cancel exposure is to call getinvoker() destroy(); @Override public void unexport() { if (unexported) { return; } unexported = true; getInvoker().destroy(); } @Override public String toString() { return getInvoker().toString(); } }
3.2: InjvmExporter
Subclass of AbstractExporter. The source code is as follows:
class InjvmExporter<T> extends AbstractExporter<T> { // The service key is generally the fully qualified class name of the service interface private final String key; // Exposed exporters private final Map<String, Exporter<?>> exporterMap; InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) { super(invoker); this.key = key; this.exporterMap = exporterMap; exporterMap.put(key, this); } // Cancel exposure @Override public void unexport() { super.unexport(); exporterMap.remove(key); } }
3.3 ListenerExporterWrapper
The Wrapper class of Exporter with listening function has the following source code:
public class ListenerExporterWrapper<T> implements Exporter<T> { private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class); private final Exporter<T> exporter; // Registered exposure listener private final List<ExporterListener> listeners; public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) { if (exporter == null) { throw new IllegalArgumentException("exporter == null"); } this.exporter = exporter; this.listeners = listeners; // Constructor execution represents that the service is exposed, and the exposed method of the corresponding listener is exported if (listeners != null && !listeners.isEmpty()) { RuntimeException exception = null; for (ExporterListener listener : listeners) { if (listener != null) { try { listener.exported(this); } catch (RuntimeException t) { logger.error(t.getMessage(), t); exception = t; } } } if (exception != null) { throw exception; } } } @Override public Invoker<T> getInvoker() { return exporter.getInvoker(); } @Override public void unexport() { // Cancel the exposure and execute the listener's unexported method try { exporter.unexport(); } finally { if (listeners != null && !listeners.isEmpty()) { RuntimeException exception = null; for (ExporterListener listener : listeners) { if (listener != null) { try { listener.unexported(this); } catch (RuntimeException t) { logger.error(t.getMessage(), t); exception = t; } } } if (exception != null) { throw exception; } } } } }
ExporterListener reference 4: ExporterListener.
4: ExporterListener
The source code is as follows:
@SPI public interface ExporterListener { // Method invoked by service exposure void exported(Exporter<?> exporter) throws RpcException; // Service unexposed method invoked void unexported(Exporter<?> exporter); }
Class diagram is as follows:
Next, take a look at the unique implementation class ExporterListenerAdapter, as follows:
public abstract class ExporterListenerAdapter implements ExporterListener { @Override public void exported(Exporter<?> exporter) throws RpcException { } @Override public void unexported(Exporter<?> exporter) throws RpcException { } }
It is just an empty implementation without actual logic.