Redis错误使用导致并发上不去
前言
抛出问题
用户发送http请求,并发总是上不去,JMeter100个压测都需要十几秒,再多点会报告redis连接超时
接口说明
接口功能仅为接收参数丢入MQ中,拦截器会去拦截记录请求
优化过程
最小化处理
修改MQ后续业务逻辑,设为sleep1秒
打印处理时间
将执行接口相关代码加入处理时间打印
eg.
long t3=System.currentTimeMillis();
JSONObject beansObject = JSONObject.parseObject(tokenList.get("beanList"));
long t4=System.currentTimeMillis();
单个处理时间结果
- jedisUtil.existKey(key)耗时140ms+
- jedisUtil.getKey(key)耗时80ms+
优化existKey方法
将existKey改为getKey方式,避免重复获取value值
优化完,只会执行getKey一次时间
eg.
String arrayString=ipWhiteList.jedisUtil.getFromCache(CommonUtil.getRedisKey(ConstantComm.IP_TABLES_WHITE_KEY));
long t2=System.currentTimeMillis();
System.out.println("获取缓存时间: "+(t2 - t1)+"ms");
//先查缓存
if (arrayString==null) {
List<String> ipList=ipWhiteList.ipTablesDao.queryIpList(params);
arrayString=JSON.toJSONString(ipList).toString();
ipWhiteList.jedisUtil.put2Cache(CommonUtil.getRedisKey(ConstantComm.IP_TABLES_WHITE_KEY), arrayString);
return ipList;
}else {
return JSONObject.parseArray(arrayString, String.class);
}
再压测100个并发
程序运行时间: 17689ms
程序运行时间: 17701ms
程序运行时间: 17714ms
程序运行时间: 17730ms
程序运行时间: 17740ms
程序运行时间: 17756ms
程序运行时间: 17763ms
程序运行时间: 17773ms
程序运行时间: 17780ms
程序运行时间: 17789ms
程序运行时间: 17798ms
程序运行时间: 17810ms
发现时间随着并发量变大而变大
优化思路
- Redis瓶颈?
- Jedis连接瓶颈?
检查是否为Redis瓶颈
查看redis慢查询
https://www.cnblogs.com/huamei2008/p/8850047.html
发现在并发数变大时,redis查询时间会变大,但也仅限于微秒级别,所以排除redis自身问题
检查Jedis连接
这个时候头都大了,按理说单次查询redis时间不至于到80ms,而且随着并发量变大几乎呈指数增长。
话不多说,检查jedis代码
@Configuration
public class JedisClusterConfig {
@Autowired
private RedisProperties redisProperties;
public JedisCluster getJedisCluster(){
String [] serverArray=redisProperties.getClusterNodes().split(",");
Set<HostAndPort> nodes=new HashSet<>();
for (String ipPort:serverArray){
String [] ipPortPair=ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim())));
}
String redisAuthPass = redisProperties.getRedisAuthPass();
return new JedisCluster(nodes,2000, 2000, 6, redisAuthPass, new JedisPoolConfig());
}
}
@Component
public class JedisUtil {
@Autowired
private JedisClusterConfig jedisClusterConfig;
/*public static JedisUtil testUtils;
@PostConstruct
public void init() {
testUtils = this;
} */
/**
* 放入缓存
*
* @param cacheKey key
* @param value
* @return
*/
public void put2Cache(String cacheKey, String value) throws Exception {
jedisClusterConfig.getJedisCluster().set(cacheKey, value);
}
...
}
仔细一看发现代码有点怪,获取redis值的方法竟然不是静态方法
每次都要使用autowired重新生成bean,而且JedisClusterConfig 竟然也没有将链接静态化
调整静态方法
@Configuration
public class JedisClusterConfig {
private static RedisProperties redisProperties;
private static JedisCluster jedisCluster = null;
@Autowired
private RedisProperties redisProperties2;
@PostConstruct
public void init() {
redisProperties = redisProperties2;
if (jedisCluster==null) {
try {
jedisCluster = reloadJedisCluster();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取JedisCluster对象
*
* @return
* @throws Exception
*/
public static JedisCluster getCluster() throws Exception {
if (jedisCluster == null) {
synchronized (JedisClusterConfig.class) {
jedisCluster = reloadJedisCluster();
}
return jedisCluster;
} else {
return jedisCluster;
}
}
// public static JedisCluster getJedisCluster(){
// String [] serverArray=redisProperties.getClusterNodes().split(",");
// Set<HostAndPort> nodes=new HashSet<>();
//
// for (String ipPort:serverArray){
// String [] ipPortPair=ipPort.split(":");
// nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim())));
//
// }
// String redisAuthPass = redisProperties.getRedisAuthPass();
// return new JedisCluster(nodes,2000, 2000, 6, redisAuthPass, new JedisPoolConfig());
// }
private static JedisCluster reloadJedisCluster() throws Exception {
System.out.println("初始化实体");
JedisCluster cluster = null;
String redisAuthPass = redisProperties.getRedisAuthPass();
String[] addrs = redisProperties.getClusterNodes().split(",");
Set<HostAndPort> nodes=new HashSet<>();
for (String ipPort:addrs){
String [] ipPortPair=ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim())));
}
cluster = new JedisCluster(nodes, 2000, 2000, 6, redisAuthPass, new JedisPoolConfig());
return cluster;
}
}
@Component
public class JedisUtil {
/*public static JedisUtil testUtils;
@PostConstruct
public void init() {
testUtils = this;
} */
/**
* 放入缓存
*
* @param cacheKey key
* @param value
* @return
*/
public void put2Cache(String cacheKey, String value) throws Exception {
JedisClusterConfig.getCluster().set(cacheKey, value);
}
}
再压测 问题解决
发现时间稳定在4-8ms之间,并发上去之后依然不变
问题思考
最后发现并不是redis的锅,也不是jedis的锅,redis完全可以适应高并发场景(本来就为此而生)
那么问题来了,是因为初始化bean占用内存空间导致???
请各位大神指点一二