性能问题汇总
1. 压测端硬件、网络或软件
问题1:域名压测导致大量请求流向外网,并出现流量清洗
现象:测试结果显示tps非常低,请求量压测端统计与服务端统计相差很大
解决思路:
确认压测域名是否走内网IP,ping + 压测域名获取到的ip地址与运维确认是否为内网ip,若不支持ping(腾讯云机器不支持ping)可尝试tracert。
确定未走内网可能需要运维协助,在入口机器(nginx)配置host将域名指定到某台或多台服务器上。
问题2:jmeter测试报告显示出现各种异常报错信息,如500、502、Non Http responsecode
现象:控制台错误请求量增加、测试报告显示相应错误提示、错误
解决思路:确定错误类型,根据错误类型寻找错误真实原因。
Non HTTP response code: java.net.ConnectException,java.net.ConnectException,一般是连接超时(查日志)。从几个方面排查,脚本问题、网络、服务器承受能力到了极限(监控系统资源),并发量超过系统处理能力会出现这种情况(可参考http://confluence.sui.work/pages/viewpage.action?pageId=13182064)
问题3:jmeter Jvm分配内存不足导致内存溢出
现象:控制台出现报错,出现OutOfMemoryError
解决思路:
Windows中对应的文件路径:Jmeter_Home/bin/jmeter.bat
set HEAP=-Xms512m -Xmx512m
set NEW=-XX:NewSize=128m -XX:MaxNewSize=128m
Linux下对应文件路径:Jmeter_Home/bin/jmeter
HEAP=-Xms512m -Xmx512m
NEW=-XX:NewSize=128m -XX:MaxNewSize=128m
通常就meter默认的HEAP -Xms512:初始化内存大小,-Xmx512m:最大堆内存。需要增加内存的时候需要注意
- 一般会将-Xms和-Xmx两个值配置为相同值,目的是为了能在java的GC完成后堆内存不需要重新分隔计算堆区大小而浪费资源
- -Xms和-Xmx两个值修改的值一般需要为512的整数倍
- -Xmx不要超过物理内存的50%,超出可能会导致jmeter变慢
- 当脚本执行过程中出现内存溢出outfmenmory错误,先尝试增加增加HEAP的-Xms和-Xmx
- JDK32位的电脑Xmx不能超过1500m,最大1378m.否则在启动Jmeter时会报错
- -XX:NewSize:新生代初始内存大小,该值一定要小于—Xms
- -XX:MaxNewSize:新生代可被分配的内存的最大上限,这个值应该小于-Xmx的值,因为新生代占内存来自整个堆内存通常设置为-Xmx的三分之一
- jvm在执行GC时,会停止工作。MaxnewSize的增大,可以降低GC频率
问题4: 端口被占用
现象:并发6000次/s,错误率高达66.89%
错误日志:Non HTTP response code:java.net.BindException,Non HTTP response message: Address already in use
原因:Jmeter windows压测环境:Windows Server 缺失MaxUserPort和TcpTimedWaitDelay;限制了tcpip最大连接数和响应时间
解决办法:注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/ Services/TCPIP/Parameters;添加MaxUserPort和TcpTimedWaitDelay,分别设置值为65534、30,以增大可分配的tcp连接端口数、减小处于TIME_WAIT状态的连接的生存时间
2. 服务器硬件、网络
问题1:压测方式使用的是域名压测,走的外部网络,所以压测压力未能如预期一样对目标服务器施压,导致TPS非常低,服务器各种资源消耗也很小。
现象:使用单台压测机器分别进行了100、500、1000进程压测,500个线程的时候tps只有180-200,再增加压力Tps上不去。
解决思路:一开始以为服务器连接数有问题,修改Tomcat最大连接数无效果。最后统计压测端请求量与被测服务器接收请求量相差比较大
解决方式:网络入口配置host/dns的形式将指定域名的请求全部指向测试服务器(对应IP)
问题2:原有集群到达最大极限,TPS达到280左右就出现CPU适应过高的情况
现象:2台web和4台service在调整配置、代码优化出现瓶颈,使用单台压测机器分别进行了100、500、1000进程压测,500个线程的时候tps达到280位最高并发,90%响应时间最小而且CPU等资源正常,增加并发数到1000后tps降低、响应时间增加CPU使用率>70%
解决方法:增加集群机器
3. 中间件
问题1:Nginx入策略配置不平均,后端4台服务器负载不均衡,导致压测时其中一台cpu使用率远远高于其他3台
解决:优化Nginx轮训策略,默认使用的轮训算法修改成安权重分配
问题2:Nginx入口未开启长连接,导致TPS上不去
现象:增加并发数,tps、响应时间无太大增长,服务器资源消耗都在正常范围内,TPC连接数中出现大量time-wait,tomcat已开启长连接配置,可以确定是入口长连接配置没生效
- keepalive_timeout 20s; #一个keepalive 连接被闲置以后还能保持多久打开状态
- keepalive_requests 1000; #一个客户端可以通过一个keepalive连接的请求次数。
问题3:Tomcat连接处瓶颈,导致高并发时出现接口超时
现象:500线程组并发的时候,服务端日志出现大量超时提示,排查Tomcat线程数配置的时候发现maxThreads(线程池中最大活跃线程数)为100。
解决:修改maxThreads、accept-count为500后错误解决
- maxThreads:最大请求进程数,默认设置为200,该值设置应该考虑实际情况,当
请求进程数达到最大值时,一般会出现错误提示:SEVERE: All threads (150) are currently busy, waiting. Increase maxThreads (150) or check the servlet status
- accept:可接队列长度,与maxThreads对应,当达到maxThreads后进入等待队列。而等待队列数达到最大值后,再有新请求后就会出现refuse connection(拒绝请求)
问题4:. 502 Bad Gateway和504 Gateway Time-out
问题定位:tomcat的参数配置问题
解决办法:调整tomcat配置文件server.xml的配置:主要是最大线程数、最大建立连接数和最长连接时间。
问题扩展:Nginx或腾讯云LB代理模式下后端tomcat服务器出现问题引起的。首先,检查Nginx/腾讯LB或者tomcat的配置参数;其次,检查tomcat应用服务器的内存、CPU和代码BUG导致的。服务端栗子:方法内部RPC 调用,并发大,方法可用率下降,同时调用次数也会急剧上升。系统外部服务的接入层,对接逻辑容器时,应该添加缓冲队列,最好异步。
问题5:未开启长连接
- keepAliveTimeout=300000 <!--没有请求保持长连接的时间ms -->
- maxKeepAliveRequests=50000 <!--长连接能保持的请求数 -->
问题6:dubbo参数优化
现象:随着压测并发线程数递增,tps未能如预期中同步正向增长,达到峰值出现一定的下降。在调整了Tomcat配置参数后进一步优化其他中间件配置
dobbo:protocol-服务提供者协议配置:
- name :协议名称
- port :dubbo协议默认端口为20880,如果配置为-1或没配置port,则会分配一个没被占用的端口
本次调优主要关注一下内容:
- heartbeat :心跳间隔,对于长连接当物理层断开(如拔网线,TCP的FIN消息未能及时发送,对方收不到断开事件此时就需要心跳检查连接是否断开)
- dispatcher :协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等
- threadpool : 默认fixed,线程池类型,可选:fixed/cached
- threads : 服务线程池大小,默认值为100
dubbo:reference-服务消费者引用服务配置:
本次性能优化主要关注内容
- connections :对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数
- timeout :服务方法调用超时时间(毫秒),缺省使用<dubbo:consumer>的timeout。
问题7:JVM优化
现象:TPS每隔段时间就降为0
问题定位:(1)监控tcp的连接数,等待数;(netstat -an |grep 6222 | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}')(2)查看服务端FULL GC的次数(过多,FULL GC会导致所有线程)
解决办法:JVM调优+ tomcat参数调优
1、取消飞行记录模式,去除参数(-XX:+UnlockCommercialFeatures -XX:+FlightRecorder)
2、jdk1.8 下取消了-XX:PermSize=500m-XX:MaxPermSize=500m
3、垃圾回收机制改成G1模式(堆内存被划分为固定大小的多个区域,存活的对象从一块区域转移到另一块区域,这样可以进行垃圾回收的同时不停止其他应用程序线程)
JVM的常用参数如下:
-Xms设置初始化堆的最大值
-Xmx 设置堆的最大值
PS:-Xms和-Xmx一般是相同的,可以减少Full GC的频率,最大可以到服务器总内存的80%
-Xmn 设置年轻代的大小(年老代=最大值-年轻代),一般为最大值的1/3左右。
问题8:java 启动脚本优化
4. 数据库、缓存等
问题1:DB 优化
(1)优化sql,尽可能少使用join、or 语句,select 出来的字段是必需的字段。
(2) 优化索引,让每条select 都走索引
(3) 设置连接池的最大连接数,设置为10000/14 = 700, (10000 为项目使用的mysql 最
大连接数,14 为机器数)
(4)尝试测试不同的连接池,选择性能最佳的,如图4 为数据库连接池性能测试,最
终确定选择hikari。
(5)不使用数据库事务,因为数据库操作代码都在消费者中,在代码中做幂等性。
查询一条语句性能测试(ms)
问题2:Redis优化
1、优化redis 存储数据结构
将db 中的数据load 保存为redis 的hash 结构(全表保存),根据业务优化redis 存储
结构,减少redis 查询次数(例如将phone 和券code 的领取状态单独存储)。
2、redis cpu为单核,进行分片处理
大量查询会成为严重短板,通过hash 值进行分片处理,因为项目不存在热点key 的问
题。优化过后redis 能够承受的量是之前的3 倍。
3、设置redis最大连接数
Redis 最大连接数设置为:3*10000/14 = 2100(这里乘以3 是因为微利项目有三台redis)
问题3:mq 优化
1、优化mq 消息
消息体一般为redis key,可以去redis 拿取数据,优化消息存储大小。按功能不同,拆
分多个队列,加快单逻辑处理速度,微利项目根据业务拆分为5 个队列。
2、加快消费者消费速度
增加消费者数量为20 个,根据下游(DB、业务方)TPS 多次测试得出,可以利用消费者数
量控制下游的负载。
增加消费者预读取数据数量为50 个,从而减少网络请求次数。
优化消费逻辑,完善幂等操作(解决消息重复消费问题),db 操作,业务查询操作。
问题4:Redis连接池瓶颈
优化后的配置:
redis.pool.maxTotal=5000
redis.pool.maxIdle=100
redis.pool.minIdle=50
关键参数说明:
maxTotal:资源池中最大连接池,默认值8
- 最大连接数的配置需要结合实际情况进行调整,而考虑的关键因素包括:
- 业务要求Redis达到的并发量
- 客户端执行命令时间
- Redis资源:如应用个数*maxTotal不能超过Redis的最大连接数
- 资源开销:控制空闲连接,尽量使连接池的频繁创建、释放造成不必要的开销
例如:
假如一个命令的平均耗时是1ms,一个连接的QPS(Redis每秒执行指令数)大约1000,而业务要求QPS(每秒执行请求数)是50000。
理论上资源池最大连接池设置应该为50000/1000=50个,但是实际设置上可以比理论值稍大。需要注意的是该值不是越大越好,一是连接太多需要占用客户端和服务端更多资源另一个是Redis假如出现阻塞资源池再大也没作用。
maxIdle:资源池允许最大空闲的连接数,默认值8
minIdle:资源池确保最少空闲的连接数,默认值0
- maxIdle:实际上才是业务需要的最大连接数,该值不能过大(不能大于maxTotal)或过小(过小会导致新增Redis连接开销),而minIdle是为了控制空闲资源监测
备注:以上均参考自https://yq.aliyun.com/articles/236383
问题5:DataSource参数优化
spring.datasource.initialSize=10
spring.datasource.minIdle=10
spring.datasource.maxActive=200
spring.datasource.maxWait=60000
- initialSize,连接初始值,连接池启动时创建的连接数量的初始值
- maxActive,连接池的最大值,同一时间可以从池分配的最多连接数量,0时无限制
- maxIdle,最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止,0时无限制
- minIdle,最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请
问题6:领券接口gainTPS 2W/s达到瓶颈
问题现象:6台tomcat服务器+3台redis-cache+2台redis-MQ的TPS仅有2W/s,和理论值6k(单台tomcat的最大值)有差距。
问题定位:(1)增加一台redis-cache,TPS没有改观,初步断定redis-cache存在瓶颈。(2)代码分析:发现gain接口里面有大量的increment操作;热点key避免产生瓶颈落在不同的redis上,但increment时所有的机器都来竞争操作这个key,造成瓶颈。(3)查看redis的监控,发现4台redis-cache中有1台的CPU达到100%,其他3台都在80~90%,形成木桶效应。
解决办法:increment主要是为了实时统计领券量,并显示在看板上方便监控数据。非核心业务,可以去掉increment操作,改成通过elk日志统计。
问题7:热点key达到瓶颈
典型的瓶颈问题:并发量大时,操作同一个key(热点key),hash的方式可能会落在同一台redis服务器,达到单台redis服务器的瓶颈。
存在问题的场景:理财市场登录预热(除夕红包活动开始前把所有开户信息加载到缓存中)
解决办法:对热点key进行加工和计算。理想的情况下,集群中有N个缓存节点,那么加工后的key也应该有N个。
问题8:Jms消费慢
问题现象:压测gain接口,jms消息有堆积
优化点:1、redis用的公共组件,有加锁;2、gain接口会产生两台msg消息,分别丢进两条队列(一条入库,一条调腾讯接口),同步改成异步;(3)一个线程取消息多个线程消费-》改成多个线程取消息多个线程消费
问题9:spdata分发有延时
问题现象:云上的数据向IDC推送数据用到了JMS消息,并发量大,容易延时(已知问题)。
影响范围:理财仅响应运营系统发奖,卡牛和微粒影响了云上的兑换,导致用户体验不好。
解决办法:spdata分发的方案不变,再增加方案:领取时前端已加密串形式保存QQ用户信息,给兑换奖券时用。
问题10:日志优化(通过日志分析不出来)
1、异步写日志文件
2、减少日志打印,例如正常请求仅打印入参和出参
3、kafka+ELK
问题11:spdatad多业务方数据源问题,导致预演并发量时,卡牛和微粒的数据未下发,不能兑换奖券(线上问题)
问题原因:for循环按业务下推数据,每次先推动理财的;如果理财有下推数据,最后会clear掉dataSource,导致其他业务查询为空;如果理财未下推数据,不会调用clear,正常推送其他业务;
代码如下:
解决办法:dataSource从上文传下来,避免执行clear时,dataSource被清空
规避方法:增加多业务的性能测试场景(跨公司的压测资源和环境受限);
问题12. 业务繁忙(线上问题)
问题原因:
(1)网络原因,例如:静态资源文件未放运营商CDN服务器、网络慢或异常;
(2)receive_api未获取QQ:openid(小概率事件),服务端重新请求腾讯接口获取QQ用户openid,生成数字验签错误;
(3)腾讯云问题,联合腾讯压测初步解决。