生产问题(十四)K8S抢占CPU导致数据库链接池打爆

news2024/9/30 17:22:03

一、引言

        线上一天出现了两次数据库连接失败的大量报错,一开始以为是数据库的问题,但是想了想如果是数据库的问题,应该会有大量的应用问题

        具体分析之后,发现其实是容器cpu出现了Throttled,导致大量线程阻塞

 二、分析

1、堆栈

        既然出现了报错,又没有发布,先看看堆栈里面报错的地方

        追踪到最底层的堆栈,显示的是数据库链接池耗尽[size:100; busy:1; idle:0; lastwait:44],100个链接只有4个在处理,没有一个空闲的,那么很明显其他的链接所在的线程都被阻塞了

2、源码

        虽然说不太可能,但是还是要看看源码。大多数同学都觉得框架里面是不可能有错的,所以也不会去看源码,基本上都是去其他方向分析,这个在99%的情况下没有问题,但是作者不止一次遇到框架本身是有问题的,只是遇到了一些极端情况。比如:

Mybatis拼接sql出错及源码解析_<if test="month!=null">-CSDN博客

生产问题(九)Mysql8.0 ddl问题_mysql8 default_authentication_plugin-CSDN博客

生产问题(十一)日志JavaAgent-NoClassDefFoundError_-javaagent 找不到报错-CSDN博客

生产问题(十三)谷歌Protobuf误修改系统全局时区-CSDN博客

        这些问题,如果不往框架的方向上走是找不到问题原因的,那么言归正传,我们看源码,这是tomcat进行线程链接的地方,超时的话就会报

throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
    "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +
    " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");

        和lastwait:44的现象也对的上,给我们进一步指明了方向,有很多占据数据库链接的线程不做事,其他很多线程在等

private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {

        if (isClosed()) {
            throw new SQLException("Connection pool closed.");
        } //end if

        //get the current time stamp
        long now = System.currentTimeMillis();
        //see if there is one available immediately
        PooledConnection con = idle.poll();

        while (true) {
            if (con!=null) {
                //configure the connection and return it
                PooledConnection result = borrowConnection(now, con, username, password);
                if (result!=null) return result;
            }

            //if we get here, see if we need to create one
            //this is not 100% accurate since it doesn't use a shared
            //atomic variable - a connection can become idle while we are creating
            //a new connection
            if (size.get() < getPoolProperties().getMaxActive()) {
                //atomic duplicate check
                if (size.addAndGet(1) > getPoolProperties().getMaxActive()) {
                    //if we got here, two threads passed through the first if
                    size.decrementAndGet();
                } else {
                    //create a connection, we're below the limit
                    return createConnection(now, con, username, password);
                }
            } //end if

            //calculate wait time for this iteration
            long maxWait = wait;
            //if the passed in wait time is -1, means we should use the pool property value
            if (wait==-1) {
                maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();
            }

            long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));
            waitcount.incrementAndGet();
            try {
                //retrieve an existing connection
                con = idle.poll(timetowait, TimeUnit.MILLISECONDS);
            } catch (InterruptedException ex) {
                if (getPoolProperties().getPropagateInterruptState()) {
                    Thread.currentThread().interrupt();
                }
                SQLException sx = new SQLException("Pool wait interrupted.");
                sx.initCause(ex);
                throw sx;
            } finally {
                waitcount.decrementAndGet();
            }
            if (maxWait==0 && con == null) { //no wait, return one if we have one
                if (jmxPool!=null) {
                    jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait.");
                }
                throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
                        "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");
            }
            //we didn't get a connection, lets see if we timed out
            if (con == null) {
                if ((System.currentTimeMillis() - now) >= maxWait) {
                    if (jmxPool!=null) {
                        jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout.");
                    }
                    throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +
                        "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +
                        " seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");
                } else {
                    //no timeout, lets try again
                    continue;
                }
            }
        } //while
    }

3、监控

        既然分析出有很多占据数据库链接的线程不做事,其他线程在等,那么在cpu、线程方面就应该有所体现,接下来看监控。

        监控显示对应问题的时间点发生了CPU Throttled,他会限制应用程序或进程的CPU使用率,一度达到十几秒,等待io的task线程激增,很明显这就是具体的原因,接下来需要找寻的是,为什么会产生Throttled。

        jvm的内存和线程激增也侧面验证了cpu阻塞,大量的线程处理不了任务

三、cpu抢占

        通过上述分析,接下来需要找寻为什么会产生Throttled?

        一般来说有两种原因:

1、瞬时流量激增

        这种一般就是入口流量突然增加,已经分配的cpu处理不过来,需要按照K8S的动态分配继续申请cpu,但是来不及

        看看请求数量,整体变化不大,但是监控采集是一分钟一次,所以这个方向不能被排除,但是没有其他方面的证据协助分析

