关于阿里云:Java-应用压测性能问题定位经验分享

4次阅读

共计 9288 个字符,预计需要花费 24 分钟才能阅读完成。

作者:凡勇

什么是压测

压测,即压力测试,是确立零碎稳定性的一种测试方法,通常在零碎失常运作范畴之外进行,以考查其性能极限和和可能存在的隐患。

压测次要用于检测服务器的承受能力,包含用户承受能力,即多少用户同时应用零碎时根本不影响品质、流量接受等。另外,通过诸如疲劳测试还能发现零碎一些稳定性的问题,比方是否存在连接池中的连贯被耗尽,内存被耗尽,线程池被耗尽,这些只能通过疲劳测试来进行发现定位。

为什么要压测

压测的目标就是通过模仿实在用户的行为,测算出机器的性能 (单台机器的 QPS、TPS),从而推算出零碎在接受指定用户数(100 W) 时,须要多少机器能撑持得住。因而在进行压测时肯定要当时设定压测目标值,这个值不能太小,也不能太大,依照目前业务预估的增长量来做一个正当的评估。压测是在上线前为了应答将来可能达到的用户数量的一次预估(提前演练),压测当前通过优化程序的性能或筹备短缺的机器,来保障用户的体验。压测还能探测利用零碎在呈现交易洪峰时稳定性状况,以及可能呈现的一些问题,发现利用零碎单薄一环,从而更有针对性地进行增强。

压测分类

这几种测试能够交叉进行,个别会在压力测试性能指标达标后,再安顿耐久性测试。

压测名词解释

常见的压测工具

ab

ApacheBench 是 Apache 服务器自带的一个 web 压力测试工具,简称 ab。ab 又是一个命令行工具,对发动负载的本机要求很低,依据 ab 命令能够创立很多的并发拜访线程,模仿多个访问者同时对某一 URL 地址进行拜访,因而能够用来测试指标服务器的负载压力。总的来说 ab 工具玲珑简略,上手学习较快,能够提供须要的根本性能指标,然而没有图形化后果,不能监控。

Jmeter

Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试,它最后被设计用于 Web 利用测试,但起初扩大到其余测试畛域。

JMeter 可能对应用程序做性能 / 回归测试,通过创立带有断言的脚本来验证你的程序返回了你冀望的后果。

JMeter 的性能过于弱小,这里临时不介绍用法,能够查问相干文档应用(参考文献中有举荐的教程文档)

LoadRunner

LoadRunner 是 HP(Mercury)公司出品的一个性能测试工具,性能十分弱小,很多企业级客户都在应用,具体请参考官网链接。

阿里云 PTS

性能测试 PTS(Performance Testing Service)是一款性能测试工具。反对按需发动压测工作,可提供百万并发、千万 TPS 流量发动能力,100% 兼容 JMeter。提供的场景编排、API 调试、流量定制、流量录制等性能,可疾速创立业务压测脚本,精准模仿不同量级用户拜访业务零碎,帮忙业务疾速晋升零碎性能和稳定性。

作为阿里外部应用多年的性能测试工具,PTS 具备如下个性:

  1. 免运维、开箱即用。SaaS 化施压、最大反对百万级并发、千万级 TPS 流量自助发动能力。
  2. 反对多协定 HTTP1.1/HTTP2/JDBC/MQTT/Kafka/RokectMq/Redis/Websocket/RMTP/HLS/TCP/UDP/SpringCloud/Dubbo/Grpc 等支流协定。
  3. 反对流量定制。寰球施压地区定制 / 运营商流量定制 /IPv6 流量定制。
  4. 稳固、平安。阿里自研引擎、多年双十一场景打磨、反对 VPC 网络压测。
  5. 性能压测一站式解决方案。** 0 编码构建简单压测场景,笼罩压测场景构建、压测模型设定、发动压力、剖析定位问题、出压测报告残缺的压测生命周期。
  6. 100% 兼容开源 JMeter。
  7. 提供平安、无侵入的生产环境写压测解决方案。

压测工具的比拟

如何抉择压测工具

