首页 > 其他分享 >一下午连续故障两次,谁把我们接口堵死了?!

一下午连续故障两次,谁把我们接口堵死了?!

时间:2024-07-26 11:30:23浏览次数:6  
标签:堵死 Tomcat 处理 接口 故障 线程 连接池 请求

唉。。。

大家好,我是程序员鱼皮。又来跟着鱼皮学习线上事故的处理经验了喔!

事故现场

周一下午,我们的 编程导航网站 连续出现了两次故障,每次持续半小时左右,现象是用户无法正常加载网站,一直转圈圈。

用户很快就在群里炸开锅了,甚至有用户表示 “我提前进去了,都不敢刷新。。”

看到这些我真的非常难受,我们团队的开发同学也第一时间展开了排查。

简单看了下前端向后端发的请求,发现所有的请求都一直阻塞,直到超时。直接请求后端服务器的接口也是一样的,等了很久都没有正常返回数据。最关键的是,所有接口都阻塞住了,哪怕只是请求个健康检查接口(后端直接返回 "ok",不查询数据库),也无法正常响应。

我们的后端服务是部署在容器托管平台的,正常情况下如果资源(比如 CPU 和内存)占用超过一定比例,会自动扩容节点来让服务承载更多的并发请求,但为什么这次没有扩容呢?

其实有经验的朋友应该已经能猜到接口堵死的原因了,下面我带大家揭开谜团。

 

事故排查

根据上面的现象,推测大概率是接口层出了问题,而不涉及到业务和数据库等依赖资源。由于我们的后端使用的是 Spring Boot + 内嵌的 Tomcat 服务器,而 Tomcat 同时处理请求的最大线程数是固定的(默认是 200),所以当同时处理的请求过多,并且每个请求一直没有处理完成时,所有的线程都在繁忙,没有办法处理新的请求,就会导致新的请求排队等待处理,从而造成了接口堵死(迟迟无法响应)的现象。

这里我用一个简单的程序来模拟下接口堵死和排查过程。

首先写一个非常简单的测试接口,在返回内容前加一个 Thread.sleep,模拟耗时的操作,让处理请求的线程进入较长的等待。

然后更改下 Tomcat 的最大线程数为 5,便于我们模拟线程数不够的情况:

启动项目,在 Thread.sleep 打断点,然后连续请求 6 次接口。

应该只有 5 次请求会进入断点,最后一次请求会一直转圈卡住,没有线程来处理。这样我们就还原了事故现场。

但以上只是推测,实际线上项目中,怎么去排查确认 Tomcat 线程都阻塞了呢?又怎么确认是哪个接口或代码让 Tomcat 线程阻塞等待了呢?

其实很简单,首先用 jps -l 命令查看 Java 后端服务对应的进程 PID:

然后使用 jstack 命令生成线程快照,并保存为文件。具体命令如下:

jstack <进程PID> > thread_dump.txt

打开线程快照文件,所有线程的状态一目了然,搜索 http-nio 就能看到 Tomcat 的请求处理线程,果然所有的请求处理线程状态都是 TIMED_WAITING ,表示线程正在等待另一个线程执行特定的动作,但是有一个指定的等待时间。而且能直接看到请求是阻塞在了哪个代码位置。

利用这个方法,我们也很快定位到了编程导航接口堵死的原因,是发生在一个从数据库查询用户的方法。由于我们昨天下午执行了短信群发召回老用户的动作,导致大量用户同时访问编程导航并执行这个方法。由于涉及的数据库查询操作执行较慢,每个请求都需要等待数据库查询出结果后,才能响应数据,下一个请求才能再进来查询数据库,就导致大量 Tomcat 请求处理线程阻塞在等待数据库查询上,再进一步导致新的请求要排队等待处理。

真相大白了!

 

如何解决?

其实我们这次遇到的问题就是典型的 “线上连接池爆满问题”,面试的时候也是经常问的。前面讲了怎么排查此类问题,那如何解决这类问题呢?

首先遇到连接池爆满的情况,先保护现场,比如按照鱼皮上面的操作 dump 线程信息,然后赶紧重启服务或启动新的实例,让用户先能正常使用。再进行排查分析和优化。

如何优化线上连接池爆满问题?首先肯定还是要优化造成请求阻塞的代码。比如数据库查询慢,我们就通过添加索引来提升查询速度。

还可以增加数据库连接池的大小,在 Spring Boot 中,默认使用 HikariCP 作为数据源连接池,而 HikariCP 的 maximumPoolSize(最大连接池大小)默认值只有 10,显然是不足以应对高并发场景的。可以把下面的配置数值调大一点:

spring:
datasource:
  hikari:
    maximum-pool-size: 50

由于后端请求操作不止有查询数据库,所以 Tomcat 线程池的最大线程数和最小空闲线程数也可以按需调整,比如下列配置:

# 设置 Tomcat 最大线程数
server.tomcat.threads.max=300
# 设置 Tomcat 最小空闲线程数
server.tomcat.threads.min-spare=20

适当调大 Tomcat 的最大线程数,可以增加并发请求的处理能力。适当调大 Tomcat 的最小空闲线程数,可以确保在并发高峰时刻,Tomcat 能迅速响应新的请求,而不需要重新创建线程。

