dubbo service local reference

debug test source code .

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:

Keywords: Dubbo

Added by j_70 on Tue, 15 Feb 2022 09:45:40 +0200