这个世界上没有最好的工具,只有最适宜的工具,工具千千万,抉择一款适宜你的才是最重要的,在理论应用中有各种场景,读者能够联合压测步骤来确定适宜本人的工具:

  1. 确定性能压测指标:性能压测指标可能源于我的项目打算、业务方需要等
  2. 确定性能压测环境:为了尽可能施展性能压测作用,性能压测环境该当尽可能同线上环境统一
  3. 确定性能压测通过规范:针对性能压测指标以及选取的性能压测环境,制订性能压测通过规范,对于不同于线上环境的性能压测环境,通过规范也该当适度放宽
  4. 设计性能压测:编排压测链路,结构性能压测数据,尽可能模仿实在的申请链路以及申请负载
  5. 执行性能压测:借助性能压测工具,依照设计执行性能压测
  6. 剖析性能压测后果报告:剖析解读性能压测后果报告,断定性能压测是否达到预期指标,若不满足,要基于性能压测后果报告剖析起因

由上述步骤可知,一次胜利的性能压测波及到多个环节,从场景设计到施压再到剖析,缺一不可。工欲善其事,必先利其器,而一款适合的性能工具意味着咱们可能在尽可能短的工夫内实现一次正当的性能压测,达到事倍功半的成果。

JAVA 利用性能问题排查指南

问题分类

问题不拘一格,各种各样的问题都会有。对其进行形象和分类是十分必要的。这里将从两个维度来对性能问题进行分类。第一个维度是资源维度,第二个维度是频率维度。

资源维度类的问题:CPU 冲高,内存使用不当,网络过载。

频率维度类的问题:交易持续性迟缓,交易偶发性迟缓。

对于每一类问题都有相应的解决办法,办法或者工具使用不当,会导致不能疾速而且精准地排查定位问题。

压测性能问题定位调优是一门须要多方面综合能力联合的一种技术工作,须要凭借集体的技术能力、教训、有时候还须要一些直觉和灵感,还须要肯定的沟通能力,因为有时候问题并不是由定位问题的人发现的,所以须要通过一直地沟通来发现一些蛛丝马迹。波及的技术知识面远不仅限于程序语言自身,还可能须要扎实的技术基本功,比方操作系统原理、网络、编译原理、JVM 等常识,决不只是简略的理解,而是真正的把握,比方 TCP/IP,必须得深刻把握。JVM 得深刻把握内存组成,内存模型,深刻把握 GC 的一些算法等。这也是一些初中级技术人员在一遇到性能问题就傻眼,齐全不晓得如何从哪里下手。如果领有扎实的技术基本功,再加上一些实战经验而后造成一套属于本人的打法,在遇到问题后能力心中不乱,疾速拨开迷雾,最终找到问题的症结。

本文笔者还带来了理论工作中定位和排查进去的一些典型的性能问题的案例,每个案例都会介绍问题产生的相干背景,一线人员提供的问题景象和初步排查定位论断,且在笔者染指后看到的问题景象,再配合一些罕用的问题定位工具,介绍发现和定位问题的整个过程,问题产生的根本原因等。

剖析思路框架

遇到一个性能问题,首先要从各种表象和一些简略工具将问题进行定义和分类,而后再做进一步的定位剖析,能够参考一下图 1 作者总结进去的一个决策图,这张图是笔者从近几个金融行业 ToB 我的项目中做性能定位调优过程的一个总结提练,不肯定适宜所有的问题,但至多笼罩到了近几个我的项目中遇到的性能问题的排查过程。在接下来的大篇幅中将对每一类问题进行开展,并附上一些实在的经典案例,这些案例都是真真实实产生的,有肯定的代表性,且很多都是客户定位了很长时间都没发现问题根本原因的问题。其中 GC 类问题在此文不做过多剖析,对于 GC 这一类问题后续有空写一篇专门的文章来进行开展。

内存溢出

内存溢出问题依照问题产生频率又可进一步分为堆内存溢出、栈内存溢出、Metaspace 内存溢出以及 Native 内存溢出,上面对每种溢出状况进行详细分析。

  • 堆内存溢出

