一、简述

同步调用是一种阻塞式的调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止。dubbo默认的协议是netty, Netty 是 NIO 异步通讯机制,那么服务调用是怎么转化为同步的呢?Dubbo是阿里开源的RPC框架,因为基于接口开发支持负载均衡、集群容错、版本控制等特性,因此现在有很多互联网公司都在使用Dubbo。
1️⃣Dubbo有三个级别的超时设置分别为:
①针对方法设置超时时间
②在服务方设置超时时间
③在调用方设置超时时间

2️⃣一般超时是调用端发生在请求发出后,无法在指定的时间内获得对应的响应。原因大概有以下几种情况:
①服务端确实处理比较慢,无法在指定的时间返回结果,调用端就自动返回一个超时的异常响应来结束此次调用。
②服务端如果响应的比较快,但当客户端 Load 很高,负载压力很大的时候,会因为客户端请求发不出去、响应卡在 TCP Buffer 等问题,造成超时。因为客户端接收到服务端发来的数据或者请求服务端的数据,都会在系统层面排队,如果系统负载比较高,在内核态的时间占比就会加长,从而造成客户端获取到值时已经超时。
③通常是业务处理太慢,可在服务提供方机器上执行:jstack [PID] > jstack.log 分析线程都卡在哪个方法调用上,这里就是慢的原因。如果不能调优性能,请调高 timeout 阈值。

3️⃣排查和解决步骤
①两边可能有 GC,检查服务端和客户端 GC 日志,耗时很长的 GC,会导致超时。超时的发生很可能意味着调用端或者服务端的资源(CPU、内存或者网络)出现了瓶颈,需要检查服务端的问题还是调用端的问题来排除GC抖动等嫌疑。
②检查服务端的网络质量,比如重传率来排除网络嫌疑。
③借助链路跟踪的分析服务(比如阿里的 ARMS,开源的 OpenTracing 系的实现 Zipkin、SkyWalking 等)来分析下各个点的耗时情况。

4️⃣Dubbo调用超时(client-side timeout)后会有两种情况:
①客户端会收到一个TimeoutException异常
②服务端会收到一个警告The timeout response finally returned at xxx
看起来还蛮正常的,但是实际上会有这样问题:调用超时后服务端还是会继续执行,该如何处理呢?

@Service(version = "1.0")
@Slf4j
public class DubboDemoServiceImpl implements DubboDemoService {public String sayHi(String name) {try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}String result = "hi: " + name;log.info("Result: {}" , result);return result;}
}

服务非常简单,三秒后返回字符串。controller层:

@RestController
@RequestMapping
public class DubboDemoController {@Reference(url = "dubbo://127.0.0.1:22888?timeout=2000", version = "1.0")private DubboDemoService demoService;@GetMappingpublic ResponseEntity<String> sayHi(@RequestParam("name") String name){return ResponseEntity.ok(demoService.sayHi(name));}
}

连接DubboDemoService服务使用的直连方式dubbo://127.0.0.1:22888?timeout=2000,演示中的超时时间都由url中的timeout指定。

二、Consumer超时处理

前面服务端的sayHi()实现休眠3秒,而连接服务时指定的超时时间是2000ms,那肯定会收到一个TimeoutException异常:

There was an unexpected error (type=Internal Server Error, status=500).
Invoke remote method timeout. method: sayHi

客户端超时处理比较简单,既然发生了异常也能捕获到异常那是该回滚还是不做处理,完全可以由开发者解决。

try{return ResponseEntity.ok(demoService.sayHi(name));
}catch (RpcException te){//do something...log.error("consumer", te);return msg;
}

重点还是解决服务方的超时异常。

三、Provider超时处理

Provider的处理没有客户端那样简单,因为Provider不会收到异常,而且线程也不会中断,这样就会导致Consumer超时数据回滚,而Provider继续执行最终执行完数据插入成功,数据不一致。

上面Provider方法休眠3000ms且Consumer的超时是参数是2000ms。调用发生2000ms后就会发生超时,而Provider的sayHi()不会中断在1000ms后打印hi xx。

很明显要保持数据一致就需要在超时后,将Provider的执行终止或回滚才行,如何做到数据一致性呢?

1️⃣重试机制
Dubbo自身有重试机制,调用超时后会发起重试,Provider端需考虑幂等性。

2️⃣最终一致性
使用补偿事务或异步MQ保持最终一致性,需要写一些与业务无关的代码来保持数据最终一致性。比如在Provider端加个check方法,检查是否成功,具体实现还需要结合自身的业务需求来处理。

