深度解析dubbo信息交换层请求与响应

本文基于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。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页