其实我们大多数情况下,线上服务器(容器)的内存利用率是不高的,所以可以根据实际的资源和并发情况,适当地改一改配置。记得多做做测试,因为过高的线程数可能导致线程调度开销增加,反而降低性能。

 

现实

好吧,以上只是我遇到此类问题的解决方案。但现实可能没那么理想,其实慢 SQL 这个问题我们在上一次故障时就已经定位到,并且在群内同步了。结果你猜怎么着,我们团队的开发同学发了一堆监控的截图,但是没有一个人真正去解决了这个问题,这才导致了故障在多日之后重新上演!

一旦发现了问题,就必须要想到尽可能长久支持的解决方案,要不然这监控不是白做了?

为什么这次事故持续了这么久呢?也是因为我团队的开发同学缺少线上问题处理的经验,在那一通分析,结果忘了恢复服务,过了半个小时用户还是无法访问,直到我去提醒。。。

所以这个时候就知道平时背的八股文有多重要了吧?Tomcat 的连接器配置和性能优化也是一道经典的八股文,也是我们 面试鸭刷题神器 收录的题目。这些知识等到真出了线上问题时,都是用的上的。

吃一堑,长一智,经过这次的事件,我相信团队的同学又一次成长了。读者朋友们,你们有收获没有嘞~

 

更多

标签:堵死,Tomcat,处理,接口,故障,线程,连接池,请求
From: https://www.cnblogs.com/yupi/p/18324970

相关文章

  • Python 获取tiktok视频评论回复数据 api接口
    TIKTOKapi接口爬取tiktok视频评论回复数据详细采集页面如图https://www.tiktok.com/@dailymail/video/7329872821990182190?q=neural%20link&t=1706783508149请求APIhttp://api.xxxx.com/tt/video/info/comment/reply?video_id=7288909913185701125&comment_id=7294900......
  • 11 个接口性能优化技巧(上)【送源码】
    接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题。该问题说简单也简单,说复杂也复杂。有时候,只需加个索引就能解决问题。有时候,需要做代码重构。有时候,需要增加缓存。有时候,需要引入一些中间件,比如mq。有时候,需要需要分库分......
  • 11 个接口性能优化技巧(下)【送源码】
     7.锁粒度在某些业务场景中,为了防止多个线程并发修改某个共享数据,造成数据异常。为了解决并发场景下,多个线程同时修改数据,造成数据不一致的情况。通常情况下,我们会:加锁。但如果锁加得不好,导致锁的粒度太粗,也会非常影响接口性能。7.1synchronized在java中提供了synchron......
  • 面向对象之抽象_接口_多态
    一.抽象1.抽象的介绍abstract​1.将多个类中共有的方法抽取到父类,发现方法体没法实现,所以将此方法定义成抽象方法​2.抽象方法所在的类一定是抽象类​3.抽象类publicabstractclass类名{}​4.抽象方法修饰符abstract返回值类型方法名(参数);​5.注意:......
  • [Java面向对象]接口
    接口接口中方法抛出异常在Java中,如果接口中的方法声明了抛出异常,那么实现这个接口的类必须处理这些异常。处理方式有两种:要么在实现方法中继续抛出异常,要么捕获并处理异常。假设接口定义如下:publicinterfaceMyInterface{voidmyMethod()throwsIOException;}继......
  • 利用Java调用人脸身份证比对接口
    一、什么是人脸身份证比对接口?人脸身份证比对接口是一种特定的API接口服务,主要用于将提供的人脸图片和对应的身份证照片/号码进行比对,以此验证其身份。这种接口的功能基于复杂的人脸识别技术,一般通过使用人工智能和深度学习算法来实现。它在许多需要实名身份验证的行业中有......
  • H3CNE(vlan与子接口技术)
    目录10.1vlan间通信技术示例一(多臂路由):10.2子接口技术 示例二(子接口技术):10.3vlannif接口技术10.3.1三层交换机与VLANNIF技术 示例三VLANNIF配置(将交换机当成路由器使用):10.1vlan间通信技术  实现不同vlan间的通讯:示例一(多臂路由):先配置SW1:[SW1]vlan......
  • 模块3 面向对象编程高级 --- 第九章:实现接口
    第九章实现接口主要知识点1、接口的定义2、接口的声明3、接口的实现4、接口的应用学习目标掌握接口的定义、声明、实现以及使用方法。接口是一种特殊的类,允许包括变量、常量等一个类所包含的基本内容,可以包含方法。接口中的方法只能有声明,不允许......
  • java之接口
    在java中,接口是一种用来定义程序的一种协议,它用来描述类或结构的一组相关行为。接口是由抽象类衍生的一个概念,并由此产生了一种编程方式。这种编程方式称为面向接口编程。面向接口编程就是将程序的不同业务逻辑相分离,以接口的形式对接不同的业务模块。在java中,使用接口的目......
  • 在K8S中,节点故障pod驱逐pod过程,时间怎么定义的?
    在Kubernetes(K8S)中,节点故障导致Pod驱逐的过程是一个涉及多个组件和参数协同工作的复杂流程。这个过程的时间定义主要通过一系列参数来配置和确定。以下是该过程的详细解释以及时间定义的说明:一、节点故障检测节点控制器(NodeController)检查间隔:node-monitor-period:这是节点控......