首页 > 其他分享 >1024个线程居然不够用?RPC 线程池被打满!

1024个线程居然不够用?RPC 线程池被打满!

时间:2024-09-21 09:51:23浏览次数:3  
标签:1024 调用 请求 接口 耗时 RPC 线程

打开公司群,就看见群里有人讨论:线上环境出现大量RPC请求报错,异常原因:被线程池拒绝。虽然异常量很大,但是异常服务非核心服务,属于系统旁路,服务于数据核对任务,即使有大量异常,也没有实际的影响。

原来有人在线上刷数据,产生了大量 binlog,数据核对任务的请求量大幅上涨,导致线程池被打满。

第二天一到公司,我就迫不及待地打开各种监控大盘,开始排查问题,最后还真叫我揪出问题根源了。

2. 排查过程

2.1 请求量的波动情况
  • 单机 RPC的 QPS从 300/s 涨到了 450/s。
  • Kafka 消息 QPS 50/s 无 明显波动。
  • 无其他请求入口和 无定时任务。

这也能叫请求量大幅上涨,请求量增加 150/s 能打爆线程池?就这么糊弄老板…… ,由此我坚定了判断:故障另有根因

2.2 RPC 线程池配置和监控

线上的端口并没有全部被打爆,仅有 1 个 RPC 端口 8001 被打爆。所以我特地查看了8001 的线程池配置。

  • 初始线程数 10
  • 最大线程数 1024(数量过大,配置的有点随意了)
  • 队列长度 0
  • 拒绝策略是抛出异常立即拒绝。
  • 在 20:11到 20:13 分,线程从初始线程数10,直线涨到了1024 。
2.3 思考

QPS 450次/秒 需要 1024 个线程处理吗?按照我的经验来看,只要接口的耗时在 100ms 以内,不可能需要如此多的线程,太蹊跷了。

2.4 接口耗时波动情况

接口 平均耗时从 5.7 ms,增加到 17000毫秒。

接口耗时大幅增加。后来和他们沟通,他们当时也看了接口耗时监控。他们认为之所以平均耗时这么高,是因为RPC 请求在排队,增加了处理耗时,所以监控平均耗时大幅增长。

这是他们的误区,错误的地方有两个。

  • 此RPC接口线程池的队列长度为 0,拒绝策略是抛出异常。当没有可用线程,请求会即被拒绝,请求不会排队,所以无排队等待时间。
  • 公司的监控系统分服务端监控和调用端监控,服务端的耗时监控不包含 处理连接的时间,不包含 RPC线程池排队的时间。仅仅是 RPC 线程池实际处理请求的耗时。RPC 调用端的监控包含 RPC 网络耗时、连接耗时、排队耗时、处理业务逻辑耗时、服务端GC 耗时等等。

他们误认为耗时大幅增加是因为请求在排队,因此忽略了至关重要的这条线索:接口实际处理阶段的性能严重恶化,吞吐量大幅降低,所以线程池大幅增长,直至被打满。

接下来我开始分析,接口性能恶化的根本原因是什么?

  • CPU 被打满?导致请求接口性能恶化?
  • 频繁GC ,导致接口性能差?
  • 调用下游 RPC 接口耗时大幅增加 ?
  • 调用 SQL,耗时大幅增加?
  • 调用 Redis,耗时大幅增加
  • 其他外部调用耗时大幅增加?
2.5 其他耗时监控情况

我快速的排查了所有可能的外部调用耗时均没有明显波动。也查看了机器的负载情况,cpu和网络负载 均不高,显然故障的根源不在以上方向。

  • CPU 负载极低。在故障期间,cpu.busy 负载在 15%,还不到午高峰,显然根源不是CPU 负载高。
  • gc 情况良好。无 FullGC,youngGC 1 分钟 2 次(younggc 频繁,会导致 cpu 负载高,会使接口性能恶化)
  • 下游 RPC 接口耗时无明显波动。我查看了服务调用 RPC 接口的耗时监控,所有的接口耗时无明显波动。
  • SQL 调用耗时无明显波动。
  • 调用 Redis 耗时无明显波动。
  • 其他下游系统调用无明显波动。(如 Tair、ES 等)
2.6 开始研究代码

为什么我一开始不看代码,因为这块内容不是我负责的内容,我不熟悉代码。

直至打开代码看了一眼,恶心死我了。代码非常复杂,分支非常多,嵌套层次非常深,方法又臭又长,堪称代码屎山的珠穆朗玛峰,多看一眼就能吐。接口的内部分支将近 10 个,每个分支方法都是一大坨代码。

这个接口是上游 BCP 核对系统定义的 SPI接口,属于聚合接口,并非单一职责的接口。看了 10 分钟以后,还是找不到问题根源。因此我换了问题排查方向,我开始排查异常 Trace。

2.7 从异常 Trace 发现了关键线索

我所在公司的基建能力还是很强大的。系统的异常 Trace 中标注了各个阶段的处理耗时,包括所有外部接口的耗时。如SQL、 RPC、 Redis等。

我发现确实是内部代码处理的问题,因为 trace 显示,在两个 SQL 请求中间,系统停顿长达 1 秒多。不知道系统在这 1 秒执行哪些内容。我查看了这两个接口的耗时,监控显示:SQL 执行很快,应该不是SQL 的问题

机器也没有发生 FullGC,到底是什么原因呢?

前面提到,故障接口是一个聚合接口,我不清楚具体哪个分支出现了问题,但是异常 Trace 中指明了具体的分支。

