Overview of Dubbo layered overall design

Let’s start with the following diagram to briefly introduce Dubbo’s layered design concept:

(From Duboo Development Guide – Framework Design Documentation)

As shown in the figure, RPC implemented by Dubbo consists of 10 layers: Service, Config, Proxy, Registry, Cluster, Monitor, Protocol, Exchange, Transport, and serialize.

Service: user defined interface and implementation class;

Config: parses the configuration defined by Dubbo, such as annotations and XML configuration, various parameters;

Proxy: mainly responsible for generating consumer and provider proxy objects, loading framework functions, such as provider filter chain, extension points;

Registry: responsible for the definition and loading of the implementation classes of the registered service;

Cluster: Only the consumer has this layer, which is responsible for packaging multiple service providers into a ‘big provider’, loading load balancing, routing, and other extension points;

Monitor: Defines monitoring services and loads monitoring implementation providers;

Protocol: Encapsulates the RPC calling interface and manages the life cycle of the calling entity.

Exchange: Encapsulates the request response pattern, synchronous to asynchronous;

Transport: Abstract transport layer model, compatible with Netty, Mina, Grizzly and other communication frameworks;

Serialize: Abstract serialization model, compatible with a variety of serialization frameworks, including fastjson, FST, Hessian2, Kryo, Kryo2, Protobuf, etc., through serialization support cross-language way, support cross-language RPC calls;

The purpose of Dubbo’s layering is to decouple layers from one another. Each layer defines an interface specification and can be customized and loaded with different implementations according to different business requirements, with high scalability.

1.1. RPC call procedure

The following is a brief description of a complete RPC call process based on the above figure:

From the perspective of Dubbo layering, the detailed sequence diagram is as follows. The blue part is the service consumer, and the light green part is the service provider. The sequence diagram starts from a Dubbo method call on the consumer to the end of the local method execution on the server.

From the point of view of Dubbo core domain objects, we refer to the official Dubbo documentation, as shown below. Dubbo core domain object is Invoker, consumer side proxy object is proxy, packaging Invoker call; The server proxy object is an Invoker wrapper that, when a server receives an invocation request, runs to the Invoker through its exporter, which actually executes the user’s business logic.

(Quoted from official Dubbo documentation)

1.2 Registration and discovery process of Dubbo service

The following figure is from the Development Guide – Framework Design – referencing the service sequence. The main flow is as follows: subscribe to the service provider from the registry, then start the TCP service to connect to the remote provider, merge multiple service providers into an Invoker, and use this Invoker to create proxy objects.

The following figure is from the Development Guide – Framework Design – Expose service sequence. The main process is to create the Invoker proxy for the local service, start the TCP service expose service, and then register the service with the registry.

Next we explain the role and mechanism of each layer, starting with the configuration layer, in conjunction with the registration and discovery of Dubbo services.

The sample service interface is defined as follows:

public interface CouponServiceViewFacade {
 
    /** ** Query single coupon */
    CouponViewDTO query(String code);
}
Copy the code

Second, the configuration layer

2.1. What to do

The configuration layer provides the configuration processing utility class, which instantiates the service provider with ServiceConfig. Export and the service consumer object with ReferenceConfig. Get at container startup time.

When the Dubbo application is started using the Spring container, the Dubbo service provider configuration handler starts the Dubbo remote service with ServiceConfig. Export to expose the local service. The Dubbo service consumer configuration handler instantiates a proxy object via ReferenceConfig. Get and connects to the remote service provider through the registry service discovery.

Dubbo configuration can be in the form of annotations and XML, and this article uses annotations.

2.2. How to do

2.2.1 Service consumer Parsing

When filling in the bean properties during Spring container startup, Using the property of include references to Dubbo annotation org. Apache. Dubbo. Config. Spring. Beans. Factory. The annotation. Initialized ReferenceAnnotationBeanPostProcessor. Below is the constructor ReferenceAnnotationBeanPostProcessor, Dubbo service consumers annotation processor to handle the following three comments: DubboReference. Class, Reference. Class, com. Alibaba. Dubbo. Config. The annotation. The Reference. The class of class.

ReferenceAnnotationBeanPostProcessor class definition:

public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
        ApplicationContextAware {
 
    public ReferenceAnnotationBeanPostProcessor(a) {
        super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class); }}Copy the code

