dubbo之NIO服务器(二)之Transport

写在前面

dubbo之NIO服务器-抽象API 一文中我们分析了dubbo提供的NIO服务器相关的API抽象,本文一起来看下transport网络传输层的相关代码,实现的功能是抽象mina和netty为统一接口,然后用户基于SPI机制就可以自由选择使用哪种NIO框架了。

1:AbstractPeer

类签名为public abstract class AbstractPeer implements Endpoint, ChannelHandler{},其中Endpoint接口参考1.1:Endpoint。AbstractPeer主要源码如下:

// com.alibaba.dubbo.remoting.transport.AbstractPeer
public abstract class AbstractPeer implements Endpoint, ChannelHandler {
    // 通道处理器
    private final ChannelHandler handler;
    // 服务提供者URL地址
    private volatile URL url;
    // 正在关闭 startClose()
    private volatile boolean closing;
    // 已经关闭 close()
    private volatile boolean closed;

    public AbstractPeer(URL url, ChannelHandler handler) {
        if (url == null) {
            throw new IllegalArgumentException("url == null");
        }
        if (handler == null) {
            throw new IllegalArgumentException("handler == null");
        }
        this.url = url;
        this.handler = handler;
    }
    
    // 发送消息
    @Override
    public void sent(Channel ch, Object msg) throws RemotingException {
        if (closed) {
            return;
        }
        // 2022年3月17日15:38:38
        handler.sent(ch, msg);
    }
}

2022年3月17日15:38:38处是直接使用ChannelHandler来发送消息,是一种装饰设计模式 的使用。

1.1:Endpoint

// com.alibaba.dubbo.remoting.Endpoint
public interface Endpoint {
    // 获取服务提供者的URL地址
    URL getUrl();
    // 获取ChannelHandler
    ChannelHandler getChannelHandler();
    // 获取本地地址
    InetSocketAddress getLocalAddress();
    // 发送消息
    void send(Object message) throws RemotingException;
    // 发送消息
    void send(Object message, boolean sent) throws RemotingException;
    // 关闭连接通道
    void close();
    // 带有超时时长的关闭,优雅关闭方式
    void close(int timeout);
    void startClose();
    // 判断通道是否已经关闭
    boolean isClosed();
}

其有一个抽象实现类AbstractEndpoint,具体参考1.2:AbstractEndpoint

1.2:AbstractEndpoint

源码如下:

// com.alibaba.dubbo.remoting.transport.AbstractEndpoint
public abstract class AbstractEndpoint extends AbstractPeer implements Resetable {

