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

唉。。。

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

事故现场

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

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

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

简单看了下前端向后端发的请求,发现所有的请求都一直阻塞,直到超时。直接请求后端服务器的接口也是一样的,等了很久都没有正常返回数据。最关键的是,所有接口都阻塞住了,哪怕只是请求个健康检查接口(后端直接返回 “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 的连接器配置和性能优化也是一道经典的八股文,也是我们 面试鸭刷题神器 收录的题目。这些知识等到真出了线上问题时,都是用的上的。

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

更多

💻 编程学习交流:编程导航
📃 简历快速制作:老鱼简历
✏️ 面试刷题神器:面试鸭

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3266841.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Golang | 腾讯一面

go的调度 Golang的调度器采用M:N调度模型&#xff0c;其中M代表用户级别的线程(也就是goroutine)&#xff0c;而N代表的事内核级别的线程。Go调度器的主要任务就是N个OS线程上调度M个goroutine。这种模型允许在少量的OS线程上运行大量的goroutine。 Go调度器使用了三种队列来…

Lua脚本简单理解

目录 1.安装 2.语法 2.1Lua数据类型 2.2变量 2.3lua循环 2.4流程控制 2.5函数 2.6运算符 2.7关系运算符 3.lua脚本在redis中的使用 3.1lua脚本再redis简单编写 3.2普通锁Lua脚本 3.3可重入锁lua脚本 1.安装 centos安装 安装指令&#xff1a; yum -y update yum i…

mysql面试(六)

前言 本章节详细讲解了一下mysql执行计划相关的属性释义&#xff0c;以及不同sql所出现的不同效果 执行计划 一条查询语句经过mysql查询优化器的各种基于成本和各种规则优化之后&#xff0c;会生成一个所谓的 执行计划&#xff0c;这个执行计划展示了这条查询语句具体查询方…

解决zabbix-server7 中文乱码问题

系统使用centos9 安装中文支持 yum install -y fontconfig langpacks-zh_CN.noarch 检查是否已有中文字体&#xff1a; fc-list :langzh 看到 直接使用GOOGLE的字体 ln -fs /usr/share/fonts/google-noto-cjk/NotoSansCJK-DemiLight.ttc /etc/alternatives/zabbix-web-fo…

Godot入门 05收集物品

创建新场景&#xff0c;添加Area2D节点&#xff0c;AnimatedSprite2D节点 &#xff0c;CollisionShape2D节点 添加硬币 按F键居中&#xff0c;放大视图。设置动画速度设为10FPS&#xff0c;加载后自动播放&#xff0c;动画循环 碰撞形状设为圆形&#xff0c;修改Area2D节点为Co…

python+vue3+onlyoffice在线文档系统实战20240725笔记,首页开发

解决遗留问题 内容区域的高度没有生效&#xff0c;会随着菜单的高度自动变化。 解决方案&#xff1a;给侧边加上一个最小高度。 首页设计 另一种设计&#xff1a; 进来以后&#xff0c;是所有的文件夹和最近的文件。 有一张表格&#xff0c;类似于Windows目录详情&…

MySQL窗口函数详解

MySQL窗口函数详解 MySQL从8.0版本开始引入了窗口函数&#xff0c;这是一个强大的特性&#xff0c;可以大大简化复杂的数据分析任务。本文将详细介绍MySQL窗口函数的概念、语法和常见用法&#xff0c;并结合实际应用场景进行说明。 什么是窗口函数? 窗口函数是一种能够对结…

git 版本回退-idea

1、选中项目&#xff0c;右键&#xff0c;打开 git历史提交记录 2、选中想要回退的版本&#xff0c;选择 hard&#xff08;不保留版本记录&#xff09; 3、最终选择强制提交&#xff08;必须强制&#xff09; OK&#xff0c;搞定

AI(Adobe lliustrator)教程+软件包

简介&#xff1a; 软件主要应用于印刷出版、海报书籍排版、专业插画、多媒体图像处理和互联网页面的制作等&#xff0c;也可以为线稿提供较高的精度和控制&#xff0c;适合生产任何小型设计到大型的复杂项目。 通常用于创建LOGO(商标或徽标)&#xff0c;图标&#xff0c;插图…

go语言学习文档精简版

Go语言是一门开源的编程语言&#xff0c;目的在于降低构建简单、可靠、高效软件的门槛。Go平衡了底层系统语言的能力&#xff0c;以及在现代语言中所见到的高级特性。 你好&#xff0c;Go package main // 程序组织成包import "fmt" // fmt包用于格式化输出数据// …

C++ primer plus 第16章string 类和标准模板库, 函数符概念

C primer plus 第16章string 类和标准模板库, 函数符概念 C primer plus 第16章string 类和标准模板库, 函数符概念 文章目录 C primer plus 第16章string 类和标准模板库, 函数符概念16.5.1 函数符概念程序清单16.15 functor.cpp 16.5.1 函数符概念 正如 STL定义了容器和迭代…

20240725项目的maven环境报红-重新配置maven

1.在编辑器里面打开项目&#xff0c;导入源码 &#xff08;1&#xff09;找到项目的地址C:\Users\zzz\IdeaProjects\datasys&#xff0c;然后右击用idea编辑器打开。 &#xff08;2&#xff09;idea中上菜单栏打开open&#xff0c;然后输入file&#xff0c;选择源代码文件 2.…

C++ //练习 15.28 定义一个存放Quote对象的vector,将Bulk_quote对象传入其中。计算vector中所有元素总的net_price。

C Primer&#xff08;第5版&#xff09; 练习 15.28 练习 15.28 定义一个存放Quote对象的vector&#xff0c;将Bulk_quote对象传入其中。计算vector中所有元素总的net_price。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块&am…

openFeign配置okhttp

原来的项目出现了性能问题&#xff0c;老大不知道怎么的&#xff0c;让我改openFeign线程池为okhttp&#xff0c;说原生的不支持线程池性能比较差。 原openFeign配置文章地址 一、pom文件 <dependency><groupId>org.springframework.cloud</groupId><arti…

泰金新能估值暴增之谜:研发费用率远低同行,资产负债率居高不下

《港湾商业观察》施子夫 王璐 作为新“国九条”首家受理的科创板IPO企业&#xff0c;外界对于西安泰金新能科技股份有限公司&#xff08;以下简称&#xff0c;泰金新能&#xff09;的关注度自然相当之高。 泰金新能保荐机构为中信建投。通过招股书不难看出&#xff0c;公司的…

idea中导入外部依赖并打包到jar包中

前言&#xff1a; 很多时候在我们写项目对接三方的时候都需要导入三方jar包&#xff0c;而这时候我们用平常的pom里面写依赖发现导入不了&#xff08;直接把jar包放在本地导入的话打包的话也不会将该依赖打包进我们项目的jar包&#xff09;&#xff0c;我在网上找了几种方法 …

使用双指针法解决最大容积问题:移动较短的线以优化面积【双指针】

在解决算法问题时&#xff0c;我们常常需要寻找最佳的方法来提高效率。今天&#xff0c;我们将讨论一个经典的问题——在一组垂直线中找到两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。这篇文章将详细解析如何使用双指针法来解决这个问题&#xff0c;特别…

仪器校准中,标准样品要怎么选用?需要注意什么?

正确使用标准物质和标准样品是保证仪器校准值准确可靠的重要手段。标准物质的正确使用包括正确选择、正确使用&#xff08;防止误用&#xff09;和使用中的注意事项。 1. 参考资料证书之中给出的“参考资料的使用”信息&#xff0c;用户应予以注意。当参比材料用于证书所述用途…

自研点直播转码核心

1. 背景 视频转码是将视频文件经过解封装、解码、滤镜处理、编码、封装从而转换为另一个视频文件的过程&#xff0c;B站每天都有大量的视频原片上传后经过转码系统转换为多个不同分辨率。转换后的视频在画质接近原片的前提下会拥有更低的码率&#xff0c;因此会提高网络传输时的…