@GetMapping
public ResponseEntity<String> sayHi(String name){try{return ResponseEntity.ok(demoService.sayHi(name));}catch (RpcException te){//do something...try{demoService.check(name);}catch (RpcException ignore){}log.error("consumer", te);return msg;}
}

虽然可以通过添加检查来验证业务状态,但是这个调用执行时间是没办法准确预知的,所以这样简单的检测是效果不大,最好还是通过MQ来做这样的检测。

3️⃣基于时间回滚
原理比较简单,在Consumer端调用时设置两个参数ctime、ttime分别表示调用时间、超时时间,将参数打包发给Provider。Provider收到两个参数后进行操作,如果执行时间越过ttime则回滚数据,否则正常执行。改造下代码:

public ResponseEntity<String> sayHi(@RequestParam("name") String name){try{RpcContext context = RpcContext.getContext();context.setAttachment("ctime", System.currentTimeMillis() + "");context.setAttachment("ttime", 2000 + "");return ResponseEntity.ok(demoService.sayHi(name));}catch (RpcException te){//do something...log.error("consumer", te);return msg;}}

将ctime、ttime两个参数传到Provider端处理:

public String sayHi(String name) {long curTime = System.currentTimeMillis();String ctime = RpcContext.getContext().getAttachment("ctime");String ttime = RpcContext.getContext().getAttachment("ttime");long ctimeAsLong = Long.parseLong(ctime);long ttimeAsLong = Long.parseLong(ttime);try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}long spent = System.currentTimeMillis() - curTime;if(spent >= (ttimeAsLong - ctimeAsLong - curTime)){throw new RpcException("Server-side timeout.");}String result = "hi: " + name;log.info("Result: {}" , result);return result;}

画个图看一下执行的时间线:

从上图在执行完成后,响应返回期间这段时间是计算不出来的,所以这种办法也不能完全解决Provider超时问题。

四、dubbo中配置的优先级

dubbo作为一个服务治理框架,功能相对比较完善,性能也挺不错。要知道dubbo中配置是有优先级的,以免出现调优参数设置了却没发现效果实际是配置被覆盖导致这样的问题。dubbo分为consumer和provider端,在配置各个参数时,其优先级如下:

1、consumer的method配置
2、provider的method配置
3、consumer的reference配置
4、provider的service配置
5、consumer的consumer节点配置
6、provider的provider节点配置

可以看到,方法级的配置优先级高于接口级,consumer的优先级高于provider。同时,在本地参数配置还存在一层优先级:

1、系统参数(-D),如-Ddubbo.protocol.port=20003
2、xml配置
3、property文件

了解了这两个优先级,调优起来才会更加清晰,省去了一些诸如配置设置了不生效这样的麻烦。注意,其实dubbo中还可以通过将配置写入注册中心的方式覆盖用户配置(优先级高于系统参数)。

作者:日常更新
链接:https://www.jianshu.com/p/75f7fbe4944f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

如何处理Dubbo调用超时相关推荐

  1. dubbo调用超时回滚_如何处理Dubbo调用超时?

    原创:Java派(微信公众号:Java派),欢迎分享,转载请保留出处. 前言 Dubbo是阿里开源的RPC框架,因为他基于接口开发支持负载均衡.集群容错.版本控制等特性,因此现在有很多互联网公司都在使 ...

  2. dubbo调用超时回滚_面试必问之Dubbo面试题

    Dubbo 支持哪些协议,每种协议的应用场景,优缺点?  dubbo: 单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用, 以及消费者远大于提供者.传输协议 TCP,异步,Hessian ...

  3. dubbo调用超时回滚_微服务痛点基于Dubbo + Seata的分布式事务(AT模式)

    前言 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata 将为用户提供了 AT.TCC.SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案. ...

  4. kafka实现异步发送_Kafka 异步消息也会阻塞?记一次 Dubbo 频繁超时排查过程

    线上某服务 A 调用服务 B 接口完成一次交易,一次晚上的生产变更之后,系统监控发现服务 B 接口频繁超时,后续甚至返回线程池耗尽错误 Thread pool is EXHAUSTED.因为服务 B ...

  5. Dubbo retries 超时重试机制的问题

    异常日志 [com.alibaba.dubbo.rpc.filter.TimeoutFilter] - [DUBBO] invoke time out. method: sendMessageargu ...

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

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

  7. dubbo服务超时机制

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

  8. Hystrix面试 - 基于 timeout 机制为服务接口调用超时提供安全保护

    Hystrix面试 - 基于 timeout 机制为服务接口调用超时提供安全保护 一般来说,在调用依赖服务的接口的时候,比较常见的一个问题就是超时.超时是在一个复杂的分布式系统中,导致系统不稳定,或者 ...

  9. dubbo调用服务出现如下异常

    dubbo调用服务出现如下异常 参考文章: (1)dubbo调用服务出现如下异常 (2)https://www.cnblogs.com/justinzhang/p/4933524.html 备忘一下.

最新文章

  1. 清华大学大数据研究中心给您拜年啦!
  2. JDBC连接数据库格式
  3. [Java] webservice soap,wsdl 例子
  4. python下标从0开始_从零学Python之入门(三)序列
  5. matlab 并行 计时,用Zen2跑MATLAB R2020a并行计算负载有点奇怪 - 桌面电脑(Computer)版 - 北大未名BBS...
  6. struts2的优缺点
  7. python 获取进程id_使用python 获取进程pid号的方法
  8. discuzcode函数
  9. Linux的WIFI架构,Linux Wireless架构总结
  10. WinXP系统连接网络教程
  11. 【Java架构师入门到精通】java分布式架构有哪些技术
  12. modbus软件开发linux,Linux C开发之用modbus实现串口通讯
  13. 台达变频器s1参数设置_台达变频器的参数设定步骤
  14. 牛腩新闻系统--.NET使用一般处理程序生成验证码
  15. Windows软件打包工具的使用
  16. windows7自带摄像头拍照摄像软件
  17. android sharedpreferences 存储对象,android中SharedPreferences实现存储用户名功能
  18. Python复数属性和方法操作实例
  19. java 串口 dtr rts_【整理】串口(RS232/RS485等)通讯中RTS/CTS,DTR/DSR的含义详解
  20. Swift中的_(下划线)是什么意思

热门文章

  1. C语言二级指针内存模型建立
  2. c++派生类的访问控制
  3. 01_MUI之Boilerplate中:HTML5示例,动态组件,自定义字体示例,自定义字体示例,图标字体示例
  4. WdatePicker()时间控制方式(转载+原创)
  5. 04springMVC结构,mvc模式,spring-mvc流程,spring-mvc的第一个例子,三种handlerMapping,几种控制器,springmvc基于注解的开发,文件上传,拦截器,s
  6. 1.物理系统PhysicsWorld,RayCast
  7. 1.进程间的关系:终端,网络设备
  8. 搭建和测试Android JAVA NDK
  9. Android-service
  10. 【tensorflow】Sequential 模型方法