    private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class);
    // 编解码器
    private Codec2 codec;

    private int timeout;
    // 连接超时时间
    private int connectTimeout;
    // 构造函数
    public AbstractEndpoint(URL url, ChannelHandler handler) {
        super(url, handler);
        this.codec = getChannelCodec(url);
        this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
        this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
    }
    // 获取编解码器,基于URL参数配合SPI机制获取对应的Codec实现类
    protected static Codec2 getChannelCodec(URL url) {
        // 通过key codec从URL上获取参数,默认是telnet
        String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
        if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
            return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
        } else {
            return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
                    .getExtension(codecName));
        }
    }
    // 重置相关属性
    @Override
    public void reset(URL url) {
        if (isClosed()) {
            throw new IllegalStateException("Failed to reset parameters "
                    + url + ", cause: Channel closed. channel: " + getLocalAddress());
        }
        try {
            if (url.hasParameter(Constants.TIMEOUT_KEY)) {
                int t = url.getParameter(Constants.TIMEOUT_KEY, 0);
                if (t > 0) {
                    this.timeout = t;
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        try {
            if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) {
                int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0);
                if (t > 0) {
                    this.connectTimeout = t;
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        try {
            if (url.hasParameter(Constants.CODEC_KEY)) {
                this.codec = getChannelCodec(url);
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
    }

    @Deprecated
    public void reset(com.alibaba.dubbo.common.Parameters parameters) {
        reset(getUrl().addParameters(parameters.getParameters()));
    }
}

2:Client

接口源码如下:

// com.alibaba.dubbo.remoting.Client
public interface Client extends Endpoint, Channel, Resetable {
    // 重新连接
    void reconnect() throws RemotingException;

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

}

其抽象实现类为AbstractClient,具体参考2.1:AbstractClient

2.1:AbstractClient

主要抽象方法如下:

class FakeCls {
    protected abstract void doOpen() throws Throwable;

    protected abstract void doClose() throws Throwable;

    protected abstract void doConnect() throws Throwable;

    protected abstract void doDisConnect() throws Throwable;

    protected abstract Channel getChannel();
}

构造函数如下:

// com.alibaba.dubbo.remoting.transport.AbstractClient
class FakeCls {
    // 定时重连的线程池
    private static final ScheduledThreadPoolExecutor reconnectExecutorService = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("DubboClientReconnectTimer", true));

    public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        // 从URL获取重连配置
        send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
        shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);
        // The default reconnection interval is 2s, 1800 means warning interval is 1 hour.
        reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);
        // 初始化客户端(如netty客户端),失败则调用close方法关闭连接
        try {
            doOpen();
        } catch (Throwable t) {
            close();
            throw new RemotingException(url.toInetSocketAddress(), null,
                    "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
                            + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
        }
        // 连接到服务提供者端,如果是失败则调用close方法关闭,比如会输出如下日志:
        // [2022-03-1716:35:03][INFO ][jpm-AbstractClient-connect(282)]- [DUBBO] Successed connect to server /192.168.64.1:20828 from NettyClient 192.168.64.1 using dubbo version 2.6.6, channel is NettyChannel [channel=[id: 0x2b3832d9, L:/192.168.64.1:17885 - R:/192.168.64.1:20828]], dubbo version: 2.6.6, current host: 192.168.64.1
        try {
            // 2022年3月17日17:09:06
            connect();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
            }
        } catch (RemotingException t) {
            // 如果开始检查的话,则关闭,并抛出异常,否则仅仅打印警告日志
            if (url.getParameter(Constants.CHECK_KEY, true)) {
                close();
                throw t;
            } else {
                logger.warn("Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
                        + " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t);
            }
        } catch (Throwable t) {
            close();
            throw new RemotingException(url.toInetSocketAddress(), null,
                    "Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
                            + " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
        }
        // 获得线程池
        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
                .getDefaultExtension().get(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
        ExtensionLoader.getExtensionLoader(DataStore.class)
                .getDefaultExtension().remove(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
    }
}

2022年3月17日17:09:06处是连接到服务提供者,具体参考2.2:connect

2.1.1:发送消息

源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractClient.send
    public void send(Object message, boolean sent) throws RemotingException {
        // 未连接时,连接
        if (send_reconnect && !isConnected()) {
            connect();
        }
        // 获取通道
        Channel channel = getChannel();
        // 通道关闭的情况
        if (channel == null || !channel.isConnected()) {
            throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
        }
        // 发送消息,当使用的是netty时,这里是NettyChannel
        channel.send(message, sent);
    }
}
2.1.2:包装通道处理器
class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractClient.wrapChannelHandler
    protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) {
        // 2022年3月17日19:53:30
        url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME);
        // 追加String THREADPOOL_KEY = "threadpool"; 即线程池键,默认DEFAULT_CLIENT_THREADPOOL = "cached";
        // dubbo://192.168.64.1:20828/...?...&threadpool=cached&...
        url = url.addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
        return ChannelHandlers.wrap(handler, url);
    }
}

2022年3月17日19:53:30处是追加线程名称到URL上,如dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&threadname=DubboClientHandler-192.168.64.1:20828&...,源码如下:

class FakeCls {
    // com.alibaba.dubbo.common.utils.ExecutorUtil.setThreadName
    public static URL setThreadName(URL url, String defaultName) {
        // 先通过THREAD_NAME_KEY = "threadname";作为key获取,否则取默认值CLIENT_THREAD_POOL_NAME = "DubboClientHandler"
        String name = url.getParameter(Constants.THREAD_NAME_KEY, defaultName);
        // 拼上地址
        name = name + "-" + url.getAddress();
        // 追加key THREAD_NAME_KEY = "threadname"到URL上
        url = url.addParameter(Constants.THREAD_NAME_KEY, name);
        return url;
    }
}

具体的NIO框架都提供了具体的实现子类,具体参考2.1.3:具体客户端子类

2.1.3:具体客户端子类

类图如下:

在这里插入图片描述

红框中就是具体的实现子类,注意其中的NettyClient有2个,是分别针对netty3,netty4的实现,如下:

com.alibaba.dubbo.remoting.transport.netty.NettyClient
com.alibaba.dubbo.remoting.transport.netty4.NettyClient

2.2:connect

源码如下:

class FakeCls {               
    // com.alibaba.dubbo.remoting.transport.AbstractClient.connect
     protected void connect() throws RemotingException {
        // private final Lock connectLock = new ReentrantLock(); 并发控制
        connectLock.lock();
        try {
            // 已经连接到服务提供者
            if (isConnected()) {
                return;
            }
            // 2022年3月17日17:18:54
            initConnectStatusCheckCommand();
            // 执行连接
            doConnect();
            // 没连接成功,则抛出RemotingException
            if (!isConnected()) {
                throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
                        + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
                        + ", cause: Connect wait timeout: " + getConnectTimeout() + "ms.");
            // 打印成功连接日志
            } else {
                if (logger.isInfoEnabled()) {
                    logger.info("Successed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
                            + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
                            + ", channel is " + this.getChannel());
                }
            }
            // 设置重连次数为0
            reconnect_count.set(0);
            // 设置没有打印过重连日志
            reconnect_error_log_flag.set(false);
        } catch (RemotingException e) {
            throw e;
        } catch (Throwable e) {
            throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
                    + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
                    + ", cause: " + e.getMessage(), e);
        } finally {
            // 释放锁
            connectLock.unlock();
        }
    }
}

