問題
自動化測試大量報錯,原因是驗證碼錯誤,從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; }