一次压测经历
1.起因
最近在重构公司的画像服务,之前的画像数据存储在cassandra中,性能也很好。但是cassandra,公司没有老司机维护,所以要替换成公司自研的ekv(基于Tidb)存储。既然新的kv存储,当然要进行测试了。
2.方案
测试方案很简单,就是当用户查询数据穿透缓存,全部打到ekv上,该存储能够支撑住,过程如下图:
soa服务代码很简单,就是通过jedis连接ekv,然后查询用户信息:
public String queryUserProfile(long userId) {
long start = System.currentTimeMillis();
Jedis jedis = null;
try {
jedis = KvStoreClient.getInstance().getResource();
String userKey="1:"+userId;
String profile = jedis.get(userKey);
return profile;
} catch (IOException e) {
LOGGER.error("",e);
}finally {
if(jedis!=null) {
jedis.close();
}
LOGGER.info("total time : "+(System.currentTimeMillis()-start));
}
return null;
}
3.第一波jmeter测试
- 开启10个线程 耗时180s,测试结果,qps大概在3.58k/s。
- 开启50个线程,耗时180s,测试结果,qps大概在3.06k/s。
- 开启100个线程,耗时180s,测试结果,qps大概还是维持在3.7k/s。
查看了测试机器和服务器的各项指标,负载都很低,但是qps压不上去了。。。
3.1 排查问题
3.1.1 soa服务排查
首先想到的是代码质量是否有问题,检测了一下jedis的连接池的配置,确认配置上 是没有问题的:
JedisPoolConfig config = new JedisPoolConfig();
config.setMinIdle(50);
config.setMaxIdle(200);
config.setMaxTotal(500);
config.setBlockWhenExhausted(false);
config.setMaxWaitMillis(1000 * 3);
config.setTestOnBorrow(false);
config.setTestOnReturn(false);
config.setTestWhileIdle(false);
再检查了一下SOA的配置是否有限流等配置,发现线程池的连接数默认为50,于是配置到100。重新测试一下,发现qps还是上不去,到此基本上可以确认soa服务,没啥问题,不过最后还是做了一个空接口的qps查询,果然qps能够轻松上万。
3.1.2 ekv是否存在性能瓶颈
经过上一步的排查后,我把问题聚焦在ekv这块,通过granfan上的监控,觉得每次到get操作数据能够达到7-8k/s(这个是有异常的,理论上应该和接口的qps保持一致才对,后来向ekv的开发人员反映后,才知道监控打点配置成了double)
,是否通过jedis获取这块存在性能瓶颈,于是在代码里面模拟用户查询,代码如下:
public String stressTest(int threads, long second, long userId) {
ExecutorService poolExecutor = Executors.newFixedThreadPool(threads);
for (int i=0;i<threads;i++){
userId=userId+100000;
poolExecutor.submit(new TestThread(userId));
}
try {
Thread.sleep(second*1000);
poolExecutor.shutdownNow();
} catch (Exception ex) {
LOGGER.warn("",ex);
}
String message="success:"+this.successCount.intValue()
+",failure:"+this.failureCount.intValue();
return message;
}
private class TestThread implements Runnable{
private long userId;
public TestThread(long userId){
this.userId = userId;
}
@Override
public void run() {
while (true){
this.userId++;
String ret=queryUserProfile(this.userId);
if (ret==null){
failureCount.incrementAndGet();
}else {
successCount.incrementAndGet();
}
}
}
}
测试后,get的qps查询能够达到100多k,立马懵逼。。。
3.1.3 连接数检测
通过netstat这个工具,查看各个服务器的连接数是否都达到有效值,测试机器开启100个连接数,服务端能够全部监听到,但是到ekv的时候,只有30多,然后怎么也上不去了。
netstat -anp | grep -i est
到此总算找到了点眉目,100个连接,到达ekv只有30多个连接。说明jedis在处理get请求是,30个线程就能处理100个连接,也就是说测试环境和服务之间网络传输有延迟。
soa服务器网络输入1.5Gps,输出只有200Mbps。也就是说单台机器的的性能瓶颈是在网卡输出上。
结合我们的业务场景用户画像服务,每个用户的标签信息有多又少,多的10几k数据,少的小于1k,我们固定用户id,找了个小于1k的返回内容,果然qps突破3k到达6k,然后我把返回内容设置为固定的“aaa”返回信息,qps大概维持在8k左右。后续就再压不上去了,终于找到我们的性能瓶颈。
不过这次测试并没有测试到ekv的性能瓶颈。。。后续会通过集群的方式对ekv进行压测。
4.总结
复盘整个压测过程,我们一开始只考虑到CPU和内存的使用情况,没有仔细查看网络负载情况,主要对网络负载这块不是很了解。若直接将几张监控报表对比一下,应该很快就能发现问题的。排查问题就先从CPU、内存和网路IO这三个方向查看,就能很快定位瓶颈。