本文基于dubbo v2.6.x
1. ExchangeChannel
在《深度解析dubbo信息交换层Exchanger》解析了信息交换层的Exchanger,在解析过程中或多或少的提及到ExchangeChannel,在HeaderExchangeClient中就将client包装成了ExchangeChannel,在HeaderExchangeServer中将channel包装成了ExchangeChannel,Channel字面意思通道,可以将它理解为客户端与服务端的连接。接下来看下ExchangeChannel,首先它是个接口,继承了Channel 接口,Channel接口主要是抽象了对属性的操作,而ExchangeChannel则是抽象了发送请求Request操作,我们之前也说过,信息交换层封装了请求响应模式,发送请求便是request。
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.
* 获取消息handler
* @return message handler
*/
ExchangeHandler getExchangeHandler();
/**
* graceful close.
* 优雅关闭
* @param timeout
*/
@Override
void close(int timeout);
}
1.1 HeaderExchangeChannel
首先来看下它的构造,其实就是检验channel,然后赋值给自己的成员。
HeaderExchangeChannel(Channel channel) {
if (channel == null) {
throw new IllegalArgumentException("channel == null");
}
this.channel = channel;
}
接下来看下两个send方法
@Override
public void send(Object message) throws RemotingException {
send(message, getUrl().getParameter(Constants.SENT_KEY, false));
}
@Override
public void send(Object message, boolean sent) throws RemotingException {
if (closed) {// 已经关闭 抛出异常
throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!");
}
if (message instanceof Request
|| message instanceof Response
|| message instanceof String) {
channel.send(message, sent);
} else {
Request request = new Request();
request.setVersion(Version.getProtocolVersion());
request.setTwoWay(false);
request.setData(message);
channel.send(request, sent);
}
}
其实就是判断要发送的对象是否是Request类型或者是Respose类型,String类类型(这个是telnet),如果是的话,直接发送,如果不是的话创建Request对象人,然后进行封装发送。
接下来再来看下这两个request请求方法。
// 发送消息
@Override
public ResponseFuture request(Object request) throws RemotingException {
return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
}
/**
* 发送消息
* @param request invocation
* @param timeout 超时时间
* @return
* @throws RemotingException
*/
@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对象
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
// 发送请求 这个channel 其实就是那个client
channel.send(req);
} catch (RemotingException e) {
future.cancel(); // 出现异常 future 取消
throw e;
}
return future;
}
request请求方法其实就是将消息进行封装成Request对象,然后进行发送,使用DefaultFuture异步接收响应结果,如果出现异常,就调用DefaultFuture的cancel方法取消等待响应并抛出异常,最后将future返回。
再往下就是介绍这个两个方法,一个是通过channel获取对应的HeaderExchangeChannel对象,一个是移除channel的HeaderExchangeChannel对象。
//创建 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;
}
static void removeChannelIfDisconnected(Channel ch) {
if (ch != null && !ch.isConnected()) {
ch.removeAttribute(CHANNEL_KEY);
}
}
这两个方法是静态方法,可以看到就是获取Channel 的 Channelkey属性值,也就是HeaderExchangeChannel.CHANNEL的属性值,这个属性是就是它对应的HeaderExchangeChannel对象,如果没有就新创建一个,然后塞到这个属性对应的值上,最后返回。remove的话就是移除channel上对应的HeaderExchangeChannel.CHANNEL属性值
最后再来看下这个优雅关闭方法:
// graceful close 优雅关闭
@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();
}
先是判断关闭状态,如果已经关闭,直接return,否则设置关闭状态,如果还有任务没有关闭,并且还没到关闭超时时间,其实就是在关闭超时范围内尽量等待任务完成。最后调用close方法关闭,这个close方法其实就是调用channel的close方法关闭的。
2.Request与Response
Request与Response 请求与响应实体,将Invocation与Result进行封装成请求响应模式。我们稍微看下Request与Response的属性。
请求实体Request:
public class Request {
/**
* 心跳事件
*/
public static final String HEARTBEAT_EVENT = null;
/**
* 事件 只读
*/
public static final String READONLY_EVENT = "R";
// 请求编号自增序列
private static final AtomicLong INVOKE_ID = new AtomicLong(0);
// 请求id
private final long mId;
//dubbo version
private String mVersion;
/**
* 是否需要响应
* true 需要
* false 不需要
*/
private boolean mTwoWay = true;
/**
* 是否是事件。例如,心跳事件。
*/
private boolean mEvent = false;
/**
* 是否异常的请求。
* 在消息解析的时候,会出现。
*/
private boolean mBroken = false;
/**
* 数据
*/
private Object mData;
public Request() {
mId = newId();//创建一个请求id
}
public Request(long id) {
mId = id;
}
/**
* 获取请求id
* @return
*/
private static long newId() {
// getAndIncrement() When it grows to MAX_VALUE, it will grow to MIN_VALUE, and the negative can be used as ID
return INVOKE_ID.getAndIncrement();
}
...
}
响应实体Response:
public class Response {
public static final String HEARTBEAT_EVENT = null;
// 只读,服务端关闭的时候,会向客户端channel发送只读消息
public static final String READONLY_EVENT = "R";
public static final byte OK = 20;
// 客户端超时
public static final byte CLIENT_TIMEOUT = 30;
//服务端超时
public static final byte SERVER_TIMEOUT = 31;
//通道不活动,直接返回未完成的请求
public static final byte CHANNEL_INACTIVE = 35;
//request格式化错误
public static final byte BAD_REQUEST = 40;
//response 格式化错误
public static final byte BAD_RESPONSE = 50;
//服务未找到
public static final byte SERVICE_NOT_FOUND = 60;
//服务错误
public static final byte SERVICE_ERROR = 70;
//服务端错误
public static final byte SERVER_ERROR = 80;
//客户端错误
public static final byte CLIENT_ERROR = 90;
//服务器端线程池耗尽,快速返回。
public static final byte SERVER_THREADPOOL_EXHAUSTED_ERROR = 100;
private long mId = 0;// 请求id
private String mVersion;// 版本
private byte mStatus = OK;//状态
private boolean mEvent = false;// 是否是事件
private String mErrorMsg;// 错误消息
private Object mResult;// 结果
....
}
3.ResponseFuture
ResponseFuture是异步响应的抽象,在上面ExchangeChannel的Request方法中,发送出去了请求,返回的是ResponseFuture,可以通过get方法来获取响应值,它有两个实现类,分别是DefaultFuture与SimpleFuture ,这个SimpleFuture没啥好解析的,重点来看下DefaultFuture的实现。
3.1 DefaultFuture 源码解析
DefaultFuture 实现ResponseFuture ,是异步响应的默认实现。
// 缓存请求id 与 channel 的对应关系
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
//缓存的 请求id 与 future 对应关系
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
static {
// 创建扫描 调用超时 的线程
Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
首先看下CHANNELS 与FUTURES 静态成员与 static静态块。CHANNELS 缓存着请求id与对应channel的对应关系,FUTURES缓存着请求id与ResponseFuture 的对应关系。
// invoke id.
private final long id; // 请求编号/ 请求id
private final Channel channel; // 通道
private final Request request; // 请求实体
private final int timeout;// 超时时间
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
// 创建开始时间
private final long start = System.currentTimeMillis();
private volatile long sent;// 发送时间
private volatile Response response;// 响应 用是不是null来判断是否结束
private volatile ResponseCallback callback;// 回调
接下来看下构造方法:
public DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();// 请求id
//默认超时时间 1s
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
/**
*将请求放到缓存中,异步会根据请求编号获取请求future
*/
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
其实就是成员变量的赋值,需要注意的是这个timeout请求超时时间属性,缺省的话就是1s,最后面两行将id与future对应关系缓存到FUTURES 中,id与channel的对应关系缓存到CHANNELS中。
接下来看下get方法:
@Override
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {// timeout 小于等于0 设置默认1s
timeout = Constants.DEFAULT_TIMEOUT;
}
if (!isDone()) {
long start = System.currentTimeMillis();
lock.lock();
try {
while (!isDone()) {// 没有完成
done.await(timeout, TimeUnit.MILLISECONDS);
if (isDone() || System.currentTimeMillis() - start > timeout) {// 结束 或者超时的时候 结束循环
break;
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
if (!isDone()) {//抛出超时异常
throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
}
}
return returnFromResponse();
}
在get方法中,先是判断timeout ,如果小于等于0就设置为默认的1s,接着如果没有完成(isDone=false,他这里判断isDone,其实就是判断Response是否是null,如果是null,就是还没有完成,如果不是null就是完成了),就获取锁,然后等带到超时时间那么久,如果完成了或者是时间差大于了超时时间,就跳出循环,最后是释放锁,如果还是没有完成,就抛出超时异常,否则调用returnFromResponse方法返回。
接下来看下 returnFromResponse 返回响应实体的方法
//返回结果
private Object returnFromResponse() throws RemotingException {
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
return res.getResult();// 正常返回
}
// 超时异常
if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
}
//运行时异常
throw new RemotingException(channel, res.getErrorMessage());
}
如果response是null抛出异常,如果状态是OK,就获取Result实体正常返回(这里就是对 Reponse的拆分,脱去Response这层),如果是超时状态,就封装超时异常抛出。最后抛出运行时异常。
接下来我们再来看下设置回调setCallback 方法:
@Override
public void setCallback(ResponseCallback callback) {
if (isDone()) {
invokeCallback(callback);
} else {
boolean isdone = false;
lock.lock();
try {
if (!isDone()) {
this.callback = callback;
} else {
isdone = true;
}
} finally {
lock.unlock();
}
if (isdone) {
invokeCallback(callback);
}
}
}
设置回调其实就是给callback 属性赋值,如果完成,则立即进行回调,如果没有的话,就获取锁,然后给callback赋值,最后释放锁,在获取锁期间,如果完成了,也需要立即进行回调。
先看下ResponseCallback接口的定义:
这个进行回调invokeCallback方法其实就是判断response的状态值,如果是正常返回,就调用回调对象的done方法,如果是异常的话调用回调对象的caught 方法。
在上面我们看完了获取方法get,这里再来看下接收方法received ,就是服务端响应回来消息,然后会调用received方法来告诉这个future结果已经响应回来了。
received 方法是个静态方法,先是从FUTURES 中移除请求id对应的future,如果future不是null,就调用其doReceived(response)进行真正的接收,否则打印警告日志,最终还是要将请求id与channel的对应关系缓存移除。
首先是获取锁,然后给设置response 的值,唤醒等待的线程,释放锁,如果需要回调就进行回调。
到这里DefaultFuture 的解析基本就完成了,还有一个调用超时扫描的任务需要看下,就是静态代码块中创建线程中传进去的RemotingInvocationTimeoutScan 对象。
其实就是遍历所有的任务,然后计算开始时间与现在时间的差值,看看有没有超时,如果超时,封装一个超时的响应,最后调用DefaultFuture的received方法告诉已经接收到响应。
4. HeaderExchangeHandler
HeaderExchangeHandler是在信息交换层的一个channel handler,主要是实现了数据接收过来数据的分发,能够根据接收过来的数据类型不同作出不同的处理。
我们先来看下sent方法(这里为啥叫sent方法呢?因为这个sent方法并不是真正发送数据的,而是发送完数据之后反过来调用的):
在sent方法中,首先设置写出时间戳,这个是为了心跳的,使用HeaderExchangeChannel包装一下channel,然后再调用handler的sent方法。如果这个message是Request类型,也就是本次发送数据是请求数据,然后调用 DefaultFuture.sent(channel, request); 记录一下发送时间,最后的就是处理异常了。
接下来我们来重点看下received接收消息的方法。
这个received 方法是正儿八经的接收数据的方法,首先是设置读取时间(这个也是为了心跳),通过channel获取HeaderExchangeChannel。然后如果数据是Request类型,也就是请求,如果是事件,交给handlerEvent方法处理,不是事件类型,如果是双向的(也就是需要回复的)交给handleRequest 方法处理,并把处理结果Response写回到channel中,其实这里就是正儿八经的远程调用,也就是用户的请求,如果是单向的,就交给其他handler的received 方法处理。
如果数据是响应实体类型,也就是Response类型,说明这是个响应,就交给handleResponse 处理。
如果数据是字符串类型,判断channel是哪端,如果是客户端的话,就抛出异常,因为客户端不接收字符串请求,如果是服务端,说明是个telnet请求,交给handler的telnet来处理,并将返回结果,写回到channel中。
接下来我们挨个看下那几个handler方法。
handlerEvent 方法是个处理事件的,如果收到的内容是R ,也就是服务端关闭的时候向客户端们发送事件通知,然后channel就设置channel.readonly 属性是true,表示不再向服务端发送请求。
handleRequest是处理正常请求的,判断如果请求是个bad request ,就直接封装个bad request的响应。否则,获取请求的data,这个data其实就是Invocation,调用handler的reply方法进行执行,如果没有出现异常,将执行结果塞到Response中,并设置Response的状态是OK,出现异常就将状态设置服务端异常,将异常信息塞到error message中,最后返回response。
handleResponse方法是处理响应的,如果response不是null并且不是心跳,说明这是个正常请求的响应,然后交给DefaultFuture.received的方法来处理响应(其实就是设置异步请求的响应结果,并唤醒相关线程)
接下来我们看下caught异常处理方法:
如果异常是执行异常,还是个需要回复的请求,并且不是心跳(这其实就是普通远程调用),就封装一个Respose响应,设置状态为服务端错误,然后发送回去。否则 交给下一个handler处理。
connected与disconnected 方法:
当连接的时候会调用connected方法, 断开连接的时候会调用disconnected方法。
connected 与disconnected 方法都会设置read 与write时间戳。然后调用下一个handler进行相应的处理,disconnected 最后会调用DefaultFuture.closeChannel方法来关闭channel,其实就是把那些与该channel有关没有处理完的异步任务响应一个Channel关闭的Response。