At this level, Dubbo service discovers that Dubbo is about to start building a proxy object for the service consumer, a proxy implementation class for the CouponServiceViewFacade interface.

2.2.2 Service Provider Resolution

Spring container startup, loading annotation @ org. Apache. The dubbo. Config. Spring. The context. The annotation. DubboComponentScan specified range of classes, and initialized; Initialization use dubbo implement the extension points org. Apache. Dubbo. Config. Spring. Beans. Factory. The annotation. ServiceClassPostProcessor.

ServiceClassPostProcessor processing annotation class has DubboService. Class, Service, class, com. Alibaba. Dubbo. Config. The annotation. The Service. The class.

The following is ServiceClassPostProcessor class definition:

public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor.EnvironmentAware.ResourceLoaderAware.BeanClassLoaderAware {
 
    private final staticList<Class<? extends Annotation>> serviceAnnotationTypes = asList( DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class ); . }Copy the code

Wait for the Spring container ContextRefreshedEvent event to start the Dubbo application service listening port and expose the local service.

The Dubbo service is registered to this layer, and Dubbo is about to start building the proxy object for the service provider, the reflection proxy class for the CouponServiceViewFacade class.

Third, the agent layer

3.1 what to do

Generate proxy implementation instances for service consumers and reflection proxy instances for service providers.

The proxy implementation instance of The CouponServiceViewFacade, through which the consumer invokes the Query method of the proxy implementation instance to invoke the remote service.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
package org.apache.dubbo.common.bytecode;
 
public class proxy1 implements DC.Destroyable.CouponServiceViewFacade.EchoService {
    public static Method[] methods;
    private InvocationHandler handler;
 
    public proxy1(InvocationHandler var1) {
        this.handler = var1;
    }
 
    public proxy1(a) {}public CouponViewDTO query(String var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[0], var2);
        return(CouponViewDTO)var3; }}Copy the code

Reflection proxy instance of The CouponServiceViewFacade through which the server finally executes the local method Query after receiving the request through its Invoke method.

/** * InvokerWrapper */
public class AbstractProxyInvoker<CouponServiceViewFacade> implements Invoker<CouponServiceViewFacade> {
        / /...
 
    public AbstractProxyInvoker(CouponServiceViewFacade proxy, Class<CouponServiceViewFacade> type, URL url) {
        / /...
        this.proxy = proxy;
        this.type = type;
        this.url = url;
    }
 
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        / /...
        Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
        / /...
    }
 
    protected Object doInvoke(CouponServiceViewFacade proxy, String methodName, Class
       [] parameterTypes, Object[] arguments) throws Throwable{
        / /...
        returnwrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); }}Copy the code

3.2 how to do

The Dubbo proxy factory interface is defined as follows, defining proxy object factory methods for service providers and service consumers. Both service provider proxy objects and service consumer proxy objects are created through factory methods, and factory implementation classes can be customized by SPI extension.

@SPI("javassist")
public interface ProxyFactory {
 
