精盡 Dubbo 源碼分析 —— NIO 服務器(四)之 Exchange 層

1.概述

exchange 信息交換層:封裝請求響應模式,同步轉異步,以 Request, Response 爲中心,擴展接口爲 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer。
類圖:
在這裏插入圖片描述

2.ExchangeChannel

繼承 Channel 接口,信息交換通道接口。方法如下:

/**
 * ExchangeChannel. (API/SPI, Prototype, ThreadSafe)
 *
 * 信息交換通道接口
 */
public interface ExchangeChannel extends Channel {

    /**
     * send request.
     *
     * 發送請求
     *
     * @param request 請求
     * @return response future
     * @throws RemotingException 遠程調用,發生異常
     */
    ResponseFuture request(Object request) throws RemotingException;

    /**
     * send request.
     *
     * 發送請求
     *
     * @param request 請求
     * @param timeout 超時時長
     * @return response future
     * @throws RemotingException 遠程調用,發生異常
     */
    ResponseFuture request(Object request, int timeout) throws RemotingException;

    /**
     * get message handler.
     *
     * 獲得信息交換處理器
     *
     * @return message handler
     */
    ExchangeHandler getExchangeHandler();

    /**
     * graceful close.
     *
     * 優雅關閉
     *
     * @param timeout 超時時長
     */
    void close(int timeout);

}
2.1 HeaderExchangeChannel

現 ExchangeChannel 接口,基於消息頭部( Header )的信息交換通道實現類。
構造方法:

/**
 * ExchangeReceiver
 *
 * 基於消息頭部( Header )的信息交換通道實現類
 */
final class HeaderExchangeChannel implements ExchangeChannel {

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

    /**
     * 通道鍵
     */
    private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL";

    /**
     * 通道
     */
    private final Channel channel;
    /**
     * 是否關閉
     */
    private volatile boolean closed = false;

    HeaderExchangeChannel(Channel channel) {
        if (channel == null) {
            throw new IllegalArgumentException("channel == null");
        }
        this.channel = channel;
    }
}

getOrAddChannel(Channel) 靜態方法,創建 HeaderExchangeChannel 對象。代碼如下:

    /**
     * 創建 HeaderExchangeChannel 對象
     *
     * @param ch 通道
     * @return HeaderExchangeChannel 對象
     */
    static HeaderExchangeChannel getOrAddChannel(Channel ch) {
        if (ch == null) {
            return null;
        }
        HeaderExchangeChannel ret = (HeaderExchangeChannel) ch.getAttribute(CHANNEL_KEY);
        if (ret == null) {
            ret = new HeaderExchangeChannel(ch);
            if (ch.isConnected()) { // 已連接
                ch.setAttribute(CHANNEL_KEY, ret);
            }
        }
        return ret;
    }

發送請求:

    @Override
    public ResponseFuture request(Object request, int timeout) throws RemotingException {
        if (closed) {
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
        }
        // create request. 創建請求
        Request req = new Request();
        req.setVersion("2.0.0");
        req.setTwoWay(true); // 需要響應
        req.setData(request);
        // 創建 DefaultFuture 對象
        DefaultFuture future = new DefaultFuture(channel, req, timeout);
        try {
            // 發送請求
            channel.send(req);
        } catch (RemotingException e) { // 發生異常,取消 DefaultFuture
            future.cancel();
            throw e;
        }
        // 返回 DefaultFuture 對象
        return future;
    }

優雅關閉:

    @Override
    public void close(int timeout) {
        if (closed) {
            return;
        }
        closed = true;
        // 等待請求完成
        if (timeout > 0) {
            long start = System.currentTimeMillis();
            while (DefaultFuture.hasFuture(channel) && System.currentTimeMillis() - start < timeout) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }
        // 關閉通道
        close();
    }

3. ExchangeClient

Client ,ExchangeChannel 接口,信息交換客戶端接口。
無自定義方法。

3.1 HeaderExchangeClient

實現 ExchangeClient 接口,基於消息頭部( Header )的信息交換客戶端實現類。


/**
 * DefaultMessageClient
 *
 * 基於消息頭部( Header )的信息交換客戶端實現類
 */
public class HeaderExchangeClient implements ExchangeClient {

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

    /**
     * 定時器線程池
     */
    private static final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(2, new NamedThreadFactory("dubbo-remoting-client-heartbeat", true));
    /**
     * 客戶端
     */
    private final Client client;
    /**
     * 信息交換通道
     */
    private final ExchangeChannel channel;
    // heartbeat timer
    /**
     * 心跳定時器
     */
    private ScheduledFuture<?> heartbeatTimer;
    /**
     * 是否心跳
     */
    private int heartbeat;
    // heartbeat timeout (ms), default value is 0 , won't execute a heartbeat.
    /**
     * 心跳間隔,單位:毫秒
     */
    private int heartbeatTimeout;