置信这类问题大家多多少少都接触过,问题产生的根本原因就是利用申请的堆内存超过了 Xmx 参数设置的值,进而导致 JVM 根本处于一个不可用的状态。如图 2 所示,示例代码模仿了堆内存溢出,运行时设置堆大小为 1MB,运行后后果如图 3 所示,抛出了一个 OutOfMemoryError 的谬误异样,相应的 Message 是 Java heap space,代表溢出的局部是堆内存。

  • 栈内存溢出

这类问题次要是因为办法调用深度太深,或者不正确的递归办法调用,又或者是 Xss 参数设置不当都会引发这个问题,如图 4 所示,一个简略的有限递归调用就会引发栈内存溢出,出错后果如图 5 所示,将会抛一个 StackOverflowError 的谬误异样。Xss 参数能够设置每个线程栈内存最大大小,JDK8 的默认大小为 1MB,失常状况下个别不须要去批改该参数,如果遇到 StackOverflowError 的报错,那么就须要注意了,须要查证是程序的问题还是参数设置的问题,如果的确是办法调用深度很深,默认的 1MB 不够用,那么就须要调高 Xss 参数。

  • Native 内存溢出

这种溢出产生在 JVM 应用堆外内存时,且超过一个过程所反对的最大的内存下限,或者堆外内存超过 MaxDirectMemorySize 参数指定的值时即会引发 Native 内存溢出。如图 6 所示,须要配置 MaxDirectMemorySize 参数,如果不配置这个参数估计很难模拟出这个问题,作者的机器的 64 位的机器,堆外内存的大小可想而知了。运行该程序失去的运行后果如图 7 所示,抛出来的异样也是 OutOfMemoryError,这个跟堆内存异样相似,然而 Message 是 Direct buffer memory,这个跟堆内存溢出的 Message 是不一样的,请特地注意这条 Message,这对精准定位问题是十分重要的。

  • Metaspace 内存溢出

Metaspace 是在 JDK8 中才呈现的,之前的版本中都叫 Perm 空间,大略用处都相差不大。模仿 Metaspace 溢出的形式很简略,如图 8 所示通过 cglib 一直动态创建类并加载到 JVM,这些类信息就是保留在 Metaspace 内存外面的,在这里为了疾速模拟出问题,将 MaxMetaspaceSize 设置为 10MB。执行后果如图 9 所示,仍然是抛出 OutOfMemoryError 的谬误异样,然而 Message 变成了 Metaspace。

JVM 的内存溢出最常见的就这四种,如果能晓得每一种内存溢出呈现的起因,那么就能疾速而精准地进行定位。上面对一些遇到的实在的经典案例进行剖析。

  • 案例:堆外内存溢出

这种问题也比拟好查,前提是在堆内存产生溢出时必须主动转储堆内存到文件中,如果压测过程中通过 kill -3 或者 jmap 命令触发堆内存转储。而后通过一些堆内存剖析工具比方 IBM 的 Heap Analyzer 等工具找出是哪种对象占用内存最多,最终能够把问题起因揪出来。

如果须要在产生 OOM 时主动转储堆内存,那么须要在启动参数中退出如下参数:

-XX:+HeapDumpOnOutOfMemoryError  

-XX:HeapDumpPath=/usr/local/oom

如果须要手工获取线程转储或者内存转储,那么请应用 kill -3 命令,或者应用 jstack 和 jmap 命令。

jstack -l pid > stackinfo,这条命令能够把线程信息转储到文本文件,把文件下载到本地而后用诸如 IBM Core file analyze 工具进行剖析。

jmap -dump:format=b,file=./jmap.hprof pid,这条命令能够把堆内存信息到当前目录的 jmap.hprof 文件中,下载到本地,而后用诸如 IBM Heap Analyze 等堆内存剖析工具进行剖析,依据二八定律,找准最耗内存的对象就能够解决 80% 的问题。

