instructions

This paper only discusses the relevant content of Dubbo service exposure, which can be divided into three parts: local exposure, network exposure and registry exposure, compared with 0. Start and 1. Register in Dubbo Architecture. Or the publishing process of the provider. I have limited ability to explore all details clearly, I hope to communicate with you more and learn from each other.

Dubbo service exposed order

The process of service exposure can be roughly divided into four processes: container initialization, service local exposure, service network exposure, and service registry exposure.

Configuration files are read and parsed during container initialization, used by Dubbo

com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser
com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandlerCopy the code

To parse the configuration file, each <dubbo:service /> configuration will be corresponding to the spring container initialization of a ServiceBean instance object, ServiceBean encapsulates the user-defined service interface implementation class instance object. ServiceBean implements spring’s ApplicationListener interface, which handles events that occur in the container. Spring calls the onApplicationEvent method of each Listener when an event is published, and dubbo service exposure is done during this process. The scope of service exposure is configurable, with “None”, “local”, and “remote” optionally configured in <dubbo:service scope=”” /> scope. Remote exposure is not specified by default.

public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if(isDelay() && ! isExported() && ! isUnexported()) {if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: "+ getInterface()); } // Dubbo service exposedexport(a); }}}Copy the code

Dubbo serves local exposure

Dubbo will change host, port, and protocol to 127.0.0.1, port to 0, and protocol to injvm. The resulting URL looks something like this:

Injvm: / / 127.0.0.1 /.net. J4love. Dubbo. Quickstart. Provider. DemoService? Anyhost = true&application = hello world – app&dubbo = 2.5.4 & generic = false&interface = net. J4love. Dubbo. Quickstart. Provider. DemoSe Rvice&methods = echoWithCurrentTime&pid = 9196 & revision 1.0.0 & side of = = provider&timestamp = 1526463775888 & version = 1.0.0, ServiceConfig service native exposed code:

private void exportLocal(URL url) {
        if(! The LOCAL_PROTOCOL. EqualsIgnoreCase (url. GetProtocol ())) {/ / get a local teach service url urllocal= URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(NetUtils.LOCALHOST) .setPort(0); // ref service interface implementation Class instance object, interfaceClass service interfaceClass instance <? > exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass,local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); }}Copy the code

First, obtain an Invoker instance through ProxyFactory. Invoker encapsulates the instance of service interface implementation Class and Class instance of service interface type, and Invoker will call the function in the service.

The ProxyFactory extension point uses JavassistProxyFactory by default. The JdkProxyFactory implementation is very simple, every time the Invoker calls the service API (function in the service interface), it will reflect the call:

public class JdkProxyFactory extends AbstractProxyFactory {

    @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<? >[] interfaces) {return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
    }

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, Class<? >[] parameterTypes, Object[] arguments) throws Throwable { Method method = proxy.getClass().getMethod(methodName, parameterTypes);returnmethod.invoke(proxy, arguments); }}; }}Copy the code

JavassistProxyFactory gets a Wrapper object before creating Invoker. The Wrapper object is used to dynamically generate code, dynamically generate Class instances, and get an instance of the Wrappr object. Performance is improved by making methods in the service interface available for direct object invocation, without reflection, and cached by already generated Wrapper instances.

public class JavassistProxyFactory extends AbstractProxyFactory {

    @SuppressWarnings("unchecked") public <T> T getProxy(Invoker<T> invoker, Class<? >[] interfaces) {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    public <T> Invoker<T> getInvoker(T proxy, Class<T> typeFinal Wrapper = wrapper.getwrapper (proxy.getClass().getName().indexof ())'$') < 0? proxy.getClass() :type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, Class<? >[] parameterTypes, Object[] arguments) throws Throwable {returnwrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); }}; }}Copy the code

The Wrapper invokeMethod is dynamically generated, and when invokeMethod is called, the invokeMethod method is directly called with an object, without reflection. Dynamically generated invokeMethod function code:

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        net.j4love.dubbo.quickstart.provider.DemoService w;
        try {
            w = ((net.j4love.dubbo.quickstart.provider.DemoService) The $1); } catch (Throwable e) { throw new IllegalArgumentException(e); } the try {/ / in.net. J4love. Dubbo. Quickstart) provider. DemoService interface / / statement in a function call"echoWithCurrentTime" 
            if ("echoWithCurrentTime".equals($2) && $3.length == 1) {
                return ($w) w.echoWithCurrentTime((java.lang.String) $4[0]);
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class net.j4love.dubbo.quickstart.provider.DemoService.");
    }Copy the code

Once you have Invoker, you should have a friend.

When the ExtensionLoader retrieves an extension of an extension point, it checks whether the extension point type has a wrapper type extension and, if so, wraps the obtained source extension with the wrapper type extension (see createExtension function of ExtensionLoader). InJvmProtocol is wrapped in two layers. The ProtocolListenerWrapper wrapper provides a Exporter listener that responds to ExporterListener exposures. The observer mode is used to perform operations on ExporterListener exposures. The ProtocolFilterWrapper does not contain any wrapper for my friend. It handles the Invoker layer by layer with a set of filters. When the Invoker is called, The chain that passes through the Filter eventually calls Invoker. The structure looks like this:

ProtocolFilterWrapper #buildInvokerChain

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        returninvoker.isAvailable(); } // Only this function will call the filter chain, Public Result Invoke (Invocation) throws RpcException {return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        returninvoker.toString(); }}; }}return last;
    }Copy the code

After these processes the service local leak is complete. The relationship between a custom service interface, Invoker, and MY Exporter looks like this:

The Dubbo service network is exposed

Dubbo’s network exposure is also through an Invoker to my Exporter process, in which network communication is dealt with.

The network leakage process of the service is done in the process from Invoker to Exporter.

Transporter, ChannelHandler is a Transporter, ChannelHandler, which is used to handle basic network connection establishment and disconnection, message receiving and sending, communication exceptions, data encoding, decoding, serialization, deserialization, and other low-level tasks.

Exchanger Handler Is used for information exchange and unidirectional communication for service invocation.

Exchange Server Server server abstraction layer, the implementation can be the server side of Netty, Mina, and Grizzly.

Transporter, Exchanger, and Exchange Server are all related to network communication.

For IP addresses that are exposed by the service, Dubbo defaults to inetaddress.getLocalHost () to get the local IP address (this retrieves the host name from the system and resolves it to InetAddress), <dubbo:protocol host=”” /> To specify the IP address.

Dubbo service registry is leaking

The process of registering a service into the registry is to register it with the registry using the URL string of the service. Available implementations are Dubbo, Multicast, ZooKeeper, redis. The registry does things such as registering services, discovering services, and synchronizing data from registered nodes. The summary is brief, and the details involved in the implementation require a lot of attention and consideration.

The above discussion is from a global perspective, there are not many details involved, in fact, there are many details involved in the implementation. This is based on my personal learning habits, first the whole and then the specific order of practice. The details involved will be discussed in smaller pieces later.

Have you learned anything after reading this article? Please share it with more people

Wechat follow “black hat technology” plus star label, see selected IT technology articles