Dubbo是一款开源RPC框架,底层使用Netty作为默认的数据传输方式,那么请求处理理论上是异步的,但是我们在使用Dubbo时,是同步拿到结果的,这是因为Dubbo框架帮我们做了异步转同步的操作。
构造一个DefaultFuture,并将DefaultFuture放在Map中,key为请求是生成的唯一id。
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<>();
// 每次请求都会生成一个DefaultFuture对象,然后保存到FUTURES中,
// 请求返回结果时,根据id从FUTURES中找到对应的DefaultFuture对象,并删除
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<>();
public static final Timer TIME_OUT_TIMER = new HashedWheelTimer(
new NamedThreadFactory("dubbo-future-timeout", true),
30,
TimeUnit.MILLISECONDS);
// 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 Timeout timeoutCheckTask;
private 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(TIMEOUT_KEY, DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
获取结果,如果结果未返回就休眠等待。
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = Constants.DEFAULT_TIMEOUT;
}
// isDone()方法就是判断Response是否有值(即是否有返回结果)
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();
}
服务提供者处理完成将结果写回,并唤醒消费者。
public static void received(Channel channel, Response response) {
try {
// 根据请求id从FUTURES中获取DefaultFuture,并删除
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
// CHANNELS也删除
CHANNELS.remove(response.getId());
}
}
private void doReceived(Response res) {
lock.lock();
try {
response = res;
if (done != null) {
// 唤醒阻塞的线程
done.signal();
}
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}