    public HeaderExchangeClient(Client client, boolean needHeartbeat) {
        if (client == null) {
            throw new IllegalArgumentException("client == null");
        }
        this.client = client;
        // 創建 HeaderExchangeChannel 對象
        this.channel = new HeaderExchangeChannel(client);
        // 讀取心跳相關配置
        String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
        this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
        this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
        if (heartbeatTimeout < heartbeat * 2) { // 避免間隔太短
            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
        }
        // 發起心跳定時器
        if (needHeartbeat) {
            startHeatbeatTimer();
        }
    }
}

發起心跳定時器:

    private void startHeatbeatTimer() {
        // 停止原有定時任務
        stopHeartbeatTimer();
        // 發起新的定時任務
        if (heartbeat > 0) {
            heartbeatTimer = scheduled.scheduleWithFixedDelay(
                    new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
                        public Collection<Channel> getChannels() {
                            return Collections.<Channel>singletonList(HeaderExchangeClient.this);
                        }
                    }, heartbeat, heartbeatTimeout),
                    heartbeat, heartbeat, TimeUnit.MILLISECONDS);
        }
    }

4. ExchangeServer

繼承 Server 接口,信息交換服務器接口。方法如下:

/**
 * ExchangeServer. (API/SPI, Prototype, ThreadSafe)
 *
 * 信息交換服務器接口
 */
public interface ExchangeServer extends Server {

    /**
     * get channels.
     *
     * 獲得通道數組
     *
     * @return channels 通道數組
     */
    Collection<ExchangeChannel> getExchangeChannels();

    /**
     * get channel.
     *
     * 獲得指定通道
     *
     * @param remoteAddress 遠程地址
     * @return channel 通道
     */
    ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress);

}
4.1 HeaderExchangeServer

實現 ExchangeServer 接口,基於消息頭部( Header )的信息交換服務器實現類。

/**
 * 定時器線程池
 */
private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1, new NamedThreadFactory("dubbo-remoting-server-heartbeat", true));
/**
 * 服務器
 */
private final Server server;
// heartbeat timer
/**
 * 心跳定時器
 */
private ScheduledFuture<?> heatbeatTimer;
/**
 * 是否心跳
 */
// heartbeat timeout (ms), default value is 0 , won't execute a heartbeat.
private int heartbeat;
/**
 * 心跳間隔,單位:毫秒
 */
private int heartbeatTimeout;
/**
 * 是否關閉
 */
private AtomicBoolean closed = new AtomicBoolean(false);

public HeaderExchangeServer(Server server) {
    if (server == null) {
        throw new IllegalArgumentException("server == null");
    }
    // 讀取心跳相關配置
    this.server = server;
    this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
    this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
    if (heartbeatTimeout < heartbeat * 2) {
        throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
    }
    // 發起心跳定時器
    startHeatbeatTimer();
}