2022年3月17日17:18:54初始化重连线程,具体参考2.3:initConnectStatusCheckCommand

2.3:initConnectStatusCheckCommand

源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractClient.initConnectStatusCheckCommand
    private synchronized void initConnectStatusCheckCommand() {
        // 2022年3月17日17:26:46
        int reconnect = getReconnectParam(getUrl());
        if (reconnect > 0 && (reconnectExecutorFuture == null || reconnectExecutorFuture.isCancelled())) {
            Runnable connectStatusCheckCommand = new Runnable() {
                @Override
                public void run() {
                    try {
                        // 没有连接,则调用方法connect重连
                        if (!isConnected()) {
                            connect();
                        } else {
                            // 记录上次尝试重连的时间
                            lastConnectedTime = System.currentTimeMillis();
                        }
                    } catch (Throwable t) {
                        String errorMsg = "client reconnect to " + getUrl().getAddress() + " find error . url: " + getUrl();
                        // 超过了一定时间还没有通过重连连接上,则打印错误日志,默认是DEFAULT_SHUTDOWN_TIMEOUT = 1000 * 60 * 15;即15分钟
                        if (System.currentTimeMillis() - lastConnectedTime > shutdown_timeout) {
                            // 还没有打印过重连超时错误日志
                            if (!reconnect_error_log_flag.get()) {
                                // 设置已经打印过超时重连错误日志
                                reconnect_error_log_flag.set(true);
                                // 打印超时重连错误日志
                                logger.error(errorMsg, t);
                                return;
                            }
                        }
                        // 达到了下一次打印超时重连错误日志此处周期则打印错误日志,reconnect_warning_period是定义多少个重连周期,如每5秒重连一次
                        // ,当该值为10时,代表重连十次才会打印错误日志
                        if (reconnect_count.getAndIncrement() % reconnect_warning_period == 0) {
                            logger.warn(errorMsg, t);
                        }
                    }
                }
            };
            // 按照reconnect的时间间隔开始定时执行重连操作
            reconnectExecutorFuture = reconnectExecutorService.scheduleWithFixedDelay(connectStatusCheckCommand, reconnect, reconnect, TimeUnit.MILLISECONDS);
        }
    }
}

2022年3月17日17:26:46处是获取重连频率配置,具体参考2.3.1:getReconnectParam

2.3.1:getReconnectParam