图 10 就是一个实在产生的案例,该问题的产生景象是这样的,压测开始后,前十分钟一切正常,然而在经验大概十分钟后,TPS 逐步降落,直到前面客户端的 TCP 连贯都建不下来,客户一度认为是服务端 Linux 的网络栈的参数设置有问题,导致 TCP 无奈建连,给出的证据是,服务端存在大量的 TIME_WAIT 状态的连贯,而后要求调整 Linux 内核网络参数,缩小 TIME_WAIT 状态的连接数。什么是 TIME_WAIT?在这个时候就不得不祭出祖传 TCP 状态机的那张图了,如图 11 所示。对照这个图就能晓得 TIME_WAIT 的来胧去脉了,TIME_WAIT 次要呈现在被动敞开连贯方,当然了,如果单方刚好同时敞开连贯的时候,那么单方都会呈现 TIME_WAIT 状态。在进行敞开连贯四路握手协定时,最初的 ACK 是由被动敞开端收回的,如果这个最终的 ACK 失落,服务器将重发最终的 FIN,因而客户端必须保护状态信息以容许它重发最终的 ACK。如果不维持这个状态信息,那么客户端将响应 RST 分节,服务器将此分节解释成一个谬误(在 java 中会抛出 connection reset 的 SocketException)。因此,要实现 TCP 全双工连贯的失常终止,必须解决终止序列四个分节中任何一个分节的失落状况,被动敞开的客户端必须维持状态信息进入 TIME_WAIT 状态。

图 10 实在堆内存溢出案例一

图 11 TCP 状态机

顺着客户提供的这些信息,查了一下压测客户端,采纳的是 HTTP 协定,keep-alive 为开,而且采纳的是连接池的形式与服务端进行交互,实践上在服务器端不应该呈现如此之多的 TIME_WAIT 连贯,猜想一种可能性是因为客户侧刚开始压测的时候 TPS 比拟高,占用连接数多,后续性能下来后,连接数闲暇且来不及跟服务端进行保活解决,导致连贯被服务端给被动敞开掉了,但这也仅限于是猜想了。

为了更精准地定位问题,决定去一线现场看下状况,在 TPS 重大往下掉的时候,通过 top、vmstat 等命令进行初步探测,发现 cpu 占比并不非常高,大概 70% 左右。然而 JVM 占用的内存曾经快靠近 Xmx 参数配置的值了,而后用 jstat -gcutil -h10 pid 5s 100 命令看一下 GC 状况,不查不晓得一查吓一跳,如图 12 所示,初看这就是一份不太失常的 GC 数据,首先老年代占比直逼 100%,而后 5 秒内竟然进行了 7 次 FullGC,eden 区占比 100%,因为老年代曾经满了,年老代的 GC 都曾经停滞了,这显著不失常,趁 JVM 还活着,连忙执行 jmap -dump:format=b,file=./jmap.hprof pid,把整个堆文件快照拿下来,整整 5 个 G。取下来后通过 IBM 的 HeapAnalyzer 工具剖析堆文件,后果如图 10 所示,通过一番查找,发现某个对象占比特地大,占比达 98%,持续追踪持有对象,最终定位出问题,申请了某个资源,然而始终没有开释,批改后问题失去完满解决,后续再通过长达 8 个小时的耐久性测,没能再发现问题,TPS 始终十分稳固。

图 12 GC 状况统计分析

再来看看为何会呈现那么多的 TIME_WAIT 连贯,跟开始的猜想是统一的,因为大量的闲置连贯被服务端被动敞开掉,所以才会呈现那么多的 TIME_WAIT 状态的连贯。

CPU 高

  • 案例

某金融银行客户在压测过程中发现一个问题,导致 TPS 极低,交易响应时长甚至靠近惊人的 30S,重大不达票,服务响应工夫如图 23 所示,这是利用打的 tracer log,显示的耗时很不乐观。利用采纳 SOFA 构建,部署在专有云容器下面,容器规格为 4C8G,应用 OceanBase 数据库。交易迟缓过程中客户在相应容器外面用 top、vmstat 命令获取 OS 信息,发现内存应用失常,然而 CPU 靠近 100%,通过 jstack 命令取线程转储文件,如图 22 所示,客户发现大量的线程都卡在了获取数据库连贯下面,再上利用日志中也报了大量的获取 DB 连贯失败的谬误日志,这让客户认为是连接池中的连接数不够,所以一直持续加大 MaxActive 这个参数,DB 连接池应用的是 Druid,在加大参数后,性能没有任何改善,且获取不到连贯的问题仍旧。客户在排查该问题大略两周且没有任何实质性停顿后,开始向阿里 GTS 的同学求助。

笔者刚好在客户现场,染指该性能问题的定位工作。跟客户一番沟通,并查阅了了历史定位信息记录后,依据以往的教训,这个问题必定不是因为连接池中的最大连接数不够的起因导致的,因为这个时候客户曾经把 MaxActive 的参数曾经调到了恐怖的 500,但问题仍旧,在图 22 中还能看到一些有用的信息,比方正在 Waiting 的线程高达 908 个,Runnable 的线程高达 295 个,都是很恐怖的数字,大量的线程处于 Runnable 状态,CPU 忙着进行线程上下文的切换,CPU 呼呼地转,但理论并没有干多少有理论有意义的事。后经询问,客户将 SOFA 的业务解决线程数调到了 1000,默认是 200。

图 22 线程卡在获取 DB 连接池中的连贯

图 23 交易迟缓截图

查到这里根本能够判定客户陷入了“头痛医头,脚痛医脚”,“治标不治本”的困境,进一步跟客户沟通后,果然如此。刚开始的时候,是因为 SOFA 报了线程池满的谬误,而后客户一直加码 SOFA 业务线程池中最大线程数,最初加到了 1000,性能晋升不显著,而后报了一个获取不到数据库连贯的谬误,客户又认为这是数据库连贯不够了,调高 Druid 的 MaxActive 参数,最初无论怎么调性能也都上不来,甚至到前面把内存都快要压爆了,如图 24 所示,内存中被一些业务 DO 对象给填满了,前面客户一度认为存在内存泄露。对于这类问题,只有像是呈现了数据库连接池不够用、或者从连接池中获取连贯超时,又或者是线程池耗尽这类问题,只有参数设置是在正当的范畴,那么十有八九就是交易自身解决太慢了。前面通过进一步的排查最终定位是某个 SQL 语句和外部的一些处理不当导致的交易迟缓。修改后,TPS 失常,最初把线程池最大大小参数、DB 连接池的参数都往回调成最佳实际中举荐的值,再次压测后,TPS 仍然放弃失常程度,问题失去最终解决。

图 24 内存填满了业务畛域对象

这个案例一虽说是因为 CPU 冲高且交易继续迟缓的这一类典型问题,但其实就这个案例所述的那样,在定位和调优的时候很容易陷进一种治标不治本的窘境,很容易被一些表象所蛊惑。如何拨开云雾见月明,笔者的认识是 5 分看教训,1 分看灵感和运气,还有 4 分得靠一直剖析。如果没教训怎么办?那就只能沉下心来剖析相干性能文件,无论是线程转储文件还是 JFR,又或者其余采集工具采集到性能信息,反正不要放过任何蛛丝马迹,最初切实没辙了再申请经验丰富的专家的帮助排查解决。

  • 应用 JMC+JFR 定位问题

如果超长问题偶尔产生,这里介绍一个比较简单且十分实用的办法,应用 JMC+JFR,能够参考链接进行应用。然而应用前必须开启 JMX 和 JFR 个性,须要在启动批改启动参数,具体参数如下,该参数不要带入生产,另外如果将容器所属宿主机的端口也裸露成跟 jmxremote.port 一样的端口,如下示例为 32433,那么还能够应用 JConsole 或者 JVisualvm 工具实时察看虚拟机的情况,这里不再做具体介绍。

-Dcom.sun.management.jmxremote.port=32433

-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote.

authenticate=false

-XX:+UnlockCommercialFeatures -XX:+FlightRecorder

上面以一个理论的 JFR 实例为例。

首先要开启 JMX 和 JFR 性能,须要在启动参数中加 JMX 开启参数和 JFR 开启参数,如下面所述,而后在容器外面执行下述命令,执行后显示“Started recording pid. The result will be written to xxxx”,即示意曾经开始录制,这个时候开始进行压测,下述命令中的 duration 是 90 秒,也就示意会录制 90S 后才会进行录制,录制完后将文件下载到本地,用 jmc 工具进行剖析,如果没有这个工具,也能够应用 IDEA 进行剖析。

