Dubbo learning record (16)--service call [2] - Invoker call, ProxyFactory, Protocol, Filter, Exchanger, Transporter extension point

Pre-learning of service calls [2]

There are many things involved in service calls, which need to be thoroughly understood before they can be connected together;

Packaging of DubboInvoker on the server side

The generation of DubboInvoker is created during the process of service export; due to Dubbo's SPI mechanism, the generation of DubboInvoker will be processed by multiple wrapper classes;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
        // 当前服务没有被导出并且没有卸载,才导出服务
        if (!isExported() && !isUnexported()) {
    
    
            // 服务导出(服务注册)
            export();
        }
    }

In the final layer-by-layer call, an Invoker instance will be created in the doExportUrlsFor1Protocol of ServiceConfig;

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    
    
		//...省略部分代码;
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
    
    

            // export to remote if the config is not local (export to local only when config is local)
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
    
    
  
                if (CollectionUtils.isNotEmpty(registryURLs)) {
    
    
                    // 如果有注册中心,则将服务注册到注册中心
                    for (URL registryURL : registryURLs) {
    
    

                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                        // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
    
    
                    if (logger.isInfoEnabled()) {
    
    
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);
    }

This Invoker is generated through the PROXY_FACTORY factory. ProxyFactory is an interface extension point. The name of the extension point implementation class is specified on the interface; that is, the JavaasistProxyFactory is used to generate this proxy instance; the getInvoker method passes several important parameters
:

  • ref : represents the service implementation class;
  • interfaceClass: represents the implemented interface;
  • registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()) : The registry URL of the service

ProxyFactory dynamic proxy extension point

@SPI("javassist")
public interface ProxyFactory {
    
    
	//省略部分代码
    @Adaptive({
    
    PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

The known extension point implementation classes are:

  • org.apache.dubbo.rpc.proxy.JdkProxyFactory
    This dynamic proxy extension implements the JDK dynamic proxy generation proxy instance used by the class;
  • org.apache.dubbo.rpc.proxy.JavassistProxyFactory
    This dynamic proxy instance uses Javassit technology to generate proxy instances;

The first layer: AbstractProxyInvoker

Calling ProxyFactory#getInvoker eventually returns an AbstractProxyInvoker

JavassistProxyFactory

public class JavassistProxyFactory extends AbstractProxyFactory {
    
    

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

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    
    
        final Wrapper 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 {
    
    

                // 执行proxy的method方法
                // 执行的proxy实例的方法
                // 如果没有wrapper,则要通过原生的反射技术去获取Method对象,然后执行
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

  • The wrapper.getWrapper method will obtain a wrapper object according to the interface class. If it has been created, it will get the Wrapper instance directly from the cache. If not, it will generate a Java dynamic proxy class using ClassGenerator in the makeWrapper method through Javaassist technology. ;
  • In the proxy method invokeMethod in the dynamic proxy class, call the business method of the implementation class;
	Wrapper
    public static Wrapper getWrapper(Class<?> c) {
    
    
    	//省略部分代码;
        Wrapper ret = WRAPPER_MAP.get(c);
        if (ret == null) {
    
    
            ret = makeWrapper(c);
            WRAPPER_MAP.put(c, ret);
        }
        return ret;
    }

Several parameters involved in invokeMethod:

  • proxy : implementation class
  • methodName : interface method name;
  • parameterTypes : parameter types
  • arguments : parameter values;

Finally, a dynamic proxy instance is generated and returned. The type of the proxy instance is AbstractProxyInvoker. This Invoker is directly linked to our implementation class ref, which is the bottom-level Invoker;

JDK extension point implementation class JdkProxyFactory

  • It is necessary to obtain the Method metadata of the class according to methodName and paramterTypes, and then execute the business method through Method.invoke(proxy, arguments); this is a common operation of reflection;
  • Another point: in the process of executing the business method before and after, some pre-enhancement and post-enhancement can be added (this is AOP);
public class JdkProxyFactory extends AbstractProxyFactory {
    
    

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

    @Override
    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);
                return method.invoke(proxy, arguments);
            }
        };
    }

}

Finally, a dynamic proxy instance is generated and returned. The type of the proxy instance is AbstractProxyInvoker. This Invoker is directly linked to our implementation class ref, which is the bottom-level Invoker;

The second layer: DelegateProviderMetaDataInvoker

After calling PROXY_FACTORY, an AbstractProxyInvoker instance invoker is returned, and the current ServiceConfig class instance and invoker are used as parameters to create a DelegateProviderMetaDataInvoker instance wrapperInvoker;

                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                        // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

Protocol extension points

Call the Protocol#export method to export the service;

DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);Exporter<?> exporter = protocol.export(wrapperInvoker);
@SPI("dubbo")
public interface Protocol {
    
    
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
	//省略部分代码
}