重置屬性:

    @Override
    public void reset(URL url) {
        // 重置服務器
        server.reset(url);
        try {
            if (url.hasParameter(Constants.HEARTBEAT_KEY)
                    || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {
                int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);
                int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h * 3);
                if (t < h * 2) {
                    throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
                }
                // 重置定時任務
                if (h != heartbeat || t != heartbeatTimeout) {
                    heartbeat = h;
                    heartbeatTimeout = t;
                    startHeatbeatTimer();
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
    }

優雅關閉

 public void close(final int timeout) {
        // 關閉
        startClose();
        if (timeout > 0) {
            final long max = (long) timeout;
            final long start = System.currentTimeMillis();
            // 發送 READONLY 事件給所有 Client ,表示 Server 不可讀了。
            if (getUrl().getParameter(Constants.CHANNEL_SEND_READONLYEVENT_KEY, true)) {
                sendChannelReadOnlyEvent();
            }
            // 等待請求完成
            while (HeaderExchangeServer.this.isRunning() && System.currentTimeMillis() - start < max) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }
        // 關閉心跳定時器
        doClose();
        // 關閉服務器
        server.close(timeout);
    }

sendChannelReadOnlyEvent() 方法,廣播客戶端,READONLY_EVENT 事件。代碼如下:


    /**
     * 廣播客戶端,READONLY_EVENT 事件
     */
    private void sendChannelReadOnlyEvent() {
        // 創建 READONLY_EVENT 請求
        Request request = new Request();
        request.setEvent(Request.READONLY_EVENT);
        request.setTwoWay(false); // 無需響應
        request.setVersion(Version.getVersion());

        // 發送給所有 Client
        Collection<Channel> channels = getChannels();
        for (Channel channel : channels) {
            try {
                if (channel.isConnected())
                    channel.send(request, getUrl().getParameter(Constants.CHANNEL_READONLYEVENT_SENT_KEY, true));
            } catch (RemotingException e) {
                logger.warn("send connot write messge error.", e);
            }
        }
    }
4.2 ExchangeServerDelegate

實現 ExchangeServer 接口,信息交換服務器裝飾者。在每個實現的方法裏,直接調用被裝飾的 server 屬性的方法。

5. 請求/響應模型

5.1 Request

請求。代碼如下:

/**
 * 事件 - 心跳
 */
public static final String HEARTBEAT_EVENT = null;
/**
 * 事件 - 只讀
 */
public static final String READONLY_EVENT = "R";

/**
 * 請求編號自增序列
 */
private static final AtomicLong INVOKE_ID = new AtomicLong(0);

/**
 * 請求編號
 */
private final long mId;
/**
 * Dubbo 版本
 */
private String mVersion;
/**
 * 是否需要響應
 *
 * true-需要
 * false-不需要
 */
private boolean mTwoWay = true;
/**
 * 是否是事件。例如,心跳事件。
 */
private boolean mEvent = false;
/**
 * 是否異常的請求。
 *
 * 在消息解析的時候,會出現。
 */
private boolean mBroken = false;
/**
 * 數據
 */
private Object mData;
5.2 Response

響應。代碼如下:

public class Response {

    /**
     * 事件 - 心跳
     */
    public static final String HEARTBEAT_EVENT = null;
    /**
     * 事件 - 只讀
     */
    public static final String READONLY_EVENT = "R";

    /**
     * ok.
     */
    public static final byte OK = 20;

    /**
     * clien side timeout.
     */
    public static final byte CLIENT_TIMEOUT = 30;

    /**
     * server side timeout.
     */
    public static final byte SERVER_TIMEOUT = 31;

    /**
     * request format error.
     */
    public static final byte BAD_REQUEST = 40;

    /**
     * response format error.
     */
    public static final byte BAD_RESPONSE = 50;

    /**
     * service not found.
     */
    public static final byte SERVICE_NOT_FOUND = 60;

    /**
     * service error.
     */
    public static final byte SERVICE_ERROR = 70;

    /**
     * internal server error.
     */
    public static final byte SERVER_ERROR = 80;

    /**
     * internal server error.
     */
    public static final byte CLIENT_ERROR = 90;

    /**
     * server side threadpool exhausted and quick return.
     */
    public static final byte SERVER_THREADPOOL_EXHAUSTED_ERROR = 100;

    /**
     * 響應編號
     *
     * 一個 {@link Request#mId} 和 {@link Response#mId} 一一對應。
     */
    private long mId = 0;
    /**
     * 版本
     */
    private String mVersion;
    /**
     * 狀態
     */
    private byte mStatus = OK;
    /**
     * 是否事件
     */
    private boolean mEvent = false;
    /**
     * 錯誤消息
     */
    private String mErrorMsg;
    /**
     * 結果
     */
    private Object mResult;

5.3 ResponseFuture

響應 Future 接口。方法如下:

/**
 *
 * 響應 Future
 */
public interface ResponseFuture {

    /**
     * get result.
     * 獲得值
     */
    Object get() throws RemotingException;

    /**
      * 獲得值
     */
    Object get(int timeoutInMillis) throws RemotingException;

    /**
     * 設置回調
     */
    void setCallback(ResponseCallback callback);

    /**
     * 是否完成
     */
    boolean isDone();

}
5.3.1 ResponseCallback

響應回調接口。方法如下:

/**
 * Callback
 *
 * 響應回調
 */
public interface ResponseCallback {

    /**
     * done.
     * 處理執行完成
     *
     * @param response 結果
     */
    void done(Object response);

    /**
     * caught exception.
     * 處理髮生異常
     *
     * @param exception 異常
     */
    void caught(Throwable exception);

}
5.3.2 DefaultFuture

實現 ResponseFuture 接口,默認響應 Future 實現類。同時,它也是所有 DefaultFuture 的管理容器。

/**
 * 通道集合
 *
 * key:請求編號
 */
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
/**
 * Future 集合
 *
 * key:請求編號
 */
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();

/**
 * 請求編號
 */
// invoke id.
private final long id;
/**
 * 通道
 */
private final Channel channel;
/**
 * 請求
 */
private final Request request;
/**
 * 超時
 */
private final int timeout;
/**
 * 創建開始時間
 */
private final long start = System.currentTimeMillis();
/**
 * 發送請求時間
 */
private volatile long sent;
/**
 * 響應
 */
private volatile Response response;
/**
 * 回調
 */
private volatile ResponseCallback callback;

public DefaultFuture(Channel channel, Request request, int timeout) {
    this.channel = channel;
    this.request = request;
    this.id = request.getId();
    this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
    // put into waiting map.
    FUTURES.put(id, this);
    CHANNELS.put(id, channel);
}
5.4 MultiMessage

實現 Iterable 接口,多消息的封裝。代碼如下:

public final class MultiMessage implements Iterable {

    private final List messages = new ArrayList();
    
    // ... 省略方法
}

6. Handler

6.1 HeartbeatHandler

實現 AbstractChannelHandlerDelegate 抽象類,心跳處理器,處理心跳事件。

    public void received(Channel channel, Object message) throws RemotingException {
        // 設置最後的讀時間
        setReadTimestamp(channel);
        // 如果是心跳事件請求,返回心跳事件的響應
        if (isHeartbeatRequest(message)) {
            Request req = (Request) message;
            if (req.isTwoWay()) {
                Response res = new Response(req.getId(), req.getVersion());
                res.setEvent(Response.HEARTBEAT_EVENT);
                channel.send(res);
                if (logger.isInfoEnabled()) {
                    int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
                                + ", cause: The channel has no data-transmission exceeds a heartbeat period"
                                + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
                    }
                }
            }
            return;
        }
        // 如果是心跳事件響應,返回
        if (isHeartbeatResponse(message)) {
            if (logger.isDebugEnabled()) {
                logger.debug(new StringBuilder(32).append("Receive heartbeat response in thread ").append(Thread.currentThread().getName()).toString());
            }
            return;
        }
        // 提交給裝飾的 `handler`,繼續處理
        handler.received(channel, message);
    }
6.1.1 HeartBeatTask

實現 Runnable 接口,心跳任務。

    public void run() {
        try {
            long now = System.currentTimeMillis();
            for (Channel channel : channelProvider.getChannels()) {
                if (channel.isClosed()) {
                    continue;
                }
                try {
                    Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);
                    Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);
                    // 最後讀寫的時間,任一超過心跳間隔,發送心跳
                    if ((lastRead != null && now - lastRead > heartbeat)
                            || (lastWrite != null && now - lastWrite > heartbeat)) {
                        Request req = new Request();
                        req.setVersion("2.0.0");
                        req.setTwoWay(true); // 需要響應
                        req.setEvent(Request.HEARTBEAT_EVENT);
                        channel.send(req);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()
                                    + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");
                        }
                    }
                    // 最後讀的時間,超過心跳超時時間
                    if (lastRead != null && now - lastRead > heartbeatTimeout) {
                        logger.warn("Close channel " + channel
                                + ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");
                        // 客戶端側,重新連接服務端
                        if (channel instanceof Client) {
                            try {
                                ((Client) channel).reconnect();
                            } catch (Exception e) {
                                //do nothing
                            }
                        // 服務端側,關閉客戶端連接
                        } else {
                            channel.close();
                        }
                    }
                } catch (Throwable t) {
                    logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);
                }
            }
        } catch (Throwable t) {
            logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);
        }
    }

