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 MapCHANNELS = new ConcurrentHashMap (); private static final Map FUTURES = new ConcurrentHashMap (); // 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就报错。
也可以在消费者端对每个服务自定义配置
这里也需要注意服务端也有一个超时时间
- 全局配置
- 服务配置
当客户端timeout值>服务端timeout值,会出现超时日志,但是仍然可以获取到结果。客户端timeout超时抛出异常时,有一个线程RemotingInvocationTimeoutScan会自动清理对应超时的Future。