Protocol is an extension point, and the implementation class of the extension point of Protocol is DubboProtocol by default, so the DubboProtocol#export method will be called to export the service. When Dubbo's SPI mechanism calls the implementation class of the DubboProtocol extension point, it will first call the packaging extension point of the Protocol
The wrapper class extension point of the implementation class to process the Protocol:

  • ProtocolFilterWrapper: It will wrap Invoker, and a series of filter interception processing is common;
  • ProtocolListenerWrapper: protocol listener processing;
  • QosProtocolWrapper : This wrapper implementation class is used in relatively few scenarios;

insert image description here

The calling link of Protocol#export is as follows

  1. ProtocolFilterWrapper#export
  2. ProtocolListenerWrapper#export
  3. QosProtocolWrapper#export
  4. DubboProtocol#export

The third layer: CallbackRegistrationInvoker instance and filter chain processing

Calling ProtocolFilterWrapper#export will create a filter chain for processing and intercept processing;

public class ProtocolFilterWrapper implements Protocol {
    
    
    private final Protocol protocol;
	
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    
    
            return protocol.export(invoker);
        }
       //调用buildInvokerChain生成CallbackRegistrationInvoker实例
        return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
    }
    //省略了buildInvokerChain方法;
 }
  • Call buildInvokerChain to generate a CallbackRegistrationInvoker instance; this instance wraps the DelegateProviderMetaDataInvoker instance and filter chain of the second layer; the parameters are as follows:
  • DelegateProviderMetaDataInvoker instance invoker
  • SERVICE_FILTER_KEY : constant service.filter
  • CommonConstants.PROVIDER: constant provider

The process of buildInvokerChain:

  1. Obtain the extension point implementation class of Filter, whose group name is the implementation class of provider;
  2. Traverse the obtained Filter; each Filter will create an Invoker, and each Invoker#invoke method will call the filter#invoke method to process; return an asynchronous result;
  3. Finally create a CallbackRegistrationInvoker, pass in the Invoker instance corresponding to the outermost Filter, and the filters instance;
public class ProtocolFilterWrapper implements Protocol {
    
    
    private final Protocol protocol;
	
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
    
    
            return protocol.export(invoker);
        }
       //调用buildInvokerChain生成CallbackRegistrationInvoker实例
        return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
    }

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    
    
        Invoker<T> last = invoker;
        // 根据url获取filter,根据url中的parameters取key为key的value所对应的filter,但是还会匹配group
        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;
                last = new Invoker<T>() {
    
    
                //省略部分代码
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
    
    
                        Result asyncResult;
                        try {
    
    
                            // 得到一个异步结果
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
    
    
                        //异常处理
                        }
                        return asyncResult;
                    }
                };
            }
        }

        return new CallbackRegistrationInvoker<>(last, filters);
    }

}

Filter extension point

  • In the Filter extension file, the following extension point implementation classes are included. "#" and the value are added by me, representing the Filter whose group name is Provider; the smaller the negative number of the value, the more it will be used first;
  • Service provider and service consumer call process interception. Most of Dubbo’s functions are implemented based on this extension point. Every time a remote method is executed, this interception will be executed, which will affect performance;
  • During development, if we want to add a new Filter, we only need to implement the Filter interface, and the call sequence is after the built-in Filter;