源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractClient.getReconnectParam
    private static int getReconnectParam(URL url) {
        int reconnect;
        // 从URL获取RECONNECT_KEY = "reconnect";配置的值
        String param = url.getParameter(Constants.RECONNECT_KEY);
        // 如果是没有配置,或者是配置的值是true,则使用默认的重连时间间隔int DEFAULT_RECONNECT_PERIOD = 2000;即2000毫秒
        if (param == null || param.length() == 0 || "true".equalsIgnoreCase(param)) {
            reconnect = Constants.DEFAULT_RECONNECT_PERIOD;
        // 如果是配置的值false,则代表不重连,0->false
        } else if ("false".equalsIgnoreCase(param)) {
            reconnect = 0;
        } else {
            // 转为整数,只允许是正整数
            try {
                reconnect = Integer.parseInt(param);
            } catch (Exception e) {
                throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:" + param);
            }
            // 判断配置为负数的情况,如配置<dubbo:reference ... reconnect="-30"/>
            if (reconnect < 0) {
                throw new IllegalArgumentException("reconnect param must be nonnegative integer or false/true. input is:" + param);
            }
        }
        return reconnect;
    }
}

3:Server

定义服务端的相关API,顶层接口是com.alibaba.dubbo.remoting.Server,源码如下:

public interface Server extends Endpoint, Resetable {
    boolean isBound();
    // 获取连接的通道们,因为可能被多个客户端连接,所以这里是个集合
    Collection<Channel> getChannels();
    // 获取对应远程客户端的通道
    Channel getChannel(InetSocketAddress remoteAddress);
    // 重置相关参数,该方法已废弃
    @Deprecated
    void reset(com.alibaba.dubbo.common.Parameters parameters);
}

其抽象实现类是com.alibaba.dubbo.remoting.transport.AbstractServer,具体参考3.1:AbstractServer

3.1:AbstractServer

供子类实现的抽象方法:

protected abstract void doOpen() throws Throwable;
protected abstract void doClose() throws Throwable;

构造方法如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractServer.AbstractServer
    // 线程池名称
    protected static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
    private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
    // 线程池
    ExecutorService executor;
    // 本地服务地址,即暴露给消费者连接的地址
    private InetSocketAddress localAddress;
    private InetSocketAddress bindAddress;
    // 最大可接受连接数量
    private int accepts;
    // 秒
    private int idleTimeout = 600;  

    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        // dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> /192.168.64.1:20828
        localAddress = getUrl().toInetSocketAddress();
        // 服务提供者绑定的IP地址
        // dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> 192.168.64.1
        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        // 服务提供者绑定的端口号 
        // dubbo://192.168.64.1:20828/dongshi.daddy.service.cluster.ClusterService?...&weight=30 结果-> 20828
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        // 是否绑定到任意主机,即0.0.0.0,这样所有可以通过任何可访问到本机的IP来访问
        if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
            // String ANYHOST = "0.0.0.0";
            bindIp = NetUtils.ANYHOST;
        }
        // 当为任意主机时:/0.0.0.0:20828
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {
            // 打开服务器
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        //fixme replace this with better method
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

}
3.1.1:被客户端连接的方法

源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractServer.connected
    // 如果是Netty,这里的实现类就是NettyChannel
    public void connected(Channel ch) throws RemotingException {
        // 如果是服务端正在关闭或者是已经关闭,则打印警告日志,并关闭客户端连接
        if (this.isClosing() || this.isClosed()) {
            logger.warn("Close new channel " + ch + ", cause: server is closing or has been closed. For example, receive a new connect request while in shutdown process.");
            ch.close();
            return;
        }
        // 获取当前建立的连接数,如果是已经超过了最大允许连接数,则打印拒绝连接的error日志(为什么要用error,用warn应该更合适吧!),并关闭连接
        Collection<Channel> channels = getChannels();
        if (accepts > 0 && channels.size() > accepts) {
            logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts);
            ch.close();
            return;
        }
        // 调用父类公共逻辑完成连接
        super.connected(ch);
    }
}
3.1.2:发送消息

