Closed Jaskey closed 4 years ago
这个问题有解决方案了吗,我们在最近使用的过程中也遇到了 客户端调用超时,服务端完全正常,偶现. 大概率不是网络问题,我们对客户端压测,停止后 服务端服务正常, 客户端调用一直超时,然后过来几个小时之后就自动恢复了.现在每天会爆几千个这样的异常,概率不大.
最好结合分布式链路追踪,或通过Arthas看服务端处理耗时占比最大在哪个阶段,或者看下服务端有没对应时间的异常日志
我们也遇到过,这种奇怪的现象曾经也怀疑过各种 IO情况,但最后定位是线程池不足,请求压根无法进入dubbo线程池,dubbo框架也没有打印异常,当然请求入参出参也没打印,客户的表现就是超时。
“最后定位是线程池不足”,这个是否是在服务端的日志中发现了Thread pool is EXHAUSTED! 故障呢?
“Thread pool is EXHAUSTED!”日志我们遇到大部分都是出现在提供者端,消费者端也遇到过一两次。当dubbo工作者的线程池满了时,有定时检测任务会默认每隔十分钟输出JStack.log文件
我想问一下有没有人解决?我们生产也是偶尔的超时,链路监控没有上,希望有碰到过然后解决的出来解答一下。我们线上dubbo线程池满的报错会有,不是因为线程池满报的这个超时
这个需要具体问题具体分析,是否有当时对应的异常日志或通过Arthas追踪 下面是我在前东家负责dubbo时排查的dubbo问题整理,抛砖引玉 https://github.com/edwardlee03/issue-case/tree/master/dubbo
我想问一下有没有人解决?我们生产也是偶尔的超时,链路监控没有上,希望有碰到过然后解决的出来解答一下。我们线上dubbo线程池满的报错会有,不是因为线程池满报的这个超时
个别应用,还是所有应用都存在这个问题?若个别应用的话,看看当时有没异常日志,能否找些一些破案的线索
线上的问题已经定位到是因为5分钟执行一次的定时任务操作大对象,导致过一段时间那个服务节点要执行GC。这个时候请求过来只能等GC结束导致dubbo请求超时。
赞👍。大对象应该触发Full GC引起应用停顿(STW),这个case是提供者端应用超时了。通过时间线结合系统监控指标综合怀疑分析
除了网络问题,还可能有其他原因么,我司也出现了这种短时抖动,服务端完全是没有延迟的报错
可能的原因: 1.请求阻塞在工作者队列里排队,消耗了大部分时间; 2.API.jar升级引起类不兼容加载不到,频繁加载某一个类会引起阻塞,可以看下ClassLoader源码。dubbo新版本已修复这个问题。 3.较长时间的GC停顿
"请求阻塞在工作者队列里排队,消耗了大部分时间 " 这个通过什么来证明呢? 我们服务也遇到这问题,我猜测就是排队了 我想扩容你能解决 但是没有找到证据
所以,这个问题最终没有答案,还被close掉了?
总结一下,如果出现消费端超时,但是服务端处理时间短,那么有这么几种可能: 1)网络问题:tcpddump/wireshark抓包去判断。 2)dubbo线程池满导致排队:监控两头的线程池状态,以及全链路信息,链路可以拼出来。 3)JVM停顿问题:包括GC的停顿和其他safepoint停顿,特别是后面这个STW停顿,很多人不知道,建议都打印出来。 4)统计不准:服务端有时候我们觉得很快,实际上也慢,只是日志或者其他监控信息不全。
总结一下,如果出现消费端超时,但是服务端处理时间短,那么有这么几种可能: 1)网络问题:tcpddump/wireshark抓包去判断。 2)dubbo线程池满导致排队:监控两头的线程池状态,以及全链路信息,链路可以拼出来。 3)JVM停顿问题:包括GC的停顿和其他safepoint停顿,特别是后面这个STW停顿,很多人不知道,建议都打印出来。 4)统计不准:服务端有时候我们觉得很快,实际上也慢,只是日志或者其他监控信息不全。
第一点是很玄学的地方,因为其他服务同样的网络通常没问题, 第二点应该大部分都不存在,因为dubbo默认线程池默认是没有队列的,核数一满就直接爆了。
碰到一样的问题了,请问下后来解决了吗
也遇到类似情况,哪位大神给出最终的解决方案,我们大多超时存在: client elapsed: 310 ms
也遇到类似情况,哪位大神给出最终的解决方案,我们大多超时存在: client elapsed: 310 ms
我们用的dubbo接口异步调用,改为同步调用后,无超时现象
总结一下,如果出现消费端超时,但是服务端处理时间短,那么有这么几种可能: 1)网络问题:tcpddump/wireshark抓包去判断。 2)dubbo线程池满导致排队:监控两头的线程池状态,以及全链路信息,链路可以拼出来。 3)JVM停顿问题:包括GC的停顿和其他safepoint停顿,特别是后面这个STW停顿,很多人不知道,建议都打印出来。 4)统计不准:服务端有时候我们觉得很快,实际上也慢,只是日志或者其他监控信息不全。
目前来看,在虚拟机或容器,问题3的因素非常大。 比如GC就是一类safepoint。 JVM内部存在大量的safepoint,都会造成JVM的STW暂停。 绝大部分时间,这个暂停都很快,但是就是有少量的几种情况下,会很慢。 此时如果不调整GClog配置,我们从各类监控和日志,是根本发现不了这个事儿的。
请教,这个问题有体系的排查思路或解决方案吗
请检查gc或safepoint暂停。
Bigface Monster @.***> 于2023年2月10日周五 16:59写道:
请教,这个问题有体系的排查思路或解决方案吗
— Reply to this email directly, view it on GitHub https://github.com/apache/dubbo/issues/1784#issuecomment-1425444758, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAGFEVBTBUGO4YRPTUPWKELWWX7NXANCNFSM4E7NTLBA . You are receiving this because you were mentioned.Message ID: @.***>
所以,这个问题最终没有答案,还被close掉了?
我最终解决了我们遇到的问题,下面分享下我们遇到的现象和解决过程
现象:每次服务发布后dubbo provider 会在发布后的十几分钟内不再响应,consumer端异常显示provider端的dubbo线程池被打满,类似这样的错误信息
Thread pool is EXHAUSTED! ...
但我们监控平台在服务provider侧监控不到任何有用的信息,GC等其他一切正常、dubbo服务方法rt也最多几百ms、连错误信息都没有,但consumer端监控到了错误信息和一些超时信息。通过对provider端线程池的监控发现DubboServerHandler线程池的数量从发布后到无响应线程数一直增加,直到满为止,时间吻合。
于是,我们判定每次发布后无响应,客户端同时也会有一些报超时的错误 的现象,是由于provider端的线程池被打满导致的。
排查:通过配置增大provider端线程池,无用。从AbortPolicyWithReport类(DubboServerHandler线程池的拒绝策略实现)中发现,线程池满后dubbo会调用一个方法
dumpJStack();
查看该方法内部发现确实dump了堆栈信息,于是我们从服务器上找到了该日志文件,下载后通过jstack分析,发现线程池打满时刻,DubboServerHandler线程池中的所有线程均RUNNING状态,表明未发生死锁,排查发现有非常多的同一个接口调用,于是问题明了了: 该接口对应的底层数据库查询数据量太大,查询很慢,当发布的时候用户着急重复刷新页面提交该查询请求,导致不断积累又不返回,线程池很快被占满。
最后的疑问: 为何发布后发布的机器会被打满而未发布的其他机器却没事?按理说负载均衡后请求路由会均分到所有可用的机器上,但我们遇到的现象是被发布的机器偶发这种现象,集群里未被发布的机器却没事。
为何发布后发布的机器会被打满而未发布的其他机器却没事?按理说负载均衡后请求路由会均分到所有可用的机器上,但我们遇到的现象是被发布的机器偶发这种现象,集群里未被发布的机器却没事。
这个需要检查你的业务逻辑,通常是有缓存需要重新加载或者是 JVM C2 编译
我最近也遇到这个问题 通过arms监控服务端接口最大耗时也就300多ms 客户端配置的是3s超时 有以下两种情况: 1.开启arms监控客户端就会不间断的报超时,所以平时不开监控 2.不开arms监控的情况下 出现的概率比较低 也会偶尔出现一下
除了网络问题,还可能有其他原因么,我司也出现了这种短时抖动,服务端完全是没有延迟的报错
可能的原因: 1.请求阻塞在工作者队列里排队,消耗了大部分时间; 2.API.jar升级引起类不兼容加载不到,频繁加载某一个类会引起阻塞,可以看下ClassLoader源码。dubbo新版本已修复这个问题。 3.较长时间的GC停顿
"请求阻塞在工作者队列里排队,消耗了大部分时间 " 这个通过什么来证明呢? 我们服务也遇到这问题,我猜测就是排队了 我想扩容你能解决 但是没有找到证据
对线程池进行监控,透出线程池的状态指标(线程各状态的数据,队列的数据) @shuohao
补充一个问题点,最近发现整体响应耗时耗费在序列化上,利用arthas监控可以发现:
trace com.alibaba.com.caucho.hessian.io.Deserializer readObject -n 5 --skipJDKMethod false
这里耗时很久,然后整体升级dubbo到2.6.2解决
我这边大概率是和GC相关,场景也是容器启动后不久访问出现这个问题
补充一个问题点,最近发现整体响应耗时耗费在序列化上,利用arthas监控可以发现:
trace com.alibaba.com.caucho.hessian.io.Deserializer readObject -n 5 --skipJDKMethod false
这里耗时很久,然后整体升级dubbo到2.6.2解决
响应数据大小是多少?有循环引用么?
补充一个问题点,最近发现整体响应耗时耗费在序列化上,利用arthas监控可以发现:
trace com.alibaba.com.caucho.hessian.io.Deserializer readObject -n 5 --skipJDKMethod false
这里耗时很久,然后整体升级dubbo到2.6.2解决
响应数据大小是多少?有循环引用么?
没有,就是客户端是 2.5.X 然后提供者是2.6.X 2边版本不一致导致的
我这边也遇到类似的问题,使用的版本 2.7.12(consumer和provider都是此版本)。 问题表现为 provider 侧的日志和指标显示请求处理都正常,但是consumer侧报请求超时,触发重试。
报错的堆栈关键词如下。
org.apache.dubbo.rpc.RpcException: Invoke remote method timeout. method: XXXXX, provider: dubbo://打码打码打码:20880/XXXXXX
....
at org.apache.dubbo.rpc.protocol.AsyncToSyncInvoker.invoke(AsyncToSyncInvoker.java:70)
at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78)
at com.alibaba.dubbo.rpc.Invoker$CompatibleInvoker.invoke(Invoker.java:55)
....
经过排查,发现是版本 [2.7.7, 2.7.15] 区间的 bug 造成的。 这些 dubbo 版本会在 Provider 侧的 IO 线程中使用到 Hessian2ObjectOutput,Hessian2ObjectOutput 对象会包含一个 Hessian2Output 对象并且使用 ThreadLocal 进行缓存,Hessian2Output 又会维护一个 IdentifyIntMap,每次复用到 Hessian2Output 对象时,会对这个 IdentifyIntMap 进行遍历操作。 见:https://github.com/apache/dubbo-hessian-lite/blob/master/hessian-lite/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java 的 init 方法,最终会调用到 IdentifyIntMap 的 clear() 方法。 记住这个 for 循环。
这个 IdentifyIntMap 是 Hessian2 序列化时辅助计算(大概是此作用)的,当序列化的内容较大(尤其是嵌套比较深的时候),这个 IdentifyIntMap 会进行扩容,扩容规则是 当前总容量的 1/4,小于等于使用量时,扩容为当前总容量的 4倍 。 见:https://github.com/apache/dubbo-hessian-lite/blob/master/hessian-lite/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java put方法
map的容量会不断撑大,每次使用又会遍历它重置,它又是运行在 netty 的 io 线程上,所以一旦map的容量超过某个阈值时,io线程就会拥塞,如下所示。 注意:程序里运行的Hession2类库版本和上述截图不一致,所以行数有偏差,但是逻辑是一样的,都是卡在for循环遍历。
heap dump 结果,发现这个 map 的元素已经被撑大到 400w+ 。
等于是出现故障时 IO线程 已经水泄不通了。 排查同期的 provider 侧 内存占用,开始故障后老年代持续增长,说明响应淤积住了。
问题解决:provider侧升级到 2.7.16 即以上版本就能修复这个问题,此版本开始移除了对 Hessian2ObjectOutput 对象的线程级缓存。
遗留问题:为什么 provider 侧的指标都正常? 根据官方的线程模型示意图:https://cn.dubbo.apache.org/en/docs3-v2/java-sdk/advanced-features-and-usage/performance/threading-model/provider/
dubbo 线程处理好业务之后,是将 response 对象丢给 IO线程处理,IO线程处理 serialize 过程(即序列化,本次的故障点),业务逻辑处理和 IO输出异步化了。所以我们基于 dubbo 线程的 filter 的指标采集,以及在 dubbo 线程中所做的日志打印,完全感知不到异常。
我这边也遇到类似的问题,使用的版本 2.7.12(consumer和provider都是此版本)。 问题表现为 provider 侧的日志和指标显示请求处理都正常,但是consumer侧报请求超时,触发重试。
报错的堆栈关键词如下。
org.apache.dubbo.rpc.RpcException: Invoke remote method timeout. method: XXXXX, provider: dubbo://打码打码打码:20880/XXXXXX .... at org.apache.dubbo.rpc.protocol.AsyncToSyncInvoker.invoke(AsyncToSyncInvoker.java:70) at org.apache.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:78) at com.alibaba.dubbo.rpc.Invoker$CompatibleInvoker.invoke(Invoker.java:55) ....
经过排查,发现是版本 [2.7.7, 2.7.15] 区间的 bug 造成的。 这些 dubbo 版本会在 Provider 侧的 IO 线程中使用到 Hessian2ObjectOutput,Hessian2ObjectOutput 对象会包含一个 Hessian2Output 对象并且使用 ThreadLocal 进行缓存,Hessian2Output 又会维护一个 IdentifyIntMap,每次复用到 Hessian2Output 对象时,会对这个 IdentifyIntMap 进行遍历操作。 见:https://github.com/apache/dubbo-hessian-lite/blob/master/hessian-lite/src/main/java/com/alibaba/com/caucho/hessian/io/Hessian2Output.java 的 init 方法,最终会调用到 IdentifyIntMap 的 clear() 方法。 记住这个 for 循环。
这个 IdentifyIntMap 是 Hessian2 序列化时辅助计算(大概是此作用)的,当序列化的内容较大(尤其是嵌套比较深的时候),这个 IdentifyIntMap 会进行扩容,扩容规则是 当前总容量的 1/4,小于等于使用量时,扩容为当前总容量的 4倍 。 见:https://github.com/apache/dubbo-hessian-lite/blob/master/hessian-lite/src/main/java/com/alibaba/com/caucho/hessian/util/IdentityIntMap.java put方法
map的容量会不断撑大,每次使用又会遍历它重置,它又是运行在 netty 的 io 线程上,所以一旦map的容量超过某个阈值时,io线程就会拥塞,如下所示。 注意:程序里运行的Hession2类库版本和上述截图不一致,所以行数有偏差,但是逻辑是一样的,都是卡在for循环遍历。
heap dump 结果,发现这个 map 的元素已经被撑大到 400w+ 。
等于是出现故障时 IO线程 已经水泄不通了。 排查同期的 provider 侧 内存占用,开始故障后老年代持续增长,说明响应淤积住了。
问题解决:provider侧升级到 2.7.16 即以上版本就能修复这个问题,此版本开始移除了对 Hessian2ObjectOutput 对象的线程级缓存。
5889 引入 #10231 修复
遗留问题:为什么 provider 侧的指标都正常? 根据官方的线程模型示意图:https://cn.dubbo.apache.org/en/docs3-v2/java-sdk/advanced-features-and-usage/performance/threading-model/provider/
dubbo 线程处理好业务之后,是将 response 对象丢给 IO线程处理,IO线程处理 serialize 过程(即序列化,本次的故障点),业务逻辑处理和 IO输出异步化了。所以我们基于 dubbo 线程的 filter 的指标采集,以及在 dubbo 线程中所做的日志打印,完全感知不到异常。
感谢大佬的这篇回复,搜关键字进来发现是一模一样的问题,省了N个小时的排查,分析的也相当完整,一般真的不太会想到是io线程的一些动作引起的
感谢大佬的这篇回复,搜关键字进来发现是一模一样的问题,省了N个小时的排查,分析的也相当完整,一般真的不太会想到是io线程的一些动作引起的
我们已经使用dubbo-3.2.16
,在尝试dubbo-3.3.1
的虚拟线程,包括JDK-21的ZGC。😄
A调用服务B,超时时间1秒,从某些请求来看超时了两次,重试了两次,第三次成功了,耗时仅4ms。
客户端有类似日志:
有以下两个奇怪的地方:
以下是我的分析:
猜测和dubbo 服务端进入业务线程前的排队有关,但是我们没有设置queue的长度,所以默认应该是不排队的,线程池耗尽应该抛出的异常不是timeout。
即使排队了,但是服务端没有找到此上下文的超时日志,感觉像完全没有收到此请求?
会不会是直接网络上丢了包?如果网络上传输就失败了,异常会是超时么?此请求是一个对象,转换为JSON后有几K。
求解答