echo=org.apache.dubbo.rpc.filter.EchoFilter #-110000
generic=org.apache.dubbo.rpc.filter.GenericFilter # -20000
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
token=org.apache.dubbo.rpc.filter.TokenFilter # 无
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter #无
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter #-30000
context=org.apache.dubbo.rpc.filter.ContextFilter # order = -10000
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
exception=org.apache.dubbo.rpc.filter.ExceptionFilter #无
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter #无
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter #无

Call sequence:

  1. EchoFilter#invoke
    provides the echo function, that is, the client judges whether the service is available; if the method name is echo, the call ends, and the returned result is the first value of the parameter;
@Activate(group = CommonConstants.PROVIDER, order = -110000)
public class EchoFilter implements Filter {
    
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
    
    
        if (inv.getMethodName().equals($ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
    
    
            return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
        }
        return invoker.invoke(inv);
    }

}
  1. ClassLoaderFilter#invoke
    class loading filter: Set the class loader of the current thread, which is the class loader of the interface class interface of the proxy of Invoker.
@Activate(group = CommonConstants.PROVIDER, order = -30000)
public class ClassLoaderFilter implements Filter {
    
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    
    
        ClassLoader ocl = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());
        try {
    
    
            return invoker.invoke(invocation);
        } finally {
    
    
            Thread.currentThread().setContextClassLoader(ocl);
        }
    }
}
  1. GenericFilter#invoke
    Dubbo's generalized service use; not used for the time being;

  2. The RpcContext context object of the ContextFilter#invoke service sets Invoker, parameter information invocation, and IP port number information;
@Activate(group = PROVIDER, order = -10000)
public class ContextFilter extends ListenableFilter {
    
    
    private static final String TAG_KEY = "dubbo.tag";
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    
    
        Map<String, String> attachments = invocation.getAttachments();
        if (attachments != null) {
    
    
            attachments = new HashMap<>(attachments);
            attachments.remove(PATH_KEY);
            attachments.remove(INTERFACE_KEY);
            attachments.remove(GROUP_KEY);
            attachments.remove(VERSION_KEY);
            attachments.remove(DUBBO_VERSION_KEY);
            attachments.remove(TOKEN_KEY);
            attachments.remove(TIMEOUT_KEY);
            // Remove async property to avoid being passed to the following invoke chain.
            attachments.remove(ASYNC_KEY);
            attachments.remove(TAG_KEY);
            attachments.remove(FORCE_USE_TAG);
        }
        RpcContext context = RpcContext.getContext();

        context.setInvoker(invoker)
                .setInvocation(invocation)
//                .setAttachments(attachments)  // merged from dubbox
                .setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
        String remoteApplication = invocation.getAttachment(REMOTE_APPLICATION_KEY);
        if (StringUtils.isNotEmpty(remoteApplication)) {
    
    
            context.setRemoteApplicationName(remoteApplication);
        } else {
    
    
            context.setRemoteApplicationName(RpcContext.getContext().getAttachment(REMOTE_APPLICATION_KEY));

        }
		
        if (attachments != null) {
    
    
            if (RpcContext.getContext().getAttachments() != null) {
    
    
                RpcContext.getContext().getAttachments().putAll(attachments);
            } else {
    
    
                RpcContext.getContext().setAttachments(attachments);
            }
        }
        if (invocation instanceof RpcInvocation) {
    
    
            ((RpcInvocation) invocation).setInvoker(invoker);
        }
        try {
    
    
            return invoker.invoke(invocation);
        } finally {
    
    
        }
    }
}
  1. TimeoutFilter#invoke
    The timeout processing of the server will only print the log, but will not terminate the operation of the service;
  • Set the start time in the invoke method;
  • In TimeoutListener#onResponse, the business execution time will be obtained according to the current time - start time, and then compared with the timeout configuration, if it is greater than the timeout time set by timeout, a warning log will be printed;
@Activate(group = CommonConstants.PROVIDER)
public class TimeoutFilter extends ListenableFilter {
    
    

