问题
自动化测试大量报错,原因是验证码错误,从redis中查询验证码,返回结果均为null。
原因
因为在UAT环境,各项目共用1个redis,db0中的数据较多,达到了600W+,导致查询速度较慢,故验证码发送服务将验证码的存储从db0切到了db4。
导致自定义的Jmeter函数无法正常查询到验证码,因为原本该函数是默认充db0中进行查询的。
解决方案
自定义函数中是使用jedis对redis使用操作,版本为3.0.1,maven详细依赖如下:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.1</version> </dependency>
在网上查询的方法有在config中添加配置与在redis对象上使用select()两种方案,可能是因为jedis版本不同,以上2中方案均不适用。
因为JedisPoolConfig中没有方法用于设置db,并且ShardedJedis中没有select()方法,只能寻找其他技术方案进行解决。
函数中原使用以下方法创建redis分片:
public static ShardedJedis getJedis (String host, String password) { JedisShardInfo jedisShardInfo = new JedisShardInfo(host,6379); jedisShardInfo.setPassword(password); List<JedisShardInfo> list = new LinkedList<JedisShardInfo>(); list.add(jedisShardInfo); ShardedJedisPool pool = new ShardedJedisPool(config, list); ShardedJedis jedis = pool.getResource(); return jedis; }
查看JedisShardInfo中的JedisShardInfo(String host, int port)构造方法,
public JedisShardInfo(String host, int port) { this(host, port, 2000); }
继续查看调用链,
public JedisShardInfo(String host, int port, int timeout) { this(host, port, timeout, timeout, 1); }
public JedisShardInfo(String host, int port, int connectionTimeout, int soTimeout, int weight) { super(weight); this.password = null; this.name = null; this.db = 0; this.host = host; this.port = port; this.connectionTimeout = connectionTimeout; this.soTimeout = soTimeout; }
发现db默认写死为0,且没有set方法。
查询JedisShardInfo中的其他构造方法,发现JedisShardInfo(String host)方法:
public JedisShardInfo(String host) { super(1); this.password = null; this.name = null; this.db = 0; URI uri = URI.create(host); if (JedisURIHelper.isValid(uri)) { this.host = uri.getHost(); this.port = uri.getPort(); this.password = JedisURIHelper.getPassword(uri); this.db = JedisURIHelper.getDBIndex(uri); this.ssl = JedisURIHelper.isRedisSSLScheme(uri); } else { this.host = host; this.port = 6379; } }
此处的db设置并未写死,看起来是根据url来获取设置,进入JedisURIHelper.getPassword()查看详情:
public static int getDBIndex(URI uri) { String[] pathSplit = uri.getPath().split("/", 2); if (pathSplit.length > 1) { String dbIndexStr = pathSplit[1]; return dbIndexStr.isEmpty() ? 0 : Integer.parseInt(dbIndexStr); } else { return 0; } }
从方法中得知,db可根据url中"/"后的值进行设置,而url是通过URI.create()方法获得,查询URI.create()方法详情:
public static URI create(String str) { try { return new URI(str); } catch (URISyntaxException x) { throw new IllegalArgumentException(x.getMessage(), x); } }
查询if判断条件中的JedisURIHelper.isValid()方法:
public static boolean isValid(URI uri) { return !isEmpty(uri.getScheme()) && !isEmpty(uri.getHost()) && uri.getPort() != -1;
故得知,使用JedisShardInfo(String host)进行实例化时,入参为URL的形式,需要有协议头、host地址、端口号,并在host添加"/",再其后设置dbIndex,如下所示形式:
http://xxxxxxx.redis.rds.aliyuncs.com:6379/4
结论
在自定义函数中的getJedis()方法里使用JedisShardInfo(String host)对JedisShardInfo进行实例化,并以http://redisHost:port/dbIndex的形式设置redis地址,则可对redis的指定db进行操作。
修改后的getJedis()方法如下:
public static ShardedJedis getJedis (String host, String password) { JedisShardInfo jedisShardInfo = new JedisShardInfo(host); jedisShardInfo.setPassword(password); List<JedisShardInfo> list = new LinkedList<JedisShardInfo>(); list.add(jedisShardInfo); ShardedJedisPool pool = new ShardedJedisPool(config, list); ShardedJedis jedis = pool.getResource(); return jedis; }