NIO server of dubbo - Abstract API

Posted by Steffen on Sat, 05 Feb 2022 10:03:12 +0100

Write in front

In the java community, some excellent NIO frameworks are netty (netty3.x,netty4.x), Mina, grizzly and dubbo. They have implemented their own NIO server based on dubbo: \ \ protocol and thrift: \ \ protocol. Of course, the underlying layer will directly use the existing NIO framework (after all, the cost of repeatedly building wheels is relatively high). So which NIO framework to choose? dubbo's approach is to let users choose by themselves. The specific approach is to provide API layer first, and then provide specific implementation for specific NIO framework, as follows:

API Layer:
    dubbo-remoting-api
 realization:
    dubbo-remoting-netty
    dubbo-remoting-netty4
    dubbo-remoting-mina
    dubbo-remoting-grizzly
    dubbo-remoting-p2p

Then cooperate Mechanism Dubbo SPI Users can choose to use it freely. In this article, let's take a look at the Dubbo remoting API section.

When we use netty to write network communication programs, we generally need the following four classes:

Server:Class used to start the server.
ServerHandler:The class used by the server to receive and process client messages.
Client: Class used to start the client.
ClientHandler: The class used by the client to process messages from the server.

dubbo is similar to this. The corresponding classes are as follows:

Server->com.alibaba.dubbo.remoting.Server
ServerHandler->com.alibaba.dubbo.remoting.ChannelHandler
Client->com.alibaba.dubbo.remoting.Client
ClientHandler->com.alibaba.dubbo.remoting.ChannelHandler

But in fact, because Dubbo uses custom protocols, such as Dubbo: / /, such as protocol coding interface com alibaba. dubbo. remoting. Codec2, message distribution interface, com alibaba. dubbo. remoting. Dispatcher, the class diagram involved in this paper is as follows:

1: Endpoint

The English translation of Endpoint is an Endpoint, which can be understood as a portal for communication with the outside world. It is used to further encapsulate the communication connection. The source code is as follows:

public interface Endpoint {
    // Get URL address
    URL getUrl();
    // Get ChannelHandler
    ChannelHandler getChannelHandler();
    // Get local network address
    InetSocketAddress getLocalAddress();
    // send message
    void send(Object message) throws RemotingException;
    // send message
    void send(Object message, boolean sent) throws RemotingException;
    // Close channel
    void close();
    // Close the channel in an elegant way
    void close(int timeout);
    void startClose();
    // Close
    boolean isClosed();
}

1.1: Channel

The interface signature is the public interface Channel extensions Endpoint {}, which inherits the Endpoint interface, so the Channel is also an Endpoint. The source code is as follows:

public interface Channel extends Endpoint {
    // Get channel remote address
    InetSocketAddress getRemoteAddress();
    // Connected
    boolean isConnected();
    // Is there a property
    boolean hasAttribute(String key);
    // get attribute
    Object getAttribute(String key);
    // set a property
    void setAttribute(String key, Object value);
    // Delete attribute
    void removeAttribute(String key);
}

The interface is the carrier of communication. Different NIO frameworks have different implementations, such as com alibaba. dubbo. remoting. transport. netty4. NettyChannel

1.2: Client

The Client interface inherits the Endpoint interface, so the Client is an Endpoint. The source code is as follows:

public interface Client extends Endpoint, Channel, Resetable {
    // Reconnect the server
    void reconnect() throws RemotingException;

    @Deprecated
    void reset(com.alibaba.dubbo.common.Parameters parameters);
}

1.3: Server

The Server interface inherits the Endpoint interface, so the Server is an Endpoint. The source code is as follows:
The source code is as follows:

public interface Server extends Endpoint, Resetable {
    // Whether to bind the local port, that is, the service has been started and can be connected and received by the client
    boolean isBound();
    // Get client connections
    Collection<Channel> getChannels();
    // Gets the client connection at the specified address
    Channel getChannel(InetSocketAddress remoteAddress);
    @Deprecated
    void reset(com.alibaba.dubbo.common.Parameters parameters);
}

1.3.1: Resetable

The resettable interface is used by the server to reset the internal related attributes according to the incoming URL parameters. The source code is as follows:

public interface Resetable {
    // Reset the internal related properties according to the passed in URL parameters
    void reset(URL url);
}

1.4: ChannelHandler

It is responsible for the logical processing of the channel, such as obtaining messages from the channel and sending messages. It can be understood as the wrapper of the channel. The source code is as follows:

@SPI
public interface ChannelHandler {
    void connected(Channel channel) throws RemotingException;
    void disconnected(Channel channel) throws RemotingException;
    void sent(Channel channel, Object message) throws RemotingException;
    void received(Channel channel, Object message) throws RemotingException;
    void caught(Channel channel, Throwable exception) throws RemotingException;
}

For example, netty4 provides a specific implementation of com alibaba. dubbo. remoting. transport. netty4. Nettyserverhandler uses the API of netty4 internally to perform specific operations.

2: Transporter

Network transmission interface, the source code is as follows:

@SPI("netty")
public interface Transporter {
    // Bind the Server and use the adaptive method to load the Transport implementation class of the corresponding Server
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;
    // Connect to the server and use the adaptive method to load the Transport implementation class of the corresponding Client
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

Use this class to obtain the Server and the corresponding classes Server and Client of the Server.

3: Transporters

This is the of Transport Facade class , because different NIO frameworks have different logic of acquiring Server and Client, and also have certain complexity. The specific implementation of different NIO frameworks is different subsystems. Therefore, facade design mode is used here to simplify the use of Client.

The source code is as follows:

// The Transporter facade simplifies the complexity of clients using different transporters
public class Transporters {

    static {
        Version.checkDuplicate(Transporters.class);
        Version.checkDuplicate(RemotingException.class);
    }

    private Transporters() {
    }
    
    // Get the facade method of the Server, get different transporters according to different url parameters, and finally get the Server
    public static Server bind(String url, ChannelHandler... handler) throws RemotingException {
        return bind(URL.valueOf(url), handler);
    }

    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handlers == null || handlers.length == 0) {
            throw new IllegalArgumentException("handlers == null");
        }
        // Create a ChannelHandler that uses Channel for communication. If there are multiple channels, a ChannelHandlerDispatcher will be created
        // Each ChannelHandler will be called internally
        ChannelHandler handler;
        if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        return getTransporter().bind(url, handler);
    }

    // The facade method of obtaining the Client will obtain different transporters according to different url parameters, and finally obtain the Client
    public static Client connect(String url, ChannelHandler... handler) throws RemotingException {
        return connect(URL.valueOf(url), handler);
    }

    public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        ChannelHandler handler;
        if (handlers == null || handlers.length == 0) {
            handler = new ChannelHandlerAdapter();
        } else if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }
        return getTransporter().connect(url, handler);
    }

    public static Transporter getTransporter() {
        // 2022-02-04 21:22:35
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }
}

It passed at 21:22:35 on February 4, 2022 Adaptive mechanism of dubbo To dynamically obtain the Transporter corresponding to the underlying NIO framework.

4: Codec2

The top-level interface of message encoding and decoding, and the source code is as follows:

@SPI
public interface Codec2 {
    // public static final String CODEC_KEY = "codec";, Dynamically calling Codec2$Adaptive based on Adaptive mechanism
    // Class to get the specific subclass and call the corresponding method
    @Adaptive({Constants.CODEC_KEY})
    void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;

    // public static final String CODEC_KEY = "codec";, Dynamically calling Codec2$Adaptive based on Adaptive mechanism
    // Class to get the specific subclass and call the corresponding method
    @Adaptive({Constants.CODEC_KEY})
    Object decode(Channel channel, ChannelBuffer buffer) throws IOException;

    // Handle unpacking and gluing strategies enumeration
    enum DecodeResult {
        NEED_MORE_INPUT, SKIP_SOME_INPUT
    }
}

4.1: Codec

The old version of Codec2, the source code is as follows;

@Deprecated
@SPI
public interface Codec {
    Object NEED_MORE_INPUT = new Object();

    @Adaptive({Constants.CODEC_KEY})
    void encode(Channel channel, OutputStream output, Object message) throws IOException;

    @Adaptive({Constants.CODEC_KEY})
    Object decode(Channel channel, InputStream input) throws IOException;
}

Used class com alibaba. dubbo. remoting. transport. codec. Codecadapter.

4.2: Decodeable

Codeable interface:

public interface Decodeable {
    void decode() throws Exception;
}

5: Dispatcher

Message distribution interface:

@SPI(AllDispatcher.NAME)
public interface Dispatcher {
    // The Adaptive mechanism is used to distribute messages to the thread pool
    @Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
    // The last two parameters are reserved for compatibility with the old version configuration
    ChannelHandler dispatch(ChannelHandler handler, URL url);
}

6: RemotingException

The source code is as follows:

public class RemotingException extends Exception {

    private static final long serialVersionUID = -3160452149606778709L;
    // Local address
    private InetSocketAddress localAddress;
    // Remote address
    private InetSocketAddress remoteAddress;

    public RemotingException(Channel channel, String msg) {
        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
                msg);
    }

    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message) {
        super(message);

        this.localAddress = localAddress;
        this.remoteAddress = remoteAddress;
    }

    public RemotingException(Channel channel, Throwable cause) {
        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
                cause);
    }

    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause) {
        super(cause);

        this.localAddress = localAddress;
        this.remoteAddress = remoteAddress;
    }

    public RemotingException(Channel channel, String message, Throwable cause) {
        this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
                message, cause);
    }

    public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message,
                             Throwable cause) {
        super(message, cause);

        this.localAddress = localAddress;
        this.remoteAddress = remoteAddress;
    }

    public InetSocketAddress getLocalAddress() {
        return localAddress;
    }

    public InetSocketAddress getRemoteAddress() {
        return remoteAddress;
    }
}

RemotingException has two subclass exceptions, ExecutionException and TimeoutException, as follows:

public class ExecutionException extends RemotingException {
    private static final long serialVersionUID = -2531085236111056860L;
    private final Object request;
    // Omit various constructors
}
public class TimeoutException extends RemotingException {
    // Represents the constant of the client
    public static final int CLIENT_SIDE = 0;
    // Represents the constant of the server
    public static final int SERVER_SIDE = 1;
    private static final long serialVersionUID = 3122966731958222692L;
    // stage
    private final int phase;
    
    // Omit various constructors
}

Topics: Java Dubbo