【任務一】最後讀或寫的時間,任一超過心跳間隔 heartbeat ,發送心跳。
【任務二】最後讀的時間,超過心跳超時時間 heartbeatTimeout ,分成兩種情況:
客戶端側,重連連接服務端。
服務端側,關閉客戶端連接。

6.2 HeaderExchangeHandler

實現 ChannelHandlerDelegate 接口,基於消息頭部( Header )的信息交換處理器實現類。

 public void received(Channel channel, Object message) throws RemotingException {
        // 設置最後的讀時間
        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
        // 創建 ExchangeChannel 對象
        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        try {
            // 處理請求( Request )
            if (message instanceof Request) {
                // handle request.
                Request request = (Request) message;
                // 處理事件請求
                if (request.isEvent()) {
                    handlerEvent(channel, request);
                } else {
                    // 處理普通請求
                    if (request.isTwoWay()) {
                        Response response = handleRequest(exchangeChannel, request);
                        channel.send(response);
                    // 提交給裝飾的 `handler`,繼續處理
                    } else {
                        handler.received(exchangeChannel, request.getData());
                    }
                }
            // 處理響應( Response )
            } else if (message instanceof Response) {
                handleResponse(channel, (Response) message);
            // 處理 String
            } else if (message instanceof String) {
                // 客戶端側,不支持 String
                if (isClientSide(channel)) {
                    Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
                    logger.error(e.getMessage(), e);
                // 服務端側,目前是 telnet 命令
                } else {
                    String echo = handler.telnet(channel, (String) message);
                    if (echo != null && echo.length() > 0) {
                        channel.send(echo);
                    }
                }
                // 提交給裝飾的 `handler`,繼續處理
            } else {
                handler.received(exchangeChannel, message);
            }
        } finally {
            // 移除 ExchangeChannel 對象,若已斷開
            HeaderExchangeChannel.removeChannelIfDisconnected(channel);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章