    private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);

    private static final String TIMEOUT_FILTER_START_TIME = "timeout_filter_start_time";

    public TimeoutFilter() {
    
    
        super.listener = new TimeoutListener();
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    
    
        invocation.setAttachment(TIMEOUT_FILTER_START_TIME, String.valueOf(System.currentTimeMillis()));
        return invoker.invoke(invocation);
    }

    static class TimeoutListener implements Listener {
    
    

        @Override
        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
    
    
            String startAttach = invocation.getAttachment(TIMEOUT_FILTER_START_TIME);
            if (startAttach != null) {
    
    
                long elapsed = System.currentTimeMillis() - Long.valueOf(startAttach);
                if (invoker.getUrl() != null && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE)) {
    
    
                    if (logger.isWarnEnabled()) {
    
    
                        logger.warn("invoke time out. method: " + invocation.getMethodName() + " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is " + invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");
                    }
                }
            }
        }

    }
}
  1. ExceptionFilter#invoke

Invocation order of server-side Invoker

Through the above, the calling sequence of Invoker can be obtained.

  1. CallbackRegistrationInvoker#invoke
    1.1 EchoFilter#invoke
    1.2 ClassLoaderFilter#invoke
    1.3 GenericFilter#invoke
    1.4 ContextFilter#invoke
    1.5 TimeoutFilter#invoke
    1.6 ExceptionFilter#invoke
  2. DelegateProviderMetaDataInvoker#invoke
  3. JavassitProxyFactory#AbstractProxyInvoker#invoke

start server

When the service is exported,

  • The service information is converted into a URL and placed in the registration center.
    This step is executed by calling the export method of the parent class Protocol parent class AbstractProxyProtocol;
  • Start the Netty server;
    this step is executed by calling the DubboProtocol#export method;
    it will package the invoker as an Exporter instance and put it in the cache; when serving the request, get the exporter from the cache to execute;
	DubboProtocol#export
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        URL url = invoker.getUrl();
        // export service.
        String key = serviceKey(url);
        // 构造一个Exporter
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);
        // 开启NettyServer
        openServer(url);
        return exporter;
    }
    //判断容器中是否存在Netty服务器, 存在就重新导出,不存在即调用createServer创建NettyServer服务器;
    private void openServer(URL url) {
    
    
        String key = url.getAddress(); 
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
    
    
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
    
    
                synchronized (this) {
    
    
                    server = serverMap.get(key);
                    if (server == null) {
    
    
                        serverMap.put(key, createServer(url));
                    }
                }
            } else {
    
    
            }
        }
    }
    //调用 Exchangers.bind(url, requestHandler)创建服务器;
    private ExchangeServer createServer(URL url) {
    
    
        url = ...;
        //省略部分代码;
        ExchangeServer server;
        try {
    
    
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
    
    
        }
        return server;
    }

Exchangers#bind method:

    public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        // codec表示协议编码方式
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        // 通过url得到HeaderExchanger, 利用HeaderExchanger进行bind,将得到一个HeaderExchangeServer
        return getExchanger(url).bind(url, handler);
    }
    public static Exchanger getExchanger(URL url) {
    
    
    	//type的值为header
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
    type的值为header, 
    public static Exchanger getExchanger(String type) {
    
    
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

Finally, through the SPI mechanism, the extension point implementation class extended to header will be obtained;

Exchanger extension point

HeaderExchangerServer server for creating request exchanges;

@SPI(HeaderExchanger.NAME)
public interface Exchanger {
    
    

    @Adaptive({
    
    Constants.EXCHANGER_KEY})
    ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;

    @Adaptive({
    
    Constants.EXCHANGER_KEY})
    ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;

}

There is only one extension point implementation class of Exchanger:

  • HeaderExchanger
header=org.apache.dubbo.remoting.exchange.support.header.HeaderExchanger

HeaderExchanger

Used to create ExchangeServer /ExchangeClient instances;

public class HeaderExchanger implements Exchanger {
    
    

    public static final String NAME = "header";

    @Override
    public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
    }

    @Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

}

When creating HeaderExchangerServer, the Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))) method is called; it also involves the Transporter extension point;

