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