dubbo超時異常
在調用dubbo服務時經常看到如下錯誤: Caused by: com.alibaba.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer.
源碼分析
客戶端調用遠程服務時,本地會生成一個DefaultFuture,調用DefaultFuture.get()獲取遠程服務返回的結構,此方法獲取鎖,調用await方法,此時當前線程進入等待隊列,此線程會有兩種結果過:要麼超時,拋出TimeOutException;如果被喚醒,則返回rpc的結果。 而這裏的報錯很明顯是由於等待服務端返回結果時客戶端超時異常,查看源碼如下:
public class DefaultFuture implements ResponseFuture {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
// invoke id.
private final long 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;
private volatile ResponseCallback callback;
public 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(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
public Object get() throws RemotingException {
return get(timeout);
}
public Object get(int timeout) throws RemotingException {
if (timeout <= 0) {
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();
}
}
這裏的超時時間設置的是1s
public static final int DEFAULT_TIMEOUT = 1000;
解決方案
檢查消費者配置查看是否配置總的超時時長,這裏建議配置一個總的,我由於沒配置導致使用的是默認配置使得超過1s就報錯。
<dubbo:consumer timeout="5000" />
也可以在消費者端對每個服務自定義配置
<dubbo:reference interface="com.foo.BarService" timeout="2000"/>
這裏也需要注意服務端也有一個超時時間
- 全局配置
<dubbo:provider timeout="5000"/>
- 服務配置
<dubbo:provider interface="com.foo.BarService" timeout="5000">
當客戶端timeout值>服務端timeout值,會出現超時日誌,但是仍然可以獲取到結果。客戶端timeout超時拋出異常時,有一個線程RemotingInvocationTimeoutScan會自動清理對應超時的Future。