dubbo超時異常 原 薦

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章