在dubbo中,可以设置服务的超时时间,服务端和消费端可以分别设置,但是服务消费端在调用的时候,会根据timeout配置的优先级,去获取阈值,消费端的配置高于消费端的,那这篇笔记主要记录的是服务消费端超时的原理,或者说,是服务调用方是如何判断服务调用时间超过了设置的阈值

结论

1.首先服务调用方,会根据配置优先级,获取一个超时时间(消费端配置 > 服务提供端配置 接口层面 > 应用层面配置)
2.然后会把超时时间一直进行透传,通过invocation对象
3.然后在调用端通过netty发送了请求之后,会通过Condition.await()来等待指定的时间
3.1 如果休眠指定的时间之后,调用执行完毕,就返回
3.2 如果依旧没有拿到返回信息,就抛出timeout异常

所以:我认为,简单而已,dubbo的超时机制,就是通过condition同步条件队列来完成的

源码阅读

在发起一笔调用的时候,调用链是这样的

com.alibaba.dubbo.rpc.protocol.AbstractInvoker#invoke
com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
调用链是上面这个,就是服务消费端在调用的时候,会通过netty进行服务调用,netty客户端会在服务引入的时候,提前创建好,所以在dubboInvoker的doInvoke()方法中,会直接根据url去集合中取出来一个client,发起请求


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);// 这里是获取一个netty服务端,通过exchangeClient去发起netty请求调用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);/*** 在发起调用时,获取超时时间的源码* 1.先取方法上的配置,如果方法上未配置* 2.接着取接口上的配置* 3.如果接口上也未配置,那就取默认的配置** 总之,这里的timeout是根据优先级过滤之后,得到的超时时间*/int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);/*** 1.如果是单路的,就无需异步或者同步,在调用之后,直接return即可* 2.如果是异步调用,就通过future获取到返回结果* 3.如果是同步调用,就等待返回结果*/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<Object>(future));return new RpcResult();} else {RpcContext.getContext().setFuture(null);/*** dubbo服务调用者超时的判断,就在下面这行代码中*/return (Result) currentClient.request(inv, timeout).get();}} catch (TimeoutException e) {throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);} catch (RemotingException e) {throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);}
}

这里比较重要的是:currentClient.request()这个方法和get()方法
我们以同步调用为例

request()

这里可以看到,把超时时间放到的defaultFuture对象中,其实这里放到这个对象中,就是为了在发起调用之后,去通过condition.await()来休眠指定的时间

