Write in front
stay Local exposure of dubbo services In this article, we analyze the process of local exposure. Let's take a look at how the corresponding service consumer refers, that is, local reference.
Generally, when using local references, the xml configuration is as follows:
<fakeRoot> <dubbo:reference id="scopeLocalService" interface="dongshi.daddy.service.scopelocal.ScopeLocalService" scope="local"/> </fakeRoot>
The java code is as follows:
class FakeCls { public class MyProviderScopeLocalMain { public static void main(String[] args) throws Exception { //Load xml configuration file start ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext("META-INF/spring/scopelocal/provider-scope-local.xml"); providerContext.start(); ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext("META-INF/spring/scopelocal/consumer-scope-local.xml"); consumerContext.start(); ScopeLocalService scopeLocalService = consumerContext.getBean("scopeLocalService", ScopeLocalService.class); scopeLocalService.sayHi("hello scope local!"); System.in.read(); // press any key to exit } } }
The code consumercontext GetBean ("scopelocservice", scopelocservice. Class) is used to obtain the proxy class for calling the service provider locally. In dubbo, the bean corresponding to < dubbo: reference > is a Factory bean , obtain the proxy class we finally need through the factory bean, and then use it to call the service provider, as shown in the following debug:
From the figure, you can finally call com alibaba. dubbo. config. Referenceconfig#createproxy, let's start with this method!
1: createProxy
The source code is as follows:
class FakeCls { // com.alibaba.dubbo.config.ReferenceConfig.createProxy // map: configuration related information, such as /* "owner" -> "dongshidaddy" "side" -> "consumer" "application" -> "dongshidaddy-consumer" "register.ip" -> "192.168.64.1" "methods" -> "sayHi" "scope" -> "local" "dubbo" -> "2.0.2" "pid" -> "29040" "interface" -> "dongshi.daddy.service.scopelocal.ScopeLocalService" "timestamp" -> "1644394930637" */ private T createProxy(Map<String, String> map) { URL tmpUrl = new URL("temp", "localhost", 0, map); // Locally referenced tags final boolean isJvmRefer; // 1: The isInjvm() method has been marked @ Deprecated // 2: It can be considered that the return value is null, that is, only if will be entered if (isInjvm() == null) { // If the url is specified, it means to use the registry. Directly set isJvmRefer to false, which means it is not a local reference if (url != null && url.length() > 0) { isJvmRefer = false; // 2022-02-09 18:08:29 } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) { isJvmRefer = true; } else { isJvmRefer = false; } } else { isJvmRefer = isInjvm().booleanValue(); } if (isJvmRefer) { // Construct url, such as injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?... 127.0.0.1 because it is a local call // Why do local calls need URLs? I think it is to maintain the unity of format with remote calls, so as to minimize the difference and make the code easier to write URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map); // 2022-02-15 10:02:41 invoker = refprotocol.refer(interfaceClass, url); if (logger.isInfoEnabled()) { logger.info("Using injvm service " + interfaceClass.getName()); } // Normal processes are usually remote references. This paper analyzes local references. Therefore, this part will not be read first } else { // Omit remote reference logic } // Service inspection Boolean c = check; if (c == null && consumer != null) { c = consumer.isCheck(); } if (c == null) { c = true; } if (c && !invoker.isAvailable()) { initialized = false; throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion()); } if (logger.isInfoEnabled()) { logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl()); } // When a Service proxy object is created, invoker.com will be called internally invoke return (T) proxyFactory.getProxy(invoker); } }
At 18:08:29 on February 9, 2022, InJvmProtocol Getinjvmprotocol() is to obtain the instance of the InJvmProtocol extension class. isInjvmRefer at 2022-02-09 18:08:29 is to judge whether it is a local reference according to the protocol. For details, refer to 1.1: isInjvmRefer. At 10:02:41 on February 15, 2022, the Invoker of the local service provider is obtained. Here, refprotocol is Protocol$Adaptive. This class is dynamically generated. The source code is as follows:
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!"); } // Gets the method invoked by the service reference 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); } // Expose the method invoked by the service reference 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); } }
Look at the method refer, because our url is injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?... Therefore, the final call is Protocol, and the extension class is com alibaba. dubbo. rpc. Protocol. injvm. Injvmprotocol. If the wrapper is considered, the calling order is Protocol $adaptive = > protocolfilterwrapper = > protocollistenerwrapper = > injvmprotocol. Refer to 2: Protocol for details.
1.1: isInjvmRefer
The source code is as follows:
class FakeCls { // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.isInjvmRefer // url value: temp://localhost?...&scope=local&... public boolean isInjvmRefer(URL url) { final boolean isJvmRefer; // temp://localhost?...&scope=local&... -> local String scope = url.getParameter(Constants.SCOPE_KEY); // url.getProtocol():temp // public static final String LOCAL_PROTOCOL = "injvm"; if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) { isJvmRefer = false; // public static final String SCOPE_LOCAL = "local"; Conditional constants SCOPE_ LOCAL. If equals (SCOPE) is satisfied, enter the judgment and the result is true } else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) { isJvmRefer = true; // public static final String SCOPE_REMOTE = "remote"; } else if (Constants.SCOPE_REMOTE.equals(scope)) { isJvmRefer = false; // Generalized call } else if (url.getParameter(Constants.GENERIC_KEY, false)) { isJvmRefer = false; // 2022-02-14 17:49:09 // Get the Exporter corresponding to the service provider locally } else if (getExporter(exporterMap, url) != null) { isJvmRefer = true; } else { isJvmRefer = false; } return isJvmRefer; } }
At 17:49:09 on February 14, 2022, you try to get the Exporter corresponding to the service provider. If you get it, it means it is a local reference. For details, refer to 1.1.1: getExporter.
1.1.1: getExporter
The source code is as follows:
class FakeCls { // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.getExporter // map: dongshi.daddy.service.scopelocal.ScopeLocalService -> {InjvmExporter@2488} "interface dongshi.daddy.service.scopelocal.ScopeLocalService -> injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?..." // key: injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?... static Exporter<?> getExporter(Map<String, Exporter<?>> map, URL key) { Exporter<?> result = null; // key. Getservicekey() - > group / xxx yyy. Aservice: version number if (!key.getServiceKey().contains("*")) { // Get the Exporte from the exposed map. The InjvmExporter is normally obtained here result = map.get(key.getServiceKey()); } else { // ... } // If no remote call is obtained, null is returned directly if (result == null) { return null; // The generalized call is also a remote call, so null is also returned } else if (ProtocolUtils.isGeneric( result.getInvoker().getUrl().getParameter(Constants.GENERIC_KEY))) { return null; } else { return result; } } }
2: Protocol
2.1: ProtocolFilterWrapper
class FakeCls { @Override public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { // registry: / / protocol, which will be used for remote exposure if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { return protocol.refer(type, url); } // 2022-02-15 10:57:02 return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER); } }
At 10:57:02 on February 15, 2022, continue to call downward, and then add the finally returned Invoker to the Filter call chain. buildInvokerChain obtains public static final String CONSUMER = "consumer"; That is, the Filter of group=consumer is obtained as follows:
@Activate(group = Constants.CONSUMER) public class DubboHystrixFilter implements Filter {}
2.2: ProtocolFilterWrapper
Package the original Invoker and add InvokerListener listeners to listen to exposure and cancellation events. The source code is as follows:
class FakeCls { // com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper.refer public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { // registry: / /, the remote protocol will be referenced here if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { return protocol.refer(type, url); } // 2022-02-15 14:03:50 // The Invoker and InvokerListener will be obtained and the wrapper class ListenerInvokerWrapper will be returned return new ListenerInvokerWrapper<T>(protocol.refer(type, url), Collections.unmodifiableList( ExtensionLoader.getExtensionLoader(InvokerListener.class) .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY))); } }
At 14:03:50 on February 15, 2022, the ListenerInvokerWrapper is the wrapper class of Invoker. The main source code is as follows:
// The Invoker interface is implemented to ensure that the decorated classes have the same behavior public class ListenerInvokerWrapper<T> implements Invoker<T> { private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class); // Decorated invoker private final Invoker<T> invoker; }
This is actually a kind of Decoration design mode Application of.
2.3: InjvmProtocol
The source code is as follows:
class FakeCls { // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.refer @Override public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { // 2022-02-15 13:19:45 return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap); } }
At 13:19:45 on February 15, 2022, the InjvmInvoker is directly encapsulated. The exporterMap contains all the service provider classes, which may be configured as follows:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!-- omit other configuration --> <dubbo:service interface="dongshi.daddy.service.scopelocal.ScopeLocalService" ref="scopeLocalService" scope="local"/> <!--Bean bean definition--> <bean id="scopeLocalService" class="dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl"/> <dubbo:service interface="dongshi.daddy.service.scopelocal.ScopeLocalService1" ref="scopeLocalService1" scope="local"/> <!--Bean bean definition--> <bean id="scopeLocalService1" class="dongshi.daddy.service.scopelocal.ScopeLocalServiceImpl1"/> </beans>
There will be two elements in the exporterMap, as follows: