针对自建存储服务的回调接口调优时,qps一直卡在1w,但是从各种监控和分析并未找到性能瓶颈,最后发现竟然因为数据库cpu监控不准备误导,特此记录整个调优的过程
一、源码分析
回调接口源码性能分析:
- 1次redis查询
- 2次db操作,其中1次查询,1次写入操作
- 5次本地缓存读取
- 1次外部接口调用
其中最大的性能瓶颈很有可能在db操作中
二、资源配置
| 服务 | 配置 |
|---|---|
| mysql | 一主一从 16c 128g |
| redis | 16g 8节点 |
| callback服务 | 2c 4g |
三、试验分析
接下里根据并发数和callback节点为唯一变量进行压测验证
| 并发数 | qps | callback节点 | avg rt/ms | 95 rt/ms | 99 rt/ms | callback cpu |
|---|---|---|---|---|---|---|
| 5 * 10 | 1922 | 1 | 28.83 | 57 | 83 | 190% |
| 5 * 10 | 4103 | 2 | 11.89 | 27 | 48 | 180% |
| 10 * 10 | 4060 | 2 | 23.28 | 63 | 105 | 170% |
| 10 * 10 | 6028 | 3 | 13.94 | 42 | 77 | 180% |
| 15 * 10 | 6060 | 3 | 21.15 | 60 | 94 | 170% |
| 15 * 10 | 7560 | 4 | 15.87 | 52 | 94 | 160% |
| 20 * 10 | 7830 | 4 | 21.27 | 64 | 111 | 170% |
| 20 * 10 | 9860 | 5 | 16.08 | 51 | 89 | 170% |
| 25 * 10 | 9960 | 5 | 19.63 | 62 | 121 | 170% |
| 25 * 10 | 9790 | 6 | - | - | - | 170% |
| 30 * 10 | 10700 | 6 | 21.35 | 68 | 115 | 170% |
| 40 * 10 | 11170 | 6 | - | - | - | 180% |
| 40 * 10 | 10170 | 10 | 24.04 | 65 | 116 | 100% |
| 40 * 20 | 10700 | 10 | 41.78 | 109 | 142 | 100% |
综上数据可以发现,1个callback节点提供2000qps,5个节点后继续增加节点qps保持在1w
四、影响因素
前面在源码分析中列举了回调接口中影响的因素:
- redis查询
- db操作
- 本地缓存读取
- 外部接口调用
再次通过唯一变量排除的方式,在代码中注释掉其中的处理逻辑
| 因子 | qps |
|---|---|
| redis查询 | 1w |
| db操作 | 2.5w |
| 外部接口调用 | 1w |
ps: 由于多处读取本地缓存信息,如果变更如果修改的范围较大所以本地缓存读取没有试验
从这个信息上面可以排除redis查询、外部接口调用并不是性能的制约点,但是mysql是一个可能的点,但并没有证实mysql就是qps上不去原因
五、数据库监控
既然mysql是目前分析到的一个可疑点,那么我们进一步针对数据库进行分析
5.1 数据库cpu
首先查看数据库的cpu监控
数据库部署的服务在16c的机器上,限制8c,从数据库的的cpu监控来看,cpu最高4c,仅占50%,远没达到瓶颈
5.2 Query Time
由于慢查询slow query设置的时间是0,所以慢查询的数据意义不大,那么看看那select和insert的sql在数据库中的处理时间
从监控中显示,insert Query Time平均时长6ms,select Query Time平均时长289us, 从试验中最后一组数据中,接口的平均响应时间art 41.78ms来看,其实6.289ms(6ms+289us)仅占15%的时间
六、客户端
不能通过监控直接分析出问题,接下来验证确认是否是因为发起压力客户端的问题导致,避免打脸
6.1 客户端资源监控
通过
dstat监控可以看到,客户端的cpu、内存、带宽资源均未出现瓶颈
6.2 增加客户端
为排除是否是因为连接数等其他潜在因素的影响,客户端从20个增加到40个、60个,在总并发数不变或者倍增的情况下,qps依旧是1w左右,那是否是除了节点数的其他因素的影响呢?
6.3 influxdb升配
由于客户端数据上报依赖到influxdb,而且数据上报是同步的处理方式,故查看influxdb的监控发现其实influxdb的cpu使用率已经达到90%左右,但是将influxdb从4c升配为8c后,再次进行压测,influxdb的cpu仅占52%不到,但是此时qps仍然是1w左右
6.4 增加额外的压力
其实单纯从监控只能简单排除并非是cpu跑满导致,那需要设计实验的方案排查是否是客户端的原因
设计实验的方案:
- 客户端提供20*10并发,运行时间2min左右,查看qps曲线
- 额外系统提供50并发,此时客户端提供的压力持续,运行时间2min左右,查看qps曲线
- 停止额外系统提供的并发压力,客户端提供的压力持续,运行时间2min左右,查看qps曲线
根据以上方案实验的结果如下:
从这个实验结果可以肯定的排除了客户端发起压力的影响
七、线程锁
查看服务端是否有线程锁信息导致qps上不去
其实callback服务节点数从5个增加到10个qps依旧是1w,基本可以排除是因为线程锁等原因引起,但是可以抓取线程数据再次确认一下
7.1 获取线程快照
jstack 1 > 1.txt
具体的【调优工具】线程快照分析 ,使用jca工具打开
7.2 线程状态
线程状态基本在
Runnable和Waiting on condition
7.3 Waiting on condition
具体查看Waiting on condition的业务处理
大部分是在tomcat的poll
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:89)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
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)
少部分在druid的getConnectionDirect
并无明显的异常可分析
八、火焰图
那既然并非是发起压力的客户端的问题,而且通过数据库的监控并不能分析出问题所在,那么看看服务端的堆栈信息分析接口处理时间消耗在哪里
使用arthas profiler抓取堆栈信息或者使用perf抓取数据,打开文件查看火焰图
因为updateOrSaveFileInfo中首先执行select sql查询判断后再执行insert sql写入数据,故从火焰图中可以发现业务处理基本消耗在两个数据库的操作上
九、链路分析
9.1 方法入口
执行命令
trace com.lluozh.llz.controller.CallbackController handleLluozhCallback '#cost > 10' -n 10
获取的结果
`---ts=2021-05-21 20:32:17;thread_name=http-nio-8080-exec-2836;id=132e;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2b9aeedb
`---[35.16514ms] com.lluozh.llz.service.callback.impl.CallbackServiceImpl:updateOrSaveFileInfo()
+---[0.005561ms] com.lluozh.llz.model.po.FileInfoPo:getAppId() #88
+---[0.003188ms] com.lluozh.llz.model.po.FileInfoPo:getFileKey() #88
+---[21.122075ms] com.lluozh.llz.daoService.FileInfoService:existFileInfo() #88
`---[14.001955ms] com.lluozh.llz.daoService.FileInfoService:addFileInfo() #101
与火焰图抓取的结果基本一致
9.2 逐步向下获取
从上面的堆栈信息,逐步向下获取,接下来获取insert的sql操作
trace com.lluozh.llz.daoService.FileInfoService addFileInfo '#cost > 10' -n 10
获取的结果
`---ts=2021-05-21 20:45:31;thread_name=http-nio-8080-exec-457;id=32c;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@49ced9c7
`---[21.410149ms] com.sun.proxy.$Proxy135:addFileInfo()
`---[21.38085ms] com.sun.proxy.$Proxy134:addFileInfo()
从这个堆栈信息中发现动态代理com.sun.proxy,而且时间基本全部在处理这个逻辑
9.3 动态代理的追踪
使用了动态代理,那怎么继续往下去获取更加细节的堆栈信息呢?
ps: 一开始捞druid的源码,发现都并没有很好的找到全部的调用方法链路,其实看火焰图分析是可以找到代理底层的所有调用方法
继续看看上面抓取的火焰图,可以发现com.sun.proxy背后执行的逻辑
从火焰图中可以看出主要执行的druid方法为
com/alibaba/druid/pool/DruidDataSource.getConnection
com/alibaba/druid/pool/DruidPooledConnection.prepareStatement
com/alibaba/druid/pool/DruidPooledPreparedStatement.execute
9.4 druid执行trace
那接下来trace一下druid的执行
- getConnection
执行命令
trace com.alibaba.druid.pool.DruidDataSource getConnection '#cost > 10' -n 10
获取的结果
`---ts=2021-05-21 20:57:22;thread_name=http-nio-8080-exec-584;id=425;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@49ced9c7
`---[19.703046ms] com.alibaba.druid.pool.DruidDataSource:getConnection()
`---[19.686816ms] com.alibaba.druid.pool.DruidDataSource:getConnection() #109
`---[19.678128ms] com.alibaba.druid.pool.DruidDataSource:getConnection()
`---[19.646259ms] com.alibaba.druid.pool.DruidDataSource:getConnection() #1296
`---[19.634726ms] com.alibaba.druid.pool.DruidDataSource:getConnection()
+---[0.003328ms] com.alibaba.druid.pool.DruidDataSource:init() #1300
`---[19.564124ms] com.alibaba.druid.pool.DruidDataSource:getConnectionDirect() #1306
抓取的是过滤后的时间较长的逻辑,从上面的火焰图可以看到,其实getConnection的处理时间(服务端执行数据库insert逻辑时间总占比18.48%,getConnection仅占1.07%)仅占整个数据库操作时间的一小部分,说明时间并没有大量消耗在获取连接上
ps: 在性能调优服务端的数据库连接数配置时,可以先trace获取getConnection耗时比重,方便有针对性的调优
- prepareStatement
执行命令
trace com.alibaba.druid.pool.DruidPooledConnection prepareStatement '#cost > 10' -n 10
获取的结果
[arthas@1]$ `---ts=2021-05-21 21:01:01;thread_name=http-nio-8080-exec-633;id=456;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@49ced9c7
`---[29.744699ms] com.alibaba.druid.pool.DruidPooledConnection:prepareStatement()
+---[0.004268ms] com.alibaba.druid.pool.DruidPooledConnection:checkState() #335
+---[0.004607ms] com.alibaba.druid.pool.DruidPooledConnection:getCatalog() #338
+---[0.004426ms] com.alibaba.druid.pool.DruidPooledPreparedStatement$PreparedStatementKey:<init>() #338
+---[0.003182ms] com.alibaba.druid.pool.DruidConnectionHolder:isPoolPreparedStatements() #340
+---[0.004073ms] com.alibaba.druid.pool.PreparedStatementHolder:<init>() #348
+---[0.003288ms] com.alibaba.druid.pool.DruidConnectionHolder:getDataSource() #349
+---[0.003013ms] com.alibaba.druid.pool.DruidAbstractDataSource:incrementPreparedStatementCount() #349
+---[0.00486ms] com.alibaba.druid.pool.DruidPooledConnection:initStatement() #355
+---[0.005498ms] com.alibaba.druid.pool.DruidPooledPreparedStatement:<init>() #357
`---[0.003386ms] com.alibaba.druid.pool.DruidConnectionHolder:addTrace() #359
druid源码
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
checkState();
PreparedStatementHolder stmtHolder = null;
PreparedStatementKey key = new PreparedStatementKey(sql, getCatalog(), MethodType.M1);
boolean poolPreparedStatements = holder.isPoolPreparedStatements();
if (poolPreparedStatements) {
stmtHolder = holder.getStatementPool().get(key);
}
if (stmtHolder == null) {
try {
stmtHolder = new PreparedStatementHolder(key, conn.prepareStatement(sql));
holder.getDataSource().incrementPreparedStatementCount();
} catch (SQLException ex) {
handleException(ex, sql);
}
}
initStatement(stmtHolder);
DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
holder.addTrace(rtnVal);
return rtnVal;
}
通过堆栈信息和源码可知,时间消耗在prepareStatement回调耗时中,那再看看火焰图中这块代码的调用逻辑
其实底层调用的均是mysql的原生处理,尝试继续trace其中底层的方法
trace com.mysql.cj.jdbc.ClientPreparedStatement getInstance '#cost > 10' -n 10
获取的结果
`---ts=2021-05-21 21:03:43;thread_name=http-nio-8080-exec-697;id=497;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@49ced9c7
`---[16.192273ms] com.mysql.cj.jdbc.ClientPreparedStatement:getInstance()
`---[16.181172ms] com.mysql.cj.jdbc.ClientPreparedStatement:<init>() #134
目前暂时无法如何继续往下分析
- execute
执行命令
trace com.alibaba.druid.pool.DruidPooledPreparedStatement execute '#cost > 10' -n 10
获取的结果
`---ts=2021-05-21 21:15:27;thread_name=http-nio-8080-exec-590;id=42b;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@49ced9c7
`---[32.167451ms] com.alibaba.druid.pool.DruidPooledPreparedStatement:execute()
+---[0.002757ms] com.alibaba.druid.pool.DruidPooledPreparedStatement:checkOpen() #488
+---[0.002721ms] com.alibaba.druid.pool.DruidPooledPreparedStatement:incrementExecuteCount() #490
+---[0.00353ms] com.alibaba.druid.pool.DruidPooledPreparedStatement:transactionRecord() #491
+---[0.002565ms] com.alibaba.druid.pool.DruidPooledConnection:beforeExecute() #495
`---[0.003494ms] com.alibaba.druid.pool.DruidPooledConnection:afterExecute() #503
druid源码
@Override
public boolean execute() throws SQLException {
checkOpen();
incrementExecuteCount();
transactionRecord(sql);
// oracleSetRowPrefetch();
conn.beforeExecute();
try {
return stmt.execute();
} catch (Throwable t) {
errorCheck(t);
throw checkException(t);
} finally {
conn.afterExecute();
}
}
可以看出mysql操作主要是在stmt.execute() ,而不是其他的checkOpen()等额外处理
9.5 不同压力下的trace结果
对比分析服务在高压力和正常情况下的trace结果
- 低压力下trace
[arthas@1]$ trace com.lluozh.cstore.controller.CallbackController handleLluozhCallback -n 100
Press Q or Ctrl+C to abort.
Affect(class count: 2 , method count: 2) cost in 387 ms, listenerId: 8
`---ts=2021-05-21 16:53:18;thread_name=http-nio-8080-exec-2263;id=fb7;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2b9aeedb
`---[14.628907ms] com.lluozh.cstore.controller.CallbackController$$EnhancerBySpringCGLIB$$e624886:handleLluozhCallback()
`---[14.378576ms] org.springframework.cglib.proxy.MethodInterceptor:intercept() #-1
`---[11.086846ms] com.lluozh.cstore.controller.CallbackController:handleLluozhCallback()
+---[0.003396ms] org.apache.commons.lang3.StringUtils:isBlank() #312
+---[0.003682ms] javax.servlet.http.HttpServletRequest:getAttribute() #315
+---[0.00307ms] org.apache.commons.lang3.StringUtils:isBlank() #316
+---[0.00373ms] org.slf4j.Logger:info() #319
+---[0.003959ms] javax.servlet.http.HttpServletRequest:getHeader() #322
+---[0.002916ms] org.apache.commons.lang3.StringUtils:isBlank() #323
+---[0.003681ms] javax.servlet.http.HttpServletRequest:getRequestURI() #324
+---[0.003218ms] javax.servlet.http.HttpServletRequest:getQueryString() #325
+---[0.003068ms] org.apache.commons.lang3.StringUtils:isBlank() #326
+---[0.003989ms] org.apache.commons.lang3.StringUtils:isEmpty() #339
+---[0.003264ms] org.apache.commons.lang3.StringUtils:isEmpty() #343
+---[0.003291ms] org.apache.commons.lang3.StringUtils:isEmpty() #346
+---[0.003914ms] org.apache.commons.lang3.StringUtils:isEmpty() #350
+---[0.004014ms] org.apache.commons.lang3.StringUtils:isEmpty() #353
+---[1.73458ms] com.lluozh.cstore.component.CacheService:getAppConfigCommonFromLocalCache() #357
+---[0.003084ms] org.apache.commons.lang3.StringUtils:isNotEmpty() #362
+---[2.699534ms] com.lluozh.cstore.component.CacheService:getBucketInfoFromLocalCache() #366
+---[1.111017ms] com.lluozh.cstore.service.AbstractStoreService:getDownloadUrl() #370
+---[0.00313ms] org.slf4j.Logger:info() #371
+---[1.626661ms] com.lluozh.cstore.component.CacheService:getSessionInfoFromRedis() #373
+---[0.002987ms] com.lluozh.cstore.dto.response.store.UploadSessionInfoDto:getExpireDays() #374
+---[0.002685ms] com.lluozh.cstore.model.po.BucketInfoPo:getServiceProviderId() #379
+---[2.673879ms] com.lluozh.cstore.service.callback.CallbackService:updateOrSaveFileInfo() #380
+---[0.08924ms] com.lluozh.cstore.model.po.BucketInfoPo:getServiceProviderId() #384
+---[0.004951ms] com.lluozh.cstore.model.dto.StoreFileEventDto:<init>() #384
+---[0.004444ms] com.lluozh.cstore.component.FridayService:storeFileEvent() #384
+---[0.003681ms] com.google.common.collect.Lists:newArrayList() #387
+---[0.009182ms] com.lluozh.cstore.controller.CallbackController:processReturnBodyFields() #392
+---[0.003904ms] org.slf4j.Logger:info() #393
+---[0.003003ms] com.lluozh.cstore.dto.response.store.UploadSessionInfoDto:getExpireDays() #398
`---[0.003562ms] com.lluozh.cstore.constants.ApiResult:success() #412
- 高压力下trace
`---ts=2021-05-21 16:55:53;thread_name=http-nio-8080-exec-2477;id=1117;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2b9aeedb
`---[5.209409ms] com.lluozh.cstore.controller.CallbackController:handleLluozhCallback()
+---[0.003767ms] org.apache.commons.lang3.StringUtils:isBlank() #312
+---[0.004069ms] javax.servlet.http.HttpServletRequest:getAttribute() #315
+---[0.003483ms] org.apache.commons.lang3.StringUtils:isBlank() #316
+---[0.004073ms] org.slf4j.Logger:info() #319
+---[0.004323ms] javax.servlet.http.HttpServletRequest:getHeader() #322
+---[0.003516ms] org.apache.commons.lang3.StringUtils:isBlank() #323
+---[0.003849ms] javax.servlet.http.HttpServletRequest:getRequestURI() #324
+---[0.003775ms] javax.servlet.http.HttpServletRequest:getQueryString() #325
+---[0.003475ms] org.apache.commons.lang3.StringUtils:isBlank() #326
+---[0.003478ms] org.apache.commons.lang3.StringUtils:isEmpty() #339
+---[0.003234ms] org.apache.commons.lang3.StringUtils:isEmpty() #343
+---[0.003204ms] org.apache.commons.lang3.StringUtils:isEmpty() #346
+---[0.003379ms] org.apache.commons.lang3.StringUtils:isEmpty() #350
+---[0.003201ms] org.apache.commons.lang3.StringUtils:isEmpty() #353
+---[0.006058ms] com.lluozh.cstore.component.CacheService:getAppConfigCommonFromLocalCache() #357
+---[0.003683ms] org.apache.commons.lang3.StringUtils:isNotEmpty() #362
+---[0.004469ms] com.lluozh.cstore.component.CacheService:getBucketInfoFromLocalCache() #366
+---[0.012478ms] com.lluozh.cstore.service.AbstractStoreService:getDownloadUrl() #370
+---[0.00316ms] org.slf4j.Logger:info() #371
+---[0.482134ms] com.lluozh.cstore.component.CacheService:getSessionInfoFromRedis() #373
+---[0.004054ms] com.lluozh.cstore.dto.response.store.UploadSessionInfoDto:getExpireDays() #374
+---[0.003648ms] com.lluozh.cstore.model.po.BucketInfoPo:getServiceProviderId() #379
+---[4.027026ms] com.lluozh.cstore.service.callback.CallbackService:updateOrSaveFileInfo() #380
+---[0.197707ms] com.lluozh.cstore.model.po.BucketInfoPo:getServiceProviderId() #384
+---[0.005363ms] com.lluozh.cstore.model.dto.StoreFileEventDto:<init>() #384
+---[0.004561ms] com.lluozh.cstore.component.FridayService:storeFileEvent() #384
+---[0.028581ms] com.google.common.collect.Lists:newArrayList() #387
+---[0.006182ms] com.lluozh.cstore.controller.CallbackController:processReturnBodyFields() #392
+---[0.003908ms] org.slf4j.Logger:info() #393
+---[0.0034ms] com.lluozh.cstore.dto.response.store.UploadSessionInfoDto:getExpireDays() #398
`---[0.004344ms] com.lluozh.cstore.constants.ApiResult:success() #412
对比并未发现异常情况
十、抓包分析
既然从堆栈信息中定位到时间都消耗在执行操作中,但是这个时间无法进行统计,所以无法判断是否和前面获取到的数据库mysql执行的平均时间一致
不同组件间大部分扯不清的问题均可抓包处理,这个是组件们澄清自己甩锅的最好方式
抓取服务端中mysql的数据包,具体的方法可查看【网络协议】mysql协议数据包分析
通过抓包聚合的响应时间曲线
虽然没有一段时间的平均值,但是这个数据可以大概看到服务端收发数据包的时间和mysql显示的平均响应时间还是有些差距的
ps: 图片中含有1/3的select操作,1/3的insert操作,1/3的transaction_read_only操作
十一、数据库分析
那数据库端还有哪些情况导致出现这样的情况呢?这次直接找了dba登录线上数据库查看数据库的资源信息
11.1 top数据库
在压测时,top获取线上数据库的cpu竟然是8c全部占满,800%,什么情况?与提供的监控cpu数据完全不一致
11.2 Threads Activity
以下是运行时的MySQL的Threads Activity数据
此时正在处理sql的线程数达到接近300,根据dba解释此时8c数据库活跃处理sql的线程数接近300,说明数据库已经在满负荷处理(具体数据库核数和Peak Threads Running的关系暂时没找到合理的数据,接下来可以研究一下)
那从数据库端直接top以及Threads Activity都可以确定此时数据库已经满负荷,在确认为什么数据库监控不准确的问题前,我们先验证一下是否是因为数据库的原因导致
11.3 数据库升配
将数据库核数限制从8c升级成12c,同样的并发数再次压测
qps 从1w 变成1.5w
此时mysql的cpu也从800%变成接近1200%
十二、数据库监控的问题
回到数据库监控为什么不准确的问题上,为什么从监控中获得的数据和实际使用率相比差不多一半左右
12.1 grafana看板配置
查看看板中数据库cpu获取的配置
12.2 container_cpu_usage_seconds_total
那这个表达式namespace_pod_name_container_name:container_cpu_usage_seconds_total代表什么意思呢?
取前5min内的平均cpu数据展示为该节点的cpu数据
因为压测时基本上压测时长是2-3min左右,而且每次压测完都会间隔2-3min,这样就造成获取到的数据基本上是真实数据的一半的情况
12.3 修改看板配置
修改看板的配置,将获取前5min内的平均cpu数据修改成前1min内的平均cpu数据
这样虽然数据并不是实时的数据,但是数据获取和展示相对来说正确一些