jcmd pid JFR.start name=test duration=90s filename=output.jfr

通过剖析火焰图,具体怎么看火焰图请参考链接。通过这个图能够看到次要的耗时是在哪个办法下面,给咱们剖析问题提供了很大的便当。

还能够查看 call tree,也能看出耗时次要产生在哪里。

JMC 工具下载地址:JDK Mission Control (JMC) 8 Downloads (oracle.com)

最初再介绍一款工具,阿里巴巴开源的 arthas,也是性能剖析和定位的一把利器,具体应用就不在这里介绍了,能够参考 arthas 官网。

  • 如何定位 CPU 耗时过高的线程及办法

首先找到 JAVA 过程的 PID,而后执行 top -H -p pid,这样能够找到最耗时的线程,如下图所示。而后应用 printf “%x\n” 17880,将线程号转成 16 进制,最终通过这个 16 进制值去 jstack 线程转储文件中去查找是哪个线程占用 CPU 最高。

其余问题案例

这类问题在产生的时候,JVM 体现得静如止水,CPU 和内存的应用都在失常水位,然而交易就是迟缓,对于这一类问题能够参考 CPU 冲高类问题来进行解决,通过应用线程转储文件或者应用 JFR 来录制一段 JVM 运行记录。这类问题大概率的起因是因为大部分线程卡在某个 IO 或者被某个锁个 Block 住了,上面也带来一个实在的案例。

  • 案例一

某金融保险头部客户,反馈某个交易十分迟缓,常常响应工夫在 10S 以上,利用部署在私有云的容器上,容器规格为 2C4G,数据库是 OceanBase。问题每次都能重现,通过分布式链路工具只能定位到在某个服务下面慢,并不能精确定是卡在哪个办法下面。在交易迟缓期间,通过 top、vmstat 命令查看 OS 的状态,CPU 和内存资源都在失常水位。因而,须要看在交易期间的线程的状态。在交易执行迟缓期间,将交易的线程给转储进去,如图 29 所示,能够定位相应的线程卡在哪个办法下面,案例中的线程卡在了执行 socket 读数据阶段,从堆栈能够判定是卡在了读数据库下面了。如果这个办法仍然不好用,那么还能够借助抓包形式来进行定位。

图 29 交易被 hang 住示例图

  • 案例二

某金融银行客户压测过程中发现 TPS 上不去,10TPS 不到,响应工夫更是高到令人发指,在通过一段时间的培训赋能和磨合,该客户曾经具备些性能定位的能力。给反馈的信息是 SQL 执行工夫、CPU 和内存应用一切正常,客户打了一份线程转储文件,发现大多数线程都卡在了应用 RedissionLock 的分布式锁下面,如图 30 所示,后经查是客户没有正当应用分布式锁导致的问题,解决后,TPS 翻了 20 倍。

图 30 分布式锁使用不当导致的问题示例

这两个案例其实都不算简单,也很容易进行排查,放到这里只是想重述一下排查这类问题的一个整体的思路和办法。如果交易迟缓且资源应用都失常,能够通过剖析线程转储文件或者 JFR 文件来定位问题,这类问题个别是因为 IO 存在瓶颈,又或者被锁 Block 住的起因导致的。

总结

问题千千万,但只有修练了足够深厚的内功,造成一套属于本人的排查问题思路和打法,再加上一套撑持问题排查的工具,凭借已有的教训还有偶发到来的那一丝丝灵感,置信所有的问题都会迎刃而解。

更多交换,欢送进钉钉群沟通,PTS 用户交换钉钉群号:11774967。

此外,PTS 近期对售卖形式做了全新降级,根底版价格直降 50%!5W 并发价格只需 199,免去自运维压测平台懊恼!更有新用户 0.99 体验版、VPC 压测专属版,欢送大家选购!

点击此处,返回官网查看更多!

正文完
 0