    // Generate a service consumer proxy object
    @Adaptive({PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;
 
    // Generate a service consumer proxy object
    @Adaptive({PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
 
     
    // Generate a service provider proxy object
    @Adaptive({PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
 
}
Copy the code

3.2.1 Service to Consumers

3.2.1.1 Create a service consumer proxy class

By default, Javaassist Proxy factory implementation is adopted, proxy.getProxy (interfaces) creates Proxy factory classes, and newInstance creates concrete Proxy objects.

public class JavassistProxyFactory extends AbstractProxyFactory {
 
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker
       
         invoker, Class
        [] interfaces)
        {
        return (T) Proxy.getProxy(interfaces).newInstance(newInvokerInvocationHandler(invoker)); }... }Copy the code

3.2.1.2 Service consumer Agent

Dubbo generates two proxy classes for each service consumer: the proxy factory class and the interface proxy class.

CouponServiceViewFacade Proxy factory class:

public class Proxy1 extends Proxy implements DC {
    public Proxy1(a) {}public Object newInstance(InvocationHandler var1) {
        return newproxy1(var1); }}Copy the code

The resulting proxy object of the CouponServiceViewFacade is shown below, where the handler implementation class is InvokerInvocationHandler and the this.Handle. invoke method initiates a Dubbo call.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
package org.apache.dubbo.common.bytecode;
 
public class proxy1 implements DC.Destroyable.CouponServiceViewFacade.EchoService {
    public static Method[] methods;
    private InvocationHandler handler;
 
    public proxy1(InvocationHandler var1) {
        this.handler = var1;
    }
 
    public proxy1(a) {}public CouponViewDTO query(String var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[0], var2);
        return(CouponViewDTO)var3; }}Copy the code

3.2.2 Service providers

3.2.2.1 Creating a service provider proxy class

The default Javaassist proxy factory implementation wraps the local service provider with a Wrapper. Proxy is the actual service provider instance, that is, the local implementation class of CouponServiceViewFacade, type is the interface class definition, and URL is injVM protocol URL.

public class JavassistProxyFactory extends AbstractProxyFactory {...@Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // Proxy wrapper class, which wraps the local service provider
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        // Proxy class entry
        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

3.2.2.2 Wrapper classes

Dubbo generates a Wrapper proxy class for each service provider’s local implementation. The abstract Wrapper class is defined as follows:

public abstract class Wrapper {...abstract public Object invokeMethod(Object instance, String mn, Class
       [] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
}
Copy the code

The Wrapper proxy class is dynamically generated using bytecode technology. Examples of proxy Wrapper classes for the local service CouponServiceViewFacade:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
 
package org.apache.dubbo.common.bytecode;
 
import com.xxx.CouponServiceViewFacade;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
 
public class Wrapper25 extends Wrapper implements DC {...public Wrapper25(a) {}public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
        CouponServiceViewFacade var5;
        try {
            var5 = (CouponServiceViewFacade)var1;
        } catch (Throwable var8) {
            throw new IllegalArgumentException(var8);
        }
 
        try {
            if ("query".equals(var2) && var3.length == 1) {
                return var5.query((String)var4[0]); }}catch (Throwable var9) {
            throw new InvocationTargetException(var9);
        }
 
        throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class com.xxx.CouponServiceViewFacade."); }... }Copy the code

In the service initialization process, the initialization is completed after the service consumer proxy object is generated, and the initialization sequence of the service consumer side is as follows: Referenceconfig. get-> subscribe service from registry -> start client -> create DubboInvoker-> build ClusterInvoker -> create service proxy object;

ServiceConfig. Export -> Create AbstractProxyInvoker, associate local service with Injvm protocol -> Start server -> register service to registry.

Next we move on to the registration layer.

4. Registration layer

4.1 what to do

Encapsulate the registration and discovery of service addresses, taking the service URL as the configuration center. After the local service of the service provider is successfully started and the Dubbo port is successfully monitored, the service provider publishes the service to the registry through the registration protocol. The service consumer subscribes to the service through the registration protocol and starts the local application to connect to the remote service.

Registration protocol URL example:

zookeeper://xxx/org.apache.dubbo.registry.RegistryService? application=xxx&…

4.2 how to do

The registry service factory interface is defined as follows, and the registry service implementation is extended via SPI, with ZK as the registry by default.

@SPI("dubbo")
public interface RegistryFactory {
 
    @Adaptive({"protocol"})
    Registry getRegistry(URL url);
 
}
Copy the code

Register the service interface definition;

public interface RegistryService {
 
    
    void register(URL url);
 
    
    void unregister(URL url);
 
    
    void subscribe(URL url, NotifyListener listener);
 
    
    void unsubscribe(URL url, NotifyListener listener);
 
    
    List<URL> lookup(URL url);
 
}
Copy the code

5. Cluster layer

5.1 what to do

After subscribing to the service provider from the registry, the service consumer wraps multiple providers into one provider and encapsulates routing and load balancing policies. And bridge registry, Invoker as the center, extension interface for Cluster, Directory, Router, LoadBalance;

There is no cluster layer on the service provider side.

5.2 how to do

5.2.1 Cluster

The clustering domain is primarily responsible for packaging multiple service providers into a ClusterInvoker, injecting routing processor chains and load balancing policies. The main policies include failover, failfast, failsafe, failback, forking, available, Mergeable, broadcast, and zone-aware.

The cluster interface is defined as follows, and there is only one method: build a ClusterInvoker from multiple service providers in the service catalog.

The function is to shield the logic of the cluster layer from the upper-layer – agent layer; The proxy layer only needs to invoke Invoker.invoke and then calculate which remote service provider to execute using routing and load balancing policies inside ClusterInvoker.

@SPI(Cluster.DEFAULT)
public interface Cluster {
    String DEFAULT = FailoverCluster.NAME;
 
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException; . }Copy the code

ClusterInvoker performs logic to filter routing policies and then select the final remote service provider by load balancing policies. The following is an example proxy:

   public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {...@Override
    public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();
 
        // binding attachments into invocation.
        Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
        if(contextAttachments ! =null&& contextAttachments.size() ! =0) {
            ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
        }
 
        // Cluster invoker is executed using a routing chain to filter service providers
        List<Invoker<T>> invokers = list(invocation);
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        returndoInvoke(invocation, invokers, loadbalance); }... }Copy the code

5.2.2 Directory

The service Directory interface is defined as follows. When the Dubbo method interface is invoked, the method information is wrapped as the Invocation and the remote service is filtered through directory. list.

By org. Apache. Dubbo. Registry. Integration. RegistryDirectory bridge registry, to monitor the registry events such as routing configuration changes, service management.

public interface Directory<T> extends Node {
 
    
    Class<T> getInterface(a);
 
    List<Invoker<T>> list(Invocation invocation) throws RpcException;
 
    List<Invoker<T>> getAllInvokers();
 
    URL getConsumerUrl(a);
 
}
Copy the code

5.2.3 requires the Router

Service providers are scrubbed from all known service providers based on routing rules.

The route processor chain is initialized when the service is subscribed, the route chain is used to filter the service provider when the remote service is invoked, and then the specific service node is selected through load balancing.

Route processor chain utility class that provides route filtering services and listens for updates to service providers.

public class RouterChain<T> {...public List<Invoker<T>> route(URL url, Invocation invocation) {
        List<Invoker<T>> finalInvokers = invokers;
        for (Router router : routers) {
            finalInvokers = router.route(finalInvokers, url, invocation);
        }
        return finalInvokers;
    }
 
    /** * Notify router chain of the initial addresses from registry at the first time. * Notify whenever addresses in registry change. */
    public void setInvokers(List<Invoker<T>> invokers) {
        // The routing chain listens for the update service provider
        this.invokers = (invokers == null ? Collections.emptyList() : invokers);
        routers.forEach(router -> router.notify(this.invokers)); }}Copy the code

When subscribing to a service, inject the routing chain into the RegistryDirectory.

public class RegistryProtocol implements Protocol {...private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {...// The service directory initializes the routing chaindirectory.buildRouterChain(subscribeUrl); directory.subscribe(toSubscribeUrl(subscribeUrl)); .returnregistryInvokerWrapper; }... }Copy the code

5.2.4 LoadBalance

Select an available remote service instance based on different load balancing policies. The interface responsible for balancing is defined as follows:

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
 
    @Adaptive("loadbalance")
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
 
}
Copy the code

Six, monitoring layer

6.1 what to do

MonitorFactory, Monitor, and MonitorService Monitor the number and time of RPC calls, with Statistics as the center.

6.2 how to do

Monitor factory interface definition, extended by SPI;

@SPI("dubbo")
public interface MonitorFactory {
 
    
    @Adaptive("protocol")
    Monitor getMonitor(URL url);
 
}

@Adaptive("protocol")
Monitor getMonitor(URL url);
Copy the code

The monitoring service interface is defined as follows, which defines some default monitoring dimensions and indicators.

public interface MonitorService {
 
    // Monitor dimensions
 
    String APPLICATION = "application";
 
    String INTERFACE = "interface";
 
    String METHOD = "method";
 
    String GROUP = "group";
 
    String VERSION = "version";
 
    String CONSUMER = "consumer";
 
    String PROVIDER = "provider";
 
    String TIMESTAMP = "timestamp";
 
    // Monitoring indicators
 
    String SUCCESS = "success";
 
    String FAILURE = "failure";
 
    String INPUT = INPUT_KEY;
 
    String OUTPUT = OUTPUT_KEY;
 
    String ELAPSED = "elapsed";
 
    String CONCURRENT = "concurrent";
 
    String MAX_INPUT = "max.input";
 
    String MAX_OUTPUT = "max.output";
 
    String MAX_ELAPSED = "max.elapsed";
 
    String MAX_CONCURRENT = "max.concurrent";

    void collect(URL statistics);
 
    List<URL> lookup(URL query);
 
}
Copy the code

6.2.1 MonitorFilter

Collect service call times and call time by filter. Default implementation:

Org. Apache. Dubbo. Monitor. Dubbo. DubboMonitor.

Protocol layer

7.1 what to do

Encapsulating RPC calls with Invocation Result and Protocol, Invoker, and mine.

Next, common concepts in Dubbo RPC procedures are introduced:

1) The Invocation is a request session domain model, with each Invocation having a corresponding Invocation instance that wraps dubbo method information as request parameters;

2) Result is the domain model of request results. Each request has corresponding Result instance, which is responsible for packaging dubbo method response;

3) Invoker is an entity domain, representing an executable entity, including local, remote, and cluster.

4) Manage entity of Invoker, my Exporter service provider;

5) Protocol is a service domain, which manages the life cycle of Invoker and provides exposure and reference entry of services;

The exposure and connection reference of remote services starts at this layer in the service initialization process.

For the CouponServiceViewFacade service, the service provider listens on the Dubbo port to start the TCP service; The service consumer discovers service provider information through the registry and starts the TCP service to connect to the remote provider.

7.2 how to do

The protocol interface is defined as follows, which abstracts the service exposure and reference models of different protocols. For example, InjvmProtocol only associates its Exporter and Invoker with local implementations. When DubboProtocol exposes the service, it needs to monitor the local port to start the service. To reference a service, you need to connect to a remote service.

@SPI("dubbo")
public interface Protocol {
 
    
    int getDefaultPort(a);
 
    
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
 
    
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
 
    
    void destroy(a);
 
    
    default List<ProtocolServer> getServers(a) {
        returnCollections.emptyList(); }}Copy the code

Invoker interface definition

The Invocation is the session object for the RPC call, which wraps the request parameters; Result is the Result object of RPC call, which is responsible for wrapping the Result object of RPC call, including exception class information.

public interface Invoker<T> extends Node {
 
    
    Class<T> getInterface(a);
 
    
    Result invoke(Invocation invocation) throws RpcException;
 
}
Copy the code

7.2.1 Exposure and Reference of services

When the service is exposed, enable the RPC server. When referencing a service, the RPC client is enabled.

public class DubboProtocol extends AbstractProtocol {...@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {...// Start the RPC server
        openServer(url);
        optimizeSerialization(url);
 
        return exporter;
    }
 
    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);
 
        // Create dubbo Invoker and open RPC client
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
 
        returninvoker; }... }Copy the code

