说道dubbo的超时机制的话,得先从dubbo支持的3种调用方式说起: 第一种:同步调用 第二种:异步调用 第三种:oneway
我们重点说下同步调用 同步其实就是一直等待结果的返回,注意如果provide和consumer端都没有设置超时时间的话,会默认设置为1000ms,
dubbo是如何实现等待超时的这个机制的呢?(注意:使用的协议是dubbo协议) 我们可以通过追踪源码发现,在进行了远程调用的时候, 会执行到DubboInvoker.doInvoke(Invocation invocation)方法:
同步调用的部分代码片段如下:
protected Result doInvoke(Invocation invocation) throws Throwable {
..... 此处省略大部分代码
RpcContext.getContext().setFuture((Future)null);
return (Result)currentClient.request(inv, timeout).get();
}
会返回一个DefaulteFuture的实例对象,然后同步调用get方法。
dubbo的超时的实现其实就是通过get方法来实现的,我们可以进入到源码看到,最终会调用到get(int timeout)的方法。
在这个逻辑里面做的处理其实包括
1:如果没有设置超时时间,就默认设置为1000ms
2:记录服务调用开始时间
3:通过reentrantlock加锁
Lock lock = new ReentrantLock();
lock.lock();
4:如果response未返回(也就是为null的时候)执行第5步,否则执行第8步
5:通过reentrantlock的Condition竞态对象进行阻塞等待
Condition done = this.lock.newCondition();
done.await((long)timeout, TimeUnit.MILLISECONDS);
6:如果返回了结果或者超时(等待结束时间 - 开始时间 > timeout),退出循环判断,然后释放锁
if (this.isDone() || System.currentTimeMillis() - start > (long)timeout) {
break;
}
7:未返回结果然后超时的情况,直接抛出异常
8:通过this.returnFromResponse()返回具体的结果。
private Object returnFromResponse() throws RemotingException {
Response res = this.response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
} else if (res.getStatus() == 20) {
return res.getResult();
} else if (res.getStatus() != Response.SERVER_TIMEOUT && res.getStatus() != Response.CLIENT_TIMEOUT)) {
throw new RemotingException(this.channel, res.getErrorMessage());
} else {
throw new TimeoutException(res.getStatus() == 31, this.channel, res.getErrorMessage());
}
}
对于returnFromResponse()这个方法中能通过不同status来判断超时的解读,我们需要看另外的一个逻辑,
我们再细看DefaultFuture类,其实在加载类的时候,有一个静态代码块:
static {
Thread th = new Thread(new DefaultFuture.RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
这个静态代码块做的事情就是启动了一个守护线程,这个守护线程主要做的事情是什么呢?
其实就是不断的遍历DefaultFuture.FUTURES这个静态常量,
DefaultFuture.FUTURES的作用是用来保存dubbo的RPC调用中,request.id和Channel的关系。
1:通过channel判断是否已经返回,并且是否已经超时,以此来对超时的请求设置一个特定的超时Response实例。
2:并且status设置为Response.SERVER_TIMEOUT 或者 Response.CLIENT_TIMEOUT,
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
3:设置response结果并且唤醒同步调用过程中调用get接口导致阻塞的流程
this.done.signal();
以及释放锁
通过分析这个守护线程,可以知道returnFromResponse方法中,response有值了,还要通过判断status的来处理是否超时。就是因为超时的时候会设置一个超时的特殊response
以上的过期策略是旧版本dubbo的实现,新版本的dubbo的实现改为时间论的方式,具体得看各自工程中使用的版本为准。