2、宿主机cpu被抢占

        这段监控是下午出问题时候的,上午找不到是哪个宿主机了

        可以看到这个宿主机不太对,他这个0.7是每个cpu维度的,对应时间从0.3到0.7,相当于原来用三个,突然用到了7个,所以宿主机争抢的概率还是大一些        

        再看看具体每个容器,明显黄色容器的cpu有一段飙升,倒数第三个是出问题的机器       ,上面的几个容器都有增加,一方面也代表了cpu被其他容器抢占的可能 

四、解决

        由于瞬时流量激增、宿主机cpu被抢占两个方向的解决方案是完全不同的,所以我们需要先快速处理。

1、升配、增加机器数量

        这样瞬时流量就会大幅减小,由于流量小了,cpu被抢占也就不会有太大影响

2、设置cpu set模式

        K8S给予linux的cgroups默认的一般都是share模式,就是给你分这么多cpu但是后续可以被抢占。

        设置set模式,就是不给别人抢占,当然你忙的时候也抢不了别人

        作者认为对于核心应用来说隔离是必须的,如果他隔离的情况下处理不了就应该升配或者扩容,而不是抢别人的

3、增加秒级监控

        一分钟一次的监控属实是坑,基本上辅助不了确定的方向,只能是结合监控数据去猜一下,之前出现过很多问题也因为监控确定不了具体原因,只是锁定几个方向

        但是怎么增加,增加有没有影响都是件麻烦事,因为框架组那边很直白的说了一分钟一次对于目前的监控都有压力,秒级的要自己暴露出prometheus规范的指标,通过加env的方式暴露出来,框架的监控agent会去采集。

        那他为什么不去全部都加一下呢,还是内部有一些资源或者风险存在的,这就很顶了。

五、总结

        这一次的问题被锁定在了瞬时流量激增、宿主机cpu被抢占两个方向,宿主机cpu被抢占概率大一些,监控分钟级别的采集不能确定唯一方向,只能沿着两个方向一起解决。

        这也是个好事,起码知道了监控的机制,后续需要改进的方向。

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

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

相关文章

UDS Flash刷写流程介绍

一、刷写流程介绍 1.1刷写包含以下三个步骤&#xff1a;预编程&#xff0c;编程&#xff0c;后编程 1.2预编程步骤 此步骤是保证能够正常进入编程&#xff08;10 02&#xff09;会话下。 &#xff08;1&#xff09;如果无特殊要求&#xff0c;只保证刷写能够正常进行&#x…

k8s的图形化工具---rancher

rancher是一个开源的企业级多集群的k8s管理平台。 rancher和k8s的区别&#xff1a;都是为了容器的调度和编排系统。但是rancher不仅可以调度还可以管理整个k8s集群。 rancher自带监控(普罗米修斯) 实验部署 master01 20.0.0.32 node01 20.0.0.34 node02 20.0.0.35 test …

嵌入式-stm32-江科大-EXTI外部中断

一&#xff1a;EXTI外部中断&#xff08;external interrupt&#xff09; 1.1 STM32 中断系统 中断是指在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前的程序&#xff0c;转而去处理中断程序&#xff0c;…

菜鸟初进stable diffusion

不知道是不是玩novelai被boss看到了&#xff0c;推荐了我学stable diffusion 扩散模型 DALL E Midjourney stable diffusion latent diffusion 说是改进点在于“给输入图片压缩降低维度&#xff0c;所以有个latent&#xff0c;从而减少计算量”&#xff0c;类似于下采样吧&…

消息队列RabbitMQ.01.安装部署与基本使用

目录 RabbitMQ的作用 Message queue 释义 问题思考 存在的问题 优化方案 案例分析 带来的好处 消息队列特点 Email邮件案例分析 Docker安装部署RabbitMQ 1.下拉镜像 2.运行RabbitMQ 3.打开防火墙端口号并重新运行防火墙 4.容器启动后,可以通过 docker logs 容器 查…

Servlet 与 MVC

主要内容 Servlet 重点 MVC 重点 Filter 重点 章节目标 掌握 Servlet 的作用 掌握 Servlet 的生命周期 掌握 JSP 的本质 掌握 MVC 的设计思想 掌握 Filter 的作用及使用场景 第一节 Servlet 1. Servlet 概念 Servlet 是在服务器上运行的能够对客户端请求进行处理&a…

leetcode 刷题2

二分查找的绝妙运用&#xff1a; 看到有序数列&#xff0c;算法复杂度 0033. 搜索旋转排序数组 class Solution { public:int search(vector<int>& nums, int target) {int left 0;int right nums.size() - 1;while (left < right) {int mid left (right - …

Debezium发布历史85