7.2.2 The Server Responds to requests

Receive response requests;

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
 
        @Override
        public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
                           。。。
            Invocation inv = (Invocation) message;
            Invoker<?> invoker = getInvoker(channel, inv);

            RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
            // Invoke the local service
            Result result = invoker.invoke(inv);
            returnresult.thenApply(Function.identity()); }... };Copy the code

7.2.3 The Client Sends a Request

Call the remote service;

public class DubboInvoker<T> extends AbstractInvoker<T> {...@Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {...boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            int timeout = calculateTimeout(invocation, methodName);
            if (isOneway) {
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                return AsyncRpcResult.newDefaultAsyncResult(invocation);
            } else {
                ExecutorService executor = getCallbackExecutor(getUrl(), inv);
                CompletableFuture<AppResponse> appResponseFuture =
                        currentClient.request(inv, timeout, executor).thenApply(obj -> (AppResponse) obj);
                FutureContext.getContext().setCompatibleFuture(appResponseFuture);
                AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, inv);
                result.setExecutor(executor);
                returnresult; }}}Copy the code

Viii. Exchange layer

8.1 what to do

Encapsulate the Request and Response mode, and convert the synchronous mode to the asynchronous mode. Request and Response are used as the center, and the expansion interface is Exchanger channel, ExchangeClient, ExchangeServer.