源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.AbstractServer.send
    public void send(Object message, boolean sent) throws RemotingException {
        // 获取所有的客户端通道
        Collection<Channel> channels = getChannels();
        // 群发消息
        for (Channel channel : channels) {
            if (channel.isConnected()) {
                channel.send(message, sent);
            }
        }
    }
}

具体的NIO框架都提供了具体的实现子类,具体参考3.1.4:Server子类

3.1.3:Server子类

类图如下:

在这里插入图片描述

注意到其中有2个NettyServer,这分别是netty3,netty4对应的实现类,如下:

com.alibaba.dubbo.remoting.transport.netty.NettyServer
com.alibaba.dubbo.remoting.transport.netty4.NettyServer

4:Channel

通道的抽象,对应的接口是com.alibaba.dubbo.remoting.Channel,源码如下:

// com.alibaba.dubbo.remoting.Channel
public interface Channel extends Endpoint {
    // 获取远端地址
    InetSocketAddress getRemoteAddress();
    // 判断是否已经连接
    boolean isConnected();
    // 判断是否拥有某属性
    boolean hasAttribute(String key);
    // 获取属性
    Object getAttribute(String key);
    // 设置属性
    void setAttribute(String key, Object value);
    // 删除某属性
    void removeAttribute(String key);
}

其对应的抽象类是com.alibaba.dubbo.remoting.transport.AbstractChannel,具体参考4.1:AbstractChannel

4.1:AbstractChannel

源码如下:

// com.alibaba.dubbo.remoting.transport.AbstractChannel
public abstract class AbstractChannel extends AbstractPeer implements Channel {

    public AbstractChannel(URL url, ChannelHandler handler) {
        super(url, handler);
    }
    // 发送消息,仅提供通道状态的判断,具体的发送交由子类实现
    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        if (isClosed()) {
            throw new RemotingException(this, "Failed to send message "
                    + (message == null ? "" : message.getClass().getName()) + ":" + message
                    + ", cause: Channel closed. channel: " + getLocalAddress() + " -> " + getRemoteAddress());
        }
    }
}

子类如下图:

在这里插入图片描述

5:ChannelHandler

通道状态和消息的监听器类,接口源码如下:

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

因为ChanelHandler是接口,所以非抽象子类都需要重写所有方法,但并非需要重写所有方法,因此定义了适配器类ChannelHandlerAdapter,只是给出了空实现,源码如下:

public class ChannelHandlerAdapter implements ChannelHandler {

    @Override
    public void connected(Channel channel) throws RemotingException {
    }

    @Override
    public void disconnected(Channel channel) throws RemotingException {
    }

    @Override
    public void sent(Channel channel, Object message) throws RemotingException {
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
    }

    @Override
    public void caught(Channel channel, Throwable exception) throws RemotingException {
    }

}

我们重点看下一个子类ChannelHandlerDispatcher,具体参考5.1:ChannelHandlerDispatcher

5.1:ChannelHandlerDispatcher

维护一组ChannelHandler,作为这一组ChannelHandler的调度器,以send方法为例,源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher.sent
    @Override
    public void sent(Channel channel, Object message) {
        for (ChannelHandler listener : channelHandlers) {
            try {
                listener.sent(channel, message);
            } catch (Throwable t) {
                logger.error(t.getMessage(), t);
            }
        }
    }
}

5.2:ChannelHandlerDelegate

ChannelHandler的装饰类,典型的装饰设计模式 落地应用,是比较重要的API,源码如下:

// com.alibaba.dubbo.remoting.transport.ChannelHandlerDelegate
public interface ChannelHandlerDelegate extends ChannelHandler {
    ChannelHandler getHandler();
}

可以看到仅仅定义了一个getHandler方法用来获取被装饰的ChannelHandler。

5.2.1:AbstractChannelHandlerDelegate

ChannelHandlerDelegate的抽象类,比较简答,只是直接调用被装饰类的方法,以sent方法为例,源码如下:

public abstract class AbstractChannelHandlerDelegate implements ChannelHandlerDelegate {

    protected ChannelHandler handler;

    protected AbstractChannelHandlerDelegate(ChannelHandler handler) {
        Assert.notNull(handler, "handler == null");
        this.handler = handler;
    }

