在工作中碰到一个业务接口时间比较长,需要修改超时时间,不知道原理,在网上搜索,看到有人说如果你觉得自己了解了dubbo的超时机制,那么问问自己以下问题:
超时是针对消费端还是服务端?
超时在哪设置?
超时设置的优先级是什么?
超时的实现原理是什么?
超时解决的是什么问题 ?
如果连这些都回答不上了,那只能说明还没有完全掌握 dubbo的超时机制。
于是索性就自己本地搭了个环境,研究了一下源码。 先来说一说结论:
超时是针对消费端的,消费端会抛出TimeoutException 而服务器端仅仅是一个 warn日志
超时在消费端、服务器端设置,dubbo会合并这两个设置
consumer方法级别 > provider 方法级别 > consumer 接口级别 > provider 接口级别 > consumer 全局级别 > provider 全局级别。如果都没配置,那么就是dubbo默认的1秒
见下面分析
最主要是宝贵的线程,客户端的用户线程不能因为服务端超时而一直类似wait, 导致无法正常响应其他业务。
一、超时时间设置
全局超时配置
指定接口以及特定方法超时配置
观察控制台打印的注册URL:
consumer://172.16.71.30/me.kimi.samples.dubbo.facade.QuestionFacade?application=demo-consumer&category=providers,configurators,routers&check=false&default.proxy=jdk&default.timeout=5000&dubbo=2.6.2&getQuestionById.timeout=7000&interface=me.kimi.samples.dubbo.facade.QuestionFacade&logger=log4j&methods=getQuestionById&pid=13884&side=consumer&timeout=6000×tamp=1536630294523
可以看到:
default.timeout=5000
timeout=6000
getQuestionById.timeout=7000
分别对应了全局、类级别、方法级别的超时设置。
省略一部分调用链,最终会来到这里 DubboInvoker,读取超时时间:
com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation; final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient; if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
} try { boolean isAsync = RpcUtils.isAsync(getUrl(), invocation); boolean isOneway = RpcUtils.isOneway(getUrl(), invocation); // 读取超时时间,这里dubbo已经把服务端的timeout参数和消费端的timeout参数合并
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); if (isOneway) { boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null); return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout);
RpcContext.getContext().setFuture(new FutureAdapter(future)); return new RpcResult();
} else {
RpcContext.getContext().setFuture(null); // 返回 DefaultFuture
// get()在没返回值之前会 阻塞 await
return (Result) currentClient.request(inv, timeout).get();
}
}