Use the Request Invocation as the complete request object and the Response Invocation as the complete response object; Compared with the Invocation of Request and Response, the Dubbo header is added.

8.2 how to do

The exchange object interface definition, which defines the binding and connection of remote services, is extended using SPI;

@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;
 
}

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


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

Copy the code

Switching layer model class diagram:

8.2.1 Service Provider

After receiving the request, the service provider executes it locally and sends the response result.

public class HeaderExchangeHandler implements ChannelHandlerDelegate {...void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
       // Encapsulate the response
        Response res = newResponse(req.getId(), req.getVersion()); . Object msg = req.getData();try {
            CompletionStage<Object> future = handler.reply(channel, msg);
            future.whenComplete((appResult, t) -> {
                try {
                    if (t == null) {
                        res.setStatus(Response.OK);
                        res.setResult(appResult);
                    } else {
                        res.setStatus(Response.SERVICE_ERROR);
                        res.setErrorMessage(StringUtils.toString(t));
                    }
                    channel.send(res);
                } catch (RemotingException e) {
                    logger.warn("Send result to consumer failed, channel is " + channel + ", msg is "+ e); }}); }catch(Throwable e) { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(e)); channel.send(res); }}... }Copy the code

8.2.2 Service consumers

The service consumer initiates the encapsulation of the request and returns a future after the method is successfully executed.