    @Override
    public void sent(Channel channel, Object message) throws RemotingException {
        handler.sent(channel, message);
    }
}
5.2.2:DecodeHandler

解码处理器,处理器接收到的消息,我们从复写的接收消息方法received开始看,源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.DecodeHandler.received
    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof Decodeable) {
            decode(message);
        }
    
        if (message instanceof Request) {
            decode(((Request) message).getData());
        }
    
        if (message instanceof Response) {
            decode(((Response) message).getResult());
        }
    
        handler.received(channel, message);
    }
}

以上的decode方法源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.DecodeHandler.decode
    private void decode(Object message) {
        // 2022年3月21日18:15:24
        if (message != null && message instanceof Decodeable) {
            try {
                ((Decodeable) message).decode();
                if (log.isDebugEnabled()) {
                    log.debug("Decode decodeable message " + message.getClass().getName());
                }
            } catch (Throwable e) {
                if (log.isWarnEnabled()) {
                    log.warn("Call Decodeable.decode failed: " + e.getMessage(), e);
                }
            } // ~ end of catch
        } // ~ end of if
    } // ~ end of method decode

}

2022年3月21日18:15:24处如果是实现了com.alibaba.dubbo.remoting.Decodeable接口则调用其decode方法完成解码,目前的子类有2个如下图:

在这里插入图片描述

5.2.3:MultiMessageHandler

多消息处理器,处理一次接收多个消息的情况,源码如下:

// com.alibaba.dubbo.remoting.transport.MultiMessageHandler
public class MultiMessageHandler extends AbstractChannelHandlerDelegate {

    public MultiMessageHandler(ChannelHandler handler) {
        super(handler);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        // 如果是com.alibaba.dubbo.remoting.exchange.support.MultiMessage类型消息,则迭代依次处理,否则直接处理
        if (message instanceof MultiMessage) {
            MultiMessage list = (MultiMessage) message;
            for (Object obj : list) {
                handler.received(channel, obj);
            }
        } else {
            handler.received(channel, message);
        }
    }
}

6:Dispatcher

接口源码如下:

@SPI(AllDispatcher.NAME)
public interface Dispatcher {
    // 分发消息到线程池,返回的ChannelHandler内部就带有了分发逻辑,因此调用返回的ChannelHandler进行处理,
    // 也相当于完成了消息分发的功能
    @Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
    ChannelHandler dispatch(ChannelHandler handler, URL url);
}

该模块负责将消息的处理分发到线程池中,交给ChannleHandler处理,如下可能的代码就是本部分要分享的内容:

class FakeCls {
    class FakeFn() {
        executor.execute(new Runnable() {
            handler.received(channel, message)
        });
    }
}

6.1:Dispatcher实现类

类图如下:

在这里插入图片描述

下面我们来分别看下每个实现类。

6.1.1:AllDispatcher

源码如下:

// com.alibaba.dubbo.remoting.transport.dispatcher.all.AllDispatcher
// 默认的Dispatcher
public class AllDispatcher implements Dispatcher {

    public static final String NAME = "all";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        // 2022年3月22日18:45:43
        return new AllChannelHandler(handler, url);
    }
}

2022年3月22日18:45:43处AllChanelHandler也是ChannelHandlerDelegate的子类,因此其也是来装饰 类,如下图可以证明:

在这里插入图片描述

关于AllChanelHandler具体参考6.1.2:AllChanelHandler

6.1.2:AllChanelHandler

AllChannelHandler继承自WrappedChannelHandler,关于WrappedChannelHandler,具体参考6.1.3:WrappedChannelHandler
我们以connected方法为例来看下,源码如下:

class FakeCls {
    // com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.connected
    @Override
    public void connected(Channel channel) throws RemotingException {
        ExecutorService cexecutor = getExecutorService();
        try {
            // 2022年3月23日17:05:54
            cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
        }
    }
}

2022年3月23日17:05:54处创建ChannelEventRunnable对象该对象是一个java.lang.Runnable子类,封装了不同的通道状态的不同操作,具体参考6.2:ChannelEventRunnable。其他的

6.1.3:WrappedChannelHandler

源码如下:

public class WrappedChannelHandler implements ChannelHandlerDelegate {

    protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);

    protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));
    // JUC线程池API
    protected final ExecutorService executor;
    // 被包装的ChannelHandler
    protected final ChannelHandler handler;

    protected final URL url;

    public WrappedChannelHandler(ChannelHandler handler, URL url) {
        this.handler = handler;
        this.url = url;
        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);

        String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
        if (Constants.CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(Constants.SIDE_KEY))) {
            componentKey = Constants.CONSUMER_SIDE;
        }
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        dataStore.put(componentKey, Integer.toString(url.getPort()), executor);
    }
    
    // 关闭线程池
    public void close() {
        try {
            if (executor != null) {
                executor.shutdown();
            }
        } catch (Throwable t) {
            logger.warn("fail to destroy thread pool of server: " + t.getMessage(), t);
        }
    }

    @Override
    public void connected(Channel channel) throws RemotingException {
        handler.connected(channel);
    }

    @Override
    public void disconnected(Channel channel) throws RemotingException {
        handler.disconnected(channel);
    }

    @Override
    public void sent(Channel channel, Object message) throws RemotingException {
        handler.sent(channel, message);
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        handler.received(channel, message);
    }

    @Override
    public void caught(Channel channel, Throwable exception) throws RemotingException {
        handler.caught(channel, exception);
    }

    public ExecutorService getExecutor() {
        return executor;
    }

    @Override
    public ChannelHandler getHandler() {
        if (handler instanceof ChannelHandlerDelegate) {
            return ((ChannelHandlerDelegate) handler).getHandler();
        } else {
            return handler;
        }
    }

    public URL getUrl() {
        return url;
    }

    public ExecutorService getExecutorService() {
        ExecutorService cexecutor = executor;
        if (cexecutor == null || cexecutor.isShutdown()) {
            cexecutor = SHARED_EXECUTOR;
        }
        return cexecutor;
    }
}

主要是线程池相关内容protected final ExecutorService executor,其他基本都是直接调用被包装的ChanelHandler对应方法。

6.2:ChannelEventRunnable

核心源码如下:

// com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable
class FakeCls {
    @Override
    public void run() {
        // 以下不同的通道状态调用ChannelHandler的不同方法进行处理
        if (state == ChannelState.RECEIVED) {
            try {
                handler.received(channel, message);
            } catch (Exception e) {
                logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                        + ", message is " + message, e);
            }
        } else {        
            switch (state) {
            // 连接
            case CONNECTED:
                try {
                    handler.connected(channel);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            // 断开连接
            case DISCONNECTED:
                try {
                    handler.disconnected(channel);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            // 发送消息
            case SENT:
                try {
                    handler.sent(channel, message);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is " + message, e);
                }
            // 异常捕获
            case CAUGHT:
                try {
                    handler.caught(channel, exception);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is: " + message + ", exception is " + exception, e);
                }
                break;
            // 当前未知状态
            default:
                logger.warn("unknown state: " + state + ", message is " + message);
            }
        }

    }

}

7:Codec

有一个废弃的接口,com.alibaba.dubbo.remoting.Codec,目前使用的是com.alibaba.dubbo.remoting.Codec2,本部分分析的也是Codec2。

负责消息编解码的API,源码如下:

// com.alibaba.dubbo.remoting.Codec2
@SPI
public interface Codec2 {

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

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

    enum DecodeResult {
        NEED_MORE_INPUT, SKIP_SOME_INPUT
    }
}

可以看到是自适应 的。先来看下其抽象实现类AbstractCodec

7.1:AbstractCodec

源码如下:

// com.alibaba.dubbo.remoting.transport.AbstractCodec
public abstract class AbstractCodec implements Codec2 {

    private static final Logger logger = LoggerFactory.getLogger(AbstractCodec.class);
    
    // 2022年3月23日18:11:02
    protected static void checkPayload(Channel channel, long size) throws IOException {
        int payload = Constants.DEFAULT_PAYLOAD;
        if (channel != null && channel.getUrl() != null) {
            payload = channel.getUrl().getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);
        }
        if (payload > 0 && size > payload) {
            ExceedPayloadLimitException e = new ExceedPayloadLimitException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
            logger.error(e);
            throw e;
        }
    }
    