原文地址&#xff1a; https://debezium.io/blog/2020/03/05/db2-cdc-approaches/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. 运行 Db2 更改数据捕获的方法 2020 年 3 月 5 日 作者&#xff1a; Luis Garcs…

JanusGraph图数据库的应用以及知识图谱技术介绍

目录 JanusGraph介绍 JanusGraph 的主要优势 JanusGraph的应用&#xff1a; JanusGraph 的行业应用&#xff1a; 架构概览 分布式技术应用 横向扩展能力 程序与janus的交互 Janus与图数据库相关概念 结构化存储 图结构存储 实体关系存储 知识存储技术 JanusGraph介…

数据结构<1>——树状数组

树状数组&#xff0c;也叫Fenwick Tree和BIT(Binary Indexed Tree)&#xff0c;是一种支持单点修改和区间查询的&#xff0c;代码量小的数据结构。 那神马是单点修改和区间查询&#xff1f;我们来看一道题。 洛谷P3374(模板): 在本题中&#xff0c;单点修改就是将某一个数加上…

销售额稳居行业第二!苏州金龙2023年跑出高质量发展加速度

2023年&#xff0c;苏州金龙海格客车销量同比去年增25.75%&#xff0c;实现销售11453辆、销售额78亿元的业绩&#xff0c;稳居行业第二位&#xff0c;更跑赢行业大盘&#xff01; 聚焦主业&#xff0c;及时呼应客户需求&#xff1b;聚力新能源技术提升&#xff0c;抓住商用车价…

LabVIEW高级CAN通信系统

LabVIEW高级CAN通信系统 在现代卫星通信和数据处理领域&#xff0c;精确的数据管理和控制系统是至关重要的。设计了一个基于LabVIEW的CAN通信系统&#xff0c;它结合了FPGA技术和LabVIEW软件&#xff0c;主要应用于模拟卫星平台的数据交换。这个系统的设计不仅充分体现了FPGA在…

时间序列大模型:TimeGPT

论文&#xff1a;https://arxiv.org/pdf/2310.03589.pdf TimeGPT&#xff0c;这是第一个用于时间序列的基础模型&#xff0c;能够为训练期间未见过的多样化数据集生成准确的预测。 大规模时间序列模型通过利用当代深度学习进步的能力&#xff0c;使精确预测和减少不确定性成为…

光流估计概念和算法

什么是光流&#xff1f; 光流就是物体和观测者之间的互相运动&#xff0c;亮度变化的速度矢量&#xff0c;下图两张图片表示了光流的原理。 光流的算法有几个基本不变的假设&#xff1a; 1&#xff0c;光强不变假设&#xff1b; 一元的n阶泰勒公式&#xff1a; 在这里插入图…

Mysql复习1--理论基础+操作实践--更新中

Mysql 索引索引的分类 索引InnoDB引擎MyISAM引擎Memory引擎Btree索引支持支持支持hash索引不支持不支持支持R-tree索引不支持支持不支持Full-text索引5.6版本以后支持支持不支持 索引 解释说明: 索引指的是帮助mysql高效的获取数据的结构叫做索引(有序) 没有建立索引的时候–…

Shell 虚拟机基线配置脚本示例

这是一个配置虚拟机基线的示例&#xff0c;包含关闭防火墙、禁用SElinux、设置时区、安装基础软件等。 这只是一个简单的模板&#xff0c;基线配置方面有很多&#xff0c;后续可以按照这个模板去逐步添加 代码示例 [rootbogon ~]# cat bastic.sh #!/bin/bashRED\E[1;31m GRE…

微信万能表单源码系统:自定义表单内容+自由创建多表单 附带完整的代码包以及安装部署教程

在当今信息化社会&#xff0c;在线表单已经成为收集、处理数据的重要工具。无论是企业还是个人&#xff0c;都需要通过表单来进行信息的收集、调查、报名等操作。然而&#xff0c;传统的表单系统往往功能单一&#xff0c;无法满足复杂多变的需求。为了解决这一问题&#xff0c;…

Hadoop3完全分布式搭建

一、第一台的操作搭建 修改主机名 使用hostnamectl set-hostname 修改当前主机名 关闭防火墙和SELlinux 1&#xff0c;使用 systemctl stop firewalld systemctl disable firewalld 关闭防火墙 2&#xff0c;使用 vim /etc/selinux/config 修改为 SELINUXdisabled 使用N…

【操作系统】实验五 添加内核模块

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

《Linux设备驱动开发详解》读书笔记

《Linux设备驱动开发详解》读书笔记 本书主要介绍linux设备驱动开发的方法&#xff0c;共有21章&#xff1a; linux设备驱动概述及开发环境搭建驱动设计的硬件基础linux内核及内核编程linux内核模块linux文件系统与设备文件字符设备驱动linux设备驱动中的并发控制linux设备驱…