final class HeaderExchangeChannel implements ExchangeChannel {...// Encapsulate the request entity
    @Override
    public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {...// create request.
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        //RpcInvocation
        req.setData(request);
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor);
        try {
            channel.send(req);
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        returnfuture; }... }Copy the code

Ix. Transport layer

9.1 what to do

Abstract transport layer model compatible with Netty, MINA, Grizzly and other communication frameworks.

9.2 how to do

The Transporter interface is defined as follows, which is similar to the Exchanger interface, except that the Exchanger interface is a facade interface wrapped around the Request and Response Dubbo, whereas the Transporter interface is more basic. Sanorecovery Is used to isolate the Dubbo protocol layer and communication layer.

@SPI("netty")
public interface Transporter {
 
    
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
 
    
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
 
}
Copy the code

Custom transport layer model

By MEANS of SPI, the specific transmission framework is dynamically selected. The default is NetTY.

public class Transporters {...public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {...return getTransporter().bind(url, handler);
    }
 
 
    public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {...return getTransporter().connect(url, handler);
    }
 
    public static Transporter getTransporter(a) {
        returnExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension(); }}Copy the code

The channel adaptation of the Netty framework is as follows: The decorative mode is adopted, and the Netty framework channel is used as the Dubbo custom channel to implement.

final class NettyChannel extends AbstractChannel {
 
    private NettyChannel(Channel channel, URL url, ChannelHandler handler) {
        super(url, handler);
        if (channel == null) {
            throw new IllegalArgumentException("netty channel == null;");
        }
        this.channel = channel; }}Copy the code

X. Serialization

10.1 what to do

Abstract serialization model, compatible with a variety of serialization frameworks, including: Fastjson, FST, Hessian2, Kryo, Kryo2, Protobuf, etc., through the serialization support cross-language way, support cross-language RPC call.

10.2 how to do

Defines Serialization extension points, hessian2 by default, with cross-language support. The Serialization interface is actually a factory interface, extended through SPI; The actual serialization and deserialization work is done by ObjectOutput, ObjectInput, and let Hessian2 do the actual work through decorator mode.

@SPI("hessian2")
public interface Serialization {
 
    
    byte getContentTypeId(a);
 
    
    String getContentType(a);
 
 
    @Adaptive
    ObjectOutput serialize(URL url, OutputStream output) throws IOException;
 
    
    @Adaptive
    ObjectInput deserialize(URL url, InputStream input) throws IOException;
 
}
Copy the code

10.2.1 Communication protocol design

The following figure is from the development guide – Implementation Details – Remote Communication details, describing the Dubbo protocol header design;

  • 0-15bit Indicates the magic number of the Dubbo protocol. The value is 0xdabb.

  • 16bit Request response flag, request-1; The Response – 0;

  • 17bit Request pattern flag, only request message, 1 indicates that the server needs to return a response;

  • 1 indicates that the message is an event message, such as a heartbeat message.

  • 19-23 bit is serialized type tag, hessian serialization id is 2, fastjson is 6, see org.apache.dubbo.com mon. Serialize. Constants;

  • 24-31bit indicates the status. Only response messages are useful.

  • 32 to 64 are RPC request ids.

  • 96-128bit indicates the length of session data.

  • 128 is the message body byte sequence;

Xi. Conclusion

Dubbo divided the whole process of RPC into the core agent layer, registration layer, cluster layer, protocol layer, transmission layer, etc. The responsibility boundary between layers is clear. The core layer is defined by interfaces, independent of implementation, which are connected together to form the skeleton of Dubbo. This skeleton can also be thought of as the kernel of Dubbo, which is highly extensible using the SPI mechanism to load plug-ins (extension points).

Vivo Internet Server team -Wang Genfu