    // 2022年3月23日18:24:35
    protected Serialization getSerialization(Channel channel) {
        return CodecSupport.getSerialization(channel.getUrl());
    }
    
    // 判断是否为客户端
    protected boolean isClientSide(Channel channel) {
        String side = (String) channel.getAttribute(Constants.SIDE_KEY);
        if ("client".equals(side)) {
            return true;
        } else if ("server".equals(side)) {
            return false;
        } else {
            InetSocketAddress address = channel.getRemoteAddress();
            URL url = channel.getUrl();
            boolean client = url.getPort() == address.getPort()
                    && NetUtils.filterLocalHost(url.getIp()).equals(
                    NetUtils.filterLocalHost(address.getAddress()
                            .getHostAddress()));
            channel.setAttribute(Constants.SIDE_KEY, client ? "client"
                    : "server");
            return client;
        }
    }
    
    // 判断是否为服务端
    protected boolean isServerSide(Channel channel) {
        return !isClientSide(channel);
    }

}

2022年3月23日18:11:02处是校验实际的数据长度是否超过允许的数据长度,比如配置<dubbo:provider payload="2"/>,当调用为System.err.println(clusterService.sayHi("helloooooo"));时就会超过最大长度,如下输出:

在这里插入图片描述

2022年3月23日18:24:35处是获取序列化类,具体参考7.2:CodecSupport。不同类型的消息对应了不同的子类,如下子类:

在这里插入图片描述

通过集成来增强了编解码的功能(此时没有使用包装,当然也是一个选择的过程)。我们以TransportCodec为例来一起看下,具体参考7.3:TransportCodec

7.2:CodecSupport

编解码工具类,提供了序列化的功能,源码如下:

// com.alibaba.dubbo.remoting.transport.CodecSupport
public class CodecSupport {

    private static final Logger logger = LoggerFactory.getLogger(CodecSupport.class);
    private static Map<Byte, Serialization> ID_SERIALIZATION_MAP = new HashMap<Byte, Serialization>();
    private static Map<Byte, String> ID_SERIALIZATIONNAME_MAP = new HashMap<Byte, String>();

    static {
        Set<String> supportedExtensions = ExtensionLoader.getExtensionLoader(Serialization.class).getSupportedExtensions();
        for (String name : supportedExtensions) {
            Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
            byte idByte = serialization.getContentTypeId();
            if (ID_SERIALIZATION_MAP.containsKey(idByte)) {
                logger.error("Serialization extension " + serialization.getClass().getName()
                        + " has duplicate id to Serialization extension "
                        + ID_SERIALIZATION_MAP.get(idByte).getClass().getName()
                        + ", ignore this Serialization extension");
                continue;
            }
            ID_SERIALIZATION_MAP.put(idByte, serialization);
            ID_SERIALIZATIONNAME_MAP.put(idByte, name);
        }
    }
}

7.3:TransportCodec

传输编解码器,直接使用Serialization进行序列化/反序列化。

7.3.1:编码消息

源码如下:

// com.alibaba.dubbo.remoting.transport.codec.TransportCodec.encode
@Override
public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
    // 包装
    OutputStream output = new ChannelBufferOutputStream(buffer);
    // 获取序列化器并序列化
    ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output);
    // 编码
    encodeData(channel, objectOutput, message);
    // 刷缓存
    objectOutput.flushBuffer();
    if (objectOutput instanceof Cleanable) {
        ((Cleanable) objectOutput).cleanup();
    }
}
7.3.2:解码消息

类似于编码,源码如下:

// com.alibaba.dubbo.remoting.transport.codec.TransportCodec.decode
@Override
public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
    InputStream input = new ChannelBufferInputStream(buffer);
    ObjectInput objectInput = getSerialization(channel).deserialize(channel.getUrl(), input);
    Object object = decodeData(channel, objectInput);
    if (objectInput instanceof Cleanable) {
        ((Cleanable) objectInput).cleanup();
    }
    return object;
}

7.4:CodecAdapter

实现了com.alibaba.dubbo.remoting.Codec2接口,适配com.alibaba.dubbo.remoting.Codec

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值