Transporter extension point

Get a Server instance and return;

    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    
    
    //省略部分代码
        // 调用NettyTransporter去绑定,Transporter表示网络传输层
        return getTransporter().bind(url, handler);
    }
  	public static Transporter getTransporter() {
    
    
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }

Through the SPI mechanism, the extension point of Transporter will be obtained to realize the class instance; then the bind method will be called;

The @SPI annotation is used on the Transporter interface to indicate which extension point implementation class is used by default;

@SPI("netty")
public interface Transporter {
    
    

    @Adaptive({
    
    Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    @Adaptive({
    
    Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

Transporter extension point implementation class:
there is only one NettyTransporter in the dubbo-remoting-netty module

netty3=org.apache.dubbo.remoting.transport.netty.NettyTransporter

In the dubbo-remoting-grizzly module, there is only one GrizzlyTransporter

grizzly=org.apache.dubbo.remoting.transport.grizzly.GrizzlyTransporter

In NettyTransporter, in the bind method, NettyServer will be created and returned;

public class NettyTransporter implements Transporter {
    
    

    public static final String NAME = "netty";

    @Override
    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
    
    
        return new NettyServer(url, listener);
    }

    @Override
    public Client connect(URL url, ChannelHandler listener) throws RemotingException {
    
    
        return new NettyClient(url, listener);
    }

}

After starting the server, the final obtained Server type is HeaderExchangerServer, which wraps a NettyServer inside;

HeaderExchangerServer与NettyServer

insert image description here
Send and receive data between NettyClient and NettyServer;
so why add a HeaderExchangerServer and HeaderExchangerClient?

  • When HeaderExchangerClient sends data, it will construct a Requset request object and send it to the server;
  • HeaderExchangerServer will construct a Response response data instance and send it to the client;
    my personal opinion is: a request/response layer protocol is encapsulated above the data exchange layer; analogous to the TCP transport layer protocol, HTTP is the same as the application layer protocol;

EchoFilter#invoke method, this class is an echo function, to determine whether the service is available; if available, it will call AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv) to return;

@Activate(group = CommonConstants.PROVIDER, order = -110000)
public class EchoFilter implements Filter {
    
    

    @Override
    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
    
    
        if (inv.getMethodName().equals($ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
    
    
            return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
        }
        return invoker.invoke(inv);
    }

}

AsyncRpcResult#newDefaultAsyncResult

  • It will create an AppResponse instance, set the return value, call asyncRpcResult.complete(appResponse) to indicate the end of processing, and return;
  • This AppResponse instance represents the response data;
    public static AsyncRpcResult newDefaultAsyncResult(Invocation invocation) {
    
    
        return newDefaultAsyncResult(null, null, invocation);
    }

    public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) {
    
    
        return newDefaultAsyncResult(value, null, invocation);
    }

    public static AsyncRpcResult newDefaultAsyncResult(Throwable t, Invocation invocation) {
    
    
        return newDefaultAsyncResult(null, t, invocation);
    }

    public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
    
    
        AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);
        AppResponse appResponse = new AppResponse();
        if (t != null) {
    
    
            appResponse.setException(t);
        } else {
    
    
            appResponse.setValue(value);
        }
        asyncRpcResult.complete(appResponse);
        return asyncRpcResult;
    }
}

Look at the Request request data;
HeaderExchangerChannel#send method: If it is a Request, send the data directly. If not, a Request instance will also be constructed to send the request data;

    @Override
    public void send(Object message, boolean sent) throws RemotingException {
    
    
        if (closed) {
    
    
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!");
        }
        if (message instanceof Request
                || message instanceof Response
                || message instanceof String) {
    
    
            channel.send(message, sent);
        } else {
    
    
            Request request = new Request();
            request.setVersion(Version.getProtocolVersion());
            request.setTwoWay(false);
            request.setData(message);
            channel.send(request, sent);
        }
    }

In the end, the NettyChannel channel will transmit the data. This is my personal opinion.

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/124526464