项目背景
这个接口大概每天早上7点到8点有100万人使用,你安排个压测吧!
接到了这个任务,发现这个接口在容量测试过程中,发现无论并发用户数有多大,TPS无增长,响应时间随着用户数的增加而增加,服务器资源消耗较小,并在逐渐增加并发用户数的过程中,出现大量超时报错。
疑问一:为何增加并发用户数时,响应时间随之递增,TPS无增长趋势?
疑问二:为何有大量超时报错产生?
疑问三:为何并发用户数增加,服务器资源损耗不增加?
请求:GET
参数:type:0
4.2分析思路
一般遇到此类问题先看线程或者线程池,最好是可以通过链路追踪工具宏观的看下整个链路的走势。
4.3排查手段
4.3.1方法一:Jstack线程dump方式
- 查看目标进程的pid(下面任意命令都可以)
ps -ef | grep <应用信息包含的关键字符> jps # 可查看所有的java进程的pid
- 执行线程dump命令
jstack <pid> > xxx.log
- 快照的内容分析
"http-nio-18080-exec-30" #155 daemon prio=5 os_prio=0 tid=0x00007fb4356cf000 nid=0x7814 waiting for monitor entry [0x00007fb38c98e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.perf.demo.cases.Perf_Synchronized_Method.run_static(Perf_Synchronized_Method.java:34)
- waiting to lock <0x00000000ec2a9498> (a java.lang.Class for com.perf.demo.cases.Perf_Synchronized_Method)
at com.perf.demo.cases.Perf_Synchronized_Method.run(Perf_Synchronized_Method.java:26)
at com.perf.demo.controller.PerfCaseController.Synchronized_Method(PerfCaseController.java:264)
at sun.reflect.GeneratedMethodAccessor378.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
······
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
- locked <0x00000000ec9731d8> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
at com.perfma.agent.ttl.TtlRunnable.run(TtlRunnable.java:65)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
- 如何判断线程阻塞
我们所说的线程阻塞,也即java.lang.Thread.State这个字段的值为BLOCKED。所以Dump文件中存在状态BLOCKED的线程就表示当前应用中存在线程阻塞的情况。
- 如何调优
丢给开发?
NO!NO!NO!
直接丢给开发,算什么性能测试,我们需要进一步进行分析!
4.3.2方法二:Arthas反编译的方式
- 打开arthas
java -jar arthas-boot.jar
- 监控被测应用
启动的过程中,会把当前机器上的java进程打印出来,我们只需要输入[id]中的数字并按回车键,就能快速监控目标进程。
- 查看是否存在线程阻塞
dashboard -i 5000 -n 10
参数名称
参数说明
[i:]
刷新实时数据的时间间隔 (ms),默认 5000ms
[n:]
刷新实时数据的次数
- 使用thread命令查看
thread -b
- 分析阻塞原因
jad com.perf.demo.cases.Perf_Synchronized_Method
对类进行反编译,查看源码
- 调优手段
降低锁的粒度
临界区代码块或者方法长度(或复杂度)
4.4线程那点事
4.4.1线程的6种状态
- NEW
至今尚未启动的线程的状态。线程刚被创建,但尚未启动。
- RUNNABLE
可运行线程的线程状态。线程正在JVM中执行,有可能在等待操作系统中的其他资源,比如处理器。
- BLOCKED
受阻塞并且正在等待监视器的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 之后再次进入同步的块/方法。
在Thread Dump日志中通常显示为 java.lang.Thread.State: BLOCKED (on object monitor) 。
- WAITING
某一等待线程的线程状态。线程正在无期限地等待另一个线程来执行某一个特定的操作,线程因为调用下面的方法之一而处于等待状态:
- 不带超时的 Object.wait 方法,日志中显示为 java.lang.Thread.State: WAITING (on object monitor)
- 不带超时的 Thread.join 方法
- LockSupport.park 方法,日志中显示为 java.lang.Thread.State: WAITING (parking)
- TIMED_WAITING
指定了等待时间的某一等待线程的线程状态。线程正在等待另一个线程来执行某一个特定的操作,并设定了指定等待的时间,线程因为调用下面的方法之一而处于定时等待状态:
- Thread.sleep 方法
- 指定超时值的 Object.wait 方法
- 指定超时值的 Thread.join 方法
- LockSupport.parkNanos
- LockSupport.parkUntil
- TERMINATED
线程处于终止状态
4.4.2各状态转换概念图
4.4.3线程阻塞的含义
当前线程 没有条件(锁) 执行 某个方法,而被挂起(让出CPU执行权),等待获取执行条件(锁)。
从整个应用来看,原本线程应该执行业务代码并快速返回结果的,但是现在线程被挂起等待了,因此相应的我们的RT也就增加了。
版权归原作者 无可无奈 所有, 如有侵权,请联系我们删除。