我开始排查具体的分支方法……, 然而捏着鼻子扒拉了半天,也没有找到原因……

2.8 山穷水复疑无路,柳暗花明又一村

这一坨屎山代码看得我实在恶心,我静静地冥想了 1 分钟才缓过劲。

  • 没有外部调用的情况下,阻塞线程的可能性有哪些?
  • 有没有加锁? Synchiozed 关键字?

于是我按着关键字搜索Synchiozed关键词,一无所获,代码中基本没有加锁的地方。

马上中午了,肚子很饿,就当我要放弃的时候。随手扒拉了一下,在类的属性声明里,看到了 Guava限流器。

激动的心,颤抖的手

private static final RateLimiter RATE_LIMITER = RateLimiter.create(10, 20, TimeUnit.SECONDS);

限流器:1 分钟 10次调用。

于是立即查看限流器的使用场景,和异常 Trace 阻塞的地方完全一致。

嘴角出现一丝很容易察觉到的微笑。

1024个线程居然不够用?RPC 线程池被打满!_SQL

破案了,真相永远只有一个。

3. 问题结论

Guava 限流器的阈值过低,每秒最大请求量只有10次。

当并发量超过这个阈值时,大量线程被阻塞,RPC线程池不断增加新线程来处理新的请求,直到达到最大线程数。线程池达到最大容量后,无法再接收新的请求,导致大量的后续请求被线程池拒绝。

于是我开始建群、摇人。把相关的同学,还有老板们,拉进了群里。把相关截图和结论发到了群里。

由于不是紧急问题,所以我开开心心的去吃午饭了。后面的事就是他们优化代码了。


最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!


标签:1024,调用,请求,接口,耗时,RPC,线程
From: https://blog.51cto.com/u_16502039/12073340

相关文章

  • [操作系统]线程在 Linux 中的实现
    线程在Linux中的实现线程机制是现代编程技术中常用的一种抽象概念。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。线程机制支持并发程序设计技术(concurrentprogramming),在多处理器系统上,它也能保证真正的并行处理(para......
  • Nodejs 揭秘:单线程魔法背后的真相以及它如何为高性能应用程序提供动力
    Node.js有时被称为“单线程”,这个词对于习惯了Java或.NET等多线程环境的开发人员来说可能会令人困惑,甚至令人畏惧。然而,Node.js如何处理作业的真相远比这个简单术语所暗示的复杂和强大。在这篇博客中,我们将了解Node.js的架构、单线程意味着什么,以及Node.js如何通过其独......
  • JavaScript 如何在后台工作:了解其单线程性质和异步操作
    javascript是网络的支柱,为数十亿网站和应用程序提供动态客户端功能。但您有没有想过javascript是如何在后台发挥其魔力的?在这篇文章中,我们将深入研究javascript单线程本质的内部工作原理,并探索异步编程的概念。单线程是什么意思?当我们说javascript是“单线程”时,这意......
  • C++ 多线程知识汇总
    https://zhuanlan.zhihu.com/p/194198073 (防链接失效)程序使用并发的原因有两种:为了关注点分离(程序中不同的功能,使用不同的线程去执行),当为了分离关注点而使用多线程时,设计线程的数量的依据,不再是依赖于CPU中的可用内核的数量,而是依据概念上的设计(依据功能的划分);为了提高性能......
  • 亿级数据表多线程update锁表问题
    目录1、问题描述2、原因分析3、问题解决1、问题描述在pg数据库,某个业务,有一张数据表test,数据表结果如下:test(sjjbh,wlbid,gzmb,sfzg,zgsj,cjsj,xx...),这个表没有主键,会有很多重复数据。test表需要根据另外表(是多张表),动态更新sfzg字段, 加入另外表结构如下sjj_ckb1(......
  • java线程池闭坑指南
    专业在线打字练习网站-巧手打字通,只输出有价值的知识。一前言线程池作为初学者常感困惑的一个领域,本次“巧手打字通课堂”将深入剖析其中几个最为普遍的误区。为了更清晰地阐述这些知识点,让我们以一个具体定义的线程池为例来展开说明。如下:ThreadPoolExecutorexecutor=ne......
  • C# 报错:System.Threading.ThreadStateException:”当前线程不在单线程单元中,因此无法
    原因分析System.Threading.ThreadStateException 错误通常发生在尝试在非UI线程中创建或访问 ActiveX 控件(如COM 组件)时。在 Windows Forms应用程序中,所有 UI操作必须在创建该UI 的线程(通常是主线程)上执行。解决方案要解决这个问题,你需要确保在 UI 线程上创建......
  • 多线程搜索文件拷贝-Python脚本
    单线程的文件拷贝太折磨人了,所以这里使用多线程的方式去拉满软件效率importosimportshutilimportthreadingimportqueueclassFileItem:"""自定义文件对象类,用于存储文件路径和命名序号"""def__init__(self,path,index):self.path=path......
  • Java线程内容
    一:线程的创建继承Thread类重写run方法publicclassMyTest{publicstaticvoidmain(String[]args){MyJobmj=newMyJob();mj.start();}}classMyJobextendsThread{@Overridepublicvoidrun(){for(inti=0;i......
  • 线程三:线程安全问题及其解决方法
    一:引入代码演示线程安全问题:200000次自增多线程运算publicclassThreadDemo12{privatestaticintcount=0;publicstaticvoidmain(String[]args)throwsInterruptedException{Threadt1=newThread(()->{for(inti=0;i<......