public ResponseFuture request(Object request, int timeout) throws RemotingException {if (closed) {throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");}// create request.Request req = new Request();req.setVersion(Version.getProtocolVersion());req.setTwoWay(true);req.setData(request);// 这里会把超时时间放到future中,然后通过channel.send()去发放真正的netty请求DefaultFuture future = new DefaultFuture(channel, req, timeout);try {channel.send(req);} catch (RemotingException e) {future.cancel();throw e;}return future;
}

放到defaultFuture对象中,一定是为了在后面使用的

get()


public Object get(int timeout) throws RemotingException {if (timeout <= 0) {timeout = Constants.DEFAULT_TIMEOUT;}/*** isDone()就是判断服务提供者是否执行完毕,通过response是否为null来判断* 如果已经执行完毕,就不会进入到下面if判断中* 如果未执行完毕*  1.加锁,reentrantLock*  2.然后双重检查,再判断服务提供者是否执行完毕*  3.如果没有执行完毕,就调用condition.await(),将当前线程放入到同步条件队列中,在指定的timeout时间到了之后,线程会继续执行*  4.再次判断是否执行完毕,如果执行完毕或者 当前时间 - start 超过了timeout,就表示要么超时了,要么执行完毕了*  5.最后会判断,如果是执行完毕了,就return即可,如果是未执行完毕,就抛出timeOutException*/if (!isDone()) {long start = System.currentTimeMillis();lock.lock();try {while (!isDone()) {/*** 如果没有执行完毕,调用condition的await方法休眠指定的时间* 然后判断是否执行完毕*/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();
}

总和以上三部分代码,我们可以发现这样一个逻辑
1.首先会判断是异步调用、还是同步调用,无论是异步还是同步,都会发起一个netty请求,如果是异步的话,会直接返回一个RpcResult对象,在后面的某一个流程中,去调用defaultFuture的get()方法
2.发起请求时,会先把超时时间放到defaultFuture对象中,因为ExchangeClient.request()方法,返回的是defaultFuture对象
3.如果是同步请求,就调用defaultFuture的get()方法阻塞,直到请求正常返回;如果超过了timeout,服务提供者依然没有返回数据,就抛出超时异常

condition

无论是异步调用,还是同步调用,都是通过future.get(),或者说是AQS中的condition来实现的
这里就是jdk的源码了,在调用condition.await()方法的时候,会把当前线程放入到一个同步条件队列中,同时,将当前线程从同步等待队列中remove
在指定的timeout时间到了之后,将线程从同步条件对列中移到同步等待队列中
进入到同步等待队列中之后,线程就会继续接着执行

服务端如果超时了会怎么处理?

举个例子:
dubbo消费者服务超时时间是5S,dubbo服务端配置的超时时间是2S,但是服务端代码的执行,实际需要3S,那这种情况下
服务消费者在调用的时候,会以5S为超时时间,而服务端还是以2S为超时时间
经过测试,发现这种场景下,服务消费端可以正常获取到返回结果,服务提供者会打印一个超时warn日志,并不会抛出异常
具体实现类是在:com.alibaba.dubbo.rpc.filter.TimeoutFilter中

/*** Log any invocation timeout, but don't stop server from running* 超时判断,只作用于服务提供者*/
@Activate(group = Constants.PROVIDER)
public class TimeoutFilter implements Filter {private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {// 1.记录开始时间long start = System.currentTimeMillis();// 2.调用服务端代码Result result = invoker.invoke(invocation);// 3.记录服务端代码执行时间long elapsed = System.currentTimeMillis() - start;// 4.如果执行时间超过了timeout配置,就打印warn日志,然后returnif (invoker.getUrl() != null&& elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(),"timeout", Integer.MAX_VALUE)) {if (logger.isWarnEnabled()) {logger.warn("invoke time out. method: " + invocation.getMethodName()+ " arguments: " + Arrays.toString(invocation.getArguments()) + " , url is "+ invoker.getUrl() + ", invoke elapsed " + elapsed + " ms.");}}return result;}}

dubbo超时机制原理相关推荐

  1. dubbo重试机制原理_Dubbo超时和重连机制

    dubbo启动时默认有重试机制和超时机制. 超时机制的规则是如果在一定的时间内,provider没有返回,则认为本次调用失败, 重试机制在出现调用失败时,会再次调用.如果在配置的调用次数内都失败,则认 ...

  2. dubbo重试机制原理_[转]dubbo重试机制和超时机制

    dubbo启动时默认有重试机制和超时机制. 超时机制的规则是如果在一定的时间内,provider没有返回,则认为本次调用失败, 重试机制在出现调用失败时,会再次调用.如果在配置的调用次数内都失败,则认 ...

  3. Dubbo超时机制导致的雪崩连接

    Bug影响:Dubbo服务提供者出现无法获取Dubbo服务处理线程异常,后端DB爆出拿不到数据库连接池,导致前端响应时间异常飙高,系统处理能力下降,核心基础服务无法提供正常服务. ​Bug发现过程: ...

  4. dubbo的超时机制和重试机制

    参考: https://www.cnblogs.com/ASPNET2008/p/7292472.html https://www.tuicool.com/articles/YfA3Ub https: ...

  5. Dubbo基本原理与超时机制

    一. dubbo基本原理 –高性能和透明化的RPC远程服务调用方案 –SOA服务治理方案 -Apache MINA 框架基于Reactor模型通信框架,基于tcp长连接 Dubbo缺省协议采用单一长连 ...

  6. dubbo超时重试和异常处理

    dubbo超时重试和异常处理 参考: https://www.cnblogs.com/ASPNET2008/p/7292472.html https://www.tuicool.com/article ...

  7. dubbo服务超时机制

    内容出自 图灵学院 我做完了作业,然后整理了整理代码,发了个博客 概念 在服务提供者和服务消费者上都可以配置服务超时时间,这两者是不一样的. 消费者调用一个服务,分为三步: 消费者发送请求(网络传输) ...

  8. Dubbo的容错机制原理

     本文是基于 apache dubbo 2.7.0 版本的源码和参考官网的集群章节而成.  为了避免单点故障,现在的应用通常至少会部署在两台服务器上.对于一些负载比较高的服务,会部署更多的服务器.这样 ...

  9. 关于Hystrix超时机制和线程状态的测试观察和个人理解

    作者:未完成交响曲,资深Java工程师!目前在某一线互联网公司任职,架构师社区合伙人! 我们在使用Hystrix时,大部分情况下都是直接基于SpringCloud的相关注解来完成请求调用的.我们有个项 ...

  10. Kafka设计解析(八)- Exactly Once语义与事务机制原理

    本文转发自技术世界,原文链接 http://www.jasongj.com/kafka/transaction/ 写在前面的话 本文所有Kafka原理性的描述除特殊说明外均基于Kafka 1.0.0版 ...

最新文章

  1. boost::gil::threshold_truncate用法的测试程序
  2. 双向特征融合的数据自适应SAR图像舰船目标检测模型
  3. MyCat分布式数据库集群架构工作笔记0003---Mycat的作用
  4. zynq文档阅读之GPIO的中断
  5. Android中Activity、Service和线程之间的通信
  6. 洛谷P3871 [TJOI2010]中位数(splay)
  7. 自动化部署工具Fabric简介
  8. 经典C语言编程100例——题目+答案代码(完结)
  9. <Java设计模式>(二)UML类图 | 设计模式概述和分类
  10. 05.Node.js和浏览器之间的差异
  11. [paper] Meta-Learner LSTM
  12. [深度学习论文笔记][Adversarial Examples] Deep Neural Networks are Easily Fooled: High Confidence Predictions
  13. CopyOnWriteArrayList
  14. 【毕业设计】基于树莓派的指纹识别打卡系统 - 单片机 物联网
  15. PDF阅读器和编辑器选择
  16. 让ADSL宽带路由器更安全
  17. flutter video_thumbnail #获取视频封面
  18. 南开计算机考研真题,2018年南开大学考研真题硕士研究生入学考试试题
  19. 2020T电梯修理实操考试视频及T电梯修理考试软件
  20. 一份2006年11月左右的alexa排名,感慨互联网的江湖变化莫测

热门文章

  1. 极客大学产品经理训练营 产品文档和原型 作业5
  2. HTTP的短连接、长连接、管道连接流水线
  3. 微型计算机中call指令,微机原理 第四章 微型计算机指令系统.ppt
  4. 高德地图轨迹方向_阿里巴巴高德地图首席科学家任小枫:高精算法推动高精地图落地...
  5. 决策树C4.5算法的不足
  6. 用傅里叶分析得到频域信息 MATLAB,信号频谱分析
  7. multimap容器查找元素的三种方法总结
  8. JAVA----简单的自旋锁
  9. windows系统下使用git出现:warning: LF will be replaced by CRLF in
  10. python 学习小结(1)