zoukankan      html  css  js  c++  java
  • FunTester框架Redis压测预备

    超万字回顾FunTester的前世今生一文中我分享了FunTester测试框架一个优点:针对所有Java可实现的接口都能进行功能封装进而进行性能测试。

    之前都已经分享过了HTTP协议、Socket协议以及MySQL的测试案例,最近要准备对Redis的接口进行测试,所以未雨绸缪,我提前将Redis的功能接口封装类重写了一下,不得不说之前对Redis的认知真是肤浅。

    话不多说,首先我分享一下自己的思路:

    • Redis连接池管理类
    • Redis资源回收
    • Redis功能封装类

    池化技术

    在开始正文之前,先分享一个技术名词叫做池化技术。池化技术的应用非常的广泛,比如我们经常说到的线程池、mysql数据库连接池,Http协议连接池以及我们今天要分享的Redis连接池。

    池化技术 (Pool) 是一种很常见的编程技巧,在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销。我们日常工作中常见的有数据库连接池、线程池、对象池等,它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。

    在做性能测试的过程中,也是用到了很多实话的技术,我的一个基本思路就是拿协议的连接池和线程池做绑定,这样既能避免资源的共享导致的问题,也能够提升性能并发能力。

    这里有一个非常重要的知识点,就是资源的回收复用。后面我会分享到。

    Redis连接池管理类

    这个连接池管理类的主要功能就是创建连接池,设置连接配置。本次我并没有做配置文件,如果大家有需求的话,可以进行配置文件,去把这些配置存起来。

    package com.funtester.db.redis;
    
    import com.alibaba.fastjson.JSONObject;
    import com.funtester.frame.SourceCode;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    /**
     * redis连接池
     */
    public class RedisPool extends SourceCode {
    
        static Logger logger = LogManager.getLogger(RedisPool.class);
    
        /**
         * 最大连接数
         */
        private static int MAX_TOTAL = 1000;
    
        /**
         * 在jedispool中最大的idle状态(空闲的)的jedis实例的个数
         */
        private static int MAX_IDLE = 300;
    
        /**
         * 在jedispool中最小的idle状态(空闲的)的jedis实例的个数
         */
        private static int MIN_IDLE = 10;
    
        /**
         * 获取实例的最大等待时间
         */
        private static long MAX_WAIT = 5000;
    
        /**
         * redis连接的超时时间
         */
        private static int TIMEOUT = 5000;
    
        /**
         * 在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值true。则得到的jedis实例肯定是可以用的
         */
        private static boolean testOnBorrow = true;
    
        /**
         * 在return一个jedis实例的时候,是否要进行验证操作,如果赋值true。则放回jedispool的jedis实例肯定是可以用的。
         */
        private static boolean testOnReturn = true;
    
        /**
         * 连接耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时。默认为true
         */
        private static boolean blockWhenExhausted = true;
    
        private static JedisPoolConfig config = getConfig();
    
        /**
         * 初始化连接池
         */
        public static JedisPool getPool(String host, int port) {
            logger.info("redis连接池IP:{},端口:{},超时设置:{}", host, port, TIMEOUT);
            return new JedisPool(config, host, port, TIMEOUT);
        }
    
        /**
         * 默认连接池配置
         *
         * @return
         */
        private static JedisPoolConfig getConfig() {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_TOTAL);
            config.setMaxIdle(MAX_IDLE);
            config.setMinIdle(MIN_IDLE);
            config.setTestOnBorrow(testOnBorrow);
            config.setTestOnReturn(testOnReturn);
            config.setBlockWhenExhausted(blockWhenExhausted);
            config.setMaxWaitMillis(MAX_WAIT);
            logger.debug("连接redis配置:{}", JSONObject.toJSONString(config));
            return config;
        }
    
    }
    
    

    资源回收知识

    这里的资源就是指Redis连接池的连接,在我们获取一个连接,进行一些操作之后,我们需要把这个连接重新放回到连接池里面,一共其他线程获取,已达到复用的目的。这里我们主要参考的是官方的实现案例。其实方法也是非常简单的,只需要我们在获取一个连接Jedis对象进行操作之后,调用close()方法即可。

    这里我用到了Javatry-catch语法:

     /**
         * 设置key的有效期,单位是秒
         *
         * @param key
         * @param exTime
         * @return
         */
        public boolean expire(String key, int exTime) {
            try (Jedis jedis = getJedis()) {
                return jedis.expire(key, exTime) == 1;
            } catch (Exception e) {
                logger.error("expire key:{} error", key, e);
                return false;
            }
        }
    
    

    这种写法等同于:

        public boolean expire(String key, int exTime) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.expire(key, exTime) == 1;
            } catch (Exception e) {
                logger.error("expire key:{} error", key, e);
                return false;
            } finally {
                if (jedis != null) jedis.close();
            }
        }
    
    

    功能封装类

    在我开始看Redis操作常用api之前,我觉得这个api的数量会比较少,但是在我详细查完所有api的之后。我就发现自己真的是too young,too simple!这个功能封装类我写了一些非常常用的操作,对于一些不太常用的,这里并没有写出。如果大家有需要,可以通过继承这个com.funtester.db.redis.RedisBase类来添加方法的封装。

    package com.funtester.db.redis;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    import java.util.*;
    
    public class RedisBase {
    
        private static Logger logger = LogManager.getLogger(RedisBase.class);
    
        String host;
    
        int port;
    
        JedisPool pool;
    
        public int index;
    
        public RedisBase() {
        }
    
        public RedisBase(String host, int port) {
            this.host = host;
            this.port = port;
            pool = RedisPool.getPool(host, port);
        }
    
        /**
         * 获取jedis操作对象,回收资源方法close,3.0以后废弃了其他方法,默认连接第一个数据库
         * 默认使用0个index
         *
         * @return
         */
        public Jedis getJedis() {
            Jedis resource = pool.getResource();
            resource.select(index);
            return resource;
        }
    
        /**
         * 设置key的有效期,单位是秒
         *
         * @param key
         * @param exTime
         * @return
         */
        public boolean expire(String key, int exTime) {
            try (Jedis jedis = getJedis()) {
                return jedis.expire(key, exTime) == 1;
            } catch (Exception e) {
                logger.error("expire key:{} error", key, e);
                return false;
            }
        }
    
        /**
         * 设置key-value,过期时间
         *
         * @param key
         * @param value
         * @param expiredTime 单位s
         * @return
         */
        public boolean set(String key, String value, int expiredTime) {
            try (Jedis jedis = getJedis()) {
                return jedis.setex(key, expiredTime, value).equalsIgnoreCase("OK");
            } catch (Exception e) {
                logger.error("setex key:{} value:{} error", key, value, e);
                return false;
            }
        }
    
        /**
         * 设置redis内容
         *
         * @param key
         * @param value
         * @return
         */
        public boolean set(String key, String value) {
            try (Jedis jedis = getJedis()) {
                return jedis.set(key, value).equalsIgnoreCase("OK");
            } catch (Exception e) {
                logger.error("set key:{} value:{} error", key, value, e);
                return false;
            }
        }
    
        /**
         * 获取value
         *
         * @param key
         * @return
         */
        public String get(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.get(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 设置redis list内容
         * Redis Lpush 命令将一个或多个值插入到列表头部。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误
         *
         * @param key
         * @param list
         * @return list长度
         */
        public Long lpush(String key, String... list) {
            try (Jedis jedis = getJedis()) {
                return jedis.lpush(key, list);
            } catch (Exception e) {
                return 0l;
            }
        }
    
    
        /**
         * 将一个或多个值插入到已存在的列表头部,列表不存在时操作无效
         *
         * @param key
         * @param list
         * @return
         */
        public Long lpushx(String key, String... list) {
            try (Jedis jedis = getJedis()) {
                return jedis.lpushx(key, list);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return 0l;
            }
        }
    
        /**
         * 将一个或多个值插入到列表的尾部(最右边)。
         * <p>
         * 如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。
         *
         * @param key
         * @param list
         * @return
         */
        public Long rpush(String key, String... list) {
            try (Jedis jedis = getJedis()) {
                return jedis.rpush(key, list);
            } catch (Exception e) {
                return 0l;
            }
        }
    
        /**
         * 用于将一个或多个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效
         *
         * @param key
         * @param list
         * @return
         */
        public Long rpushx(String key, String... list) {
            try (Jedis jedis = getJedis()) {
                return jedis.rpushx(key, list);
            } catch (Exception e) {
                return 0l;
            }
        }
    
        /**
         * 从数组中获取第一个值,并删除该值
         *
         * @param key
         * @return
         */
        public String lpop(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.lpop(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 用于移除并返回列表的最后一个元素
         *
         * @param key
         * @return
         */
        public String rpop(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.rpop(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 获取数组长度
         *
         * @param key
         * @return
         */
        public Long llen(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.llen(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return 0l;
            }
        }
    
        /**
         * 获取固定索引的值
         *
         * @param key
         * @param index 从0开始
         * @return
         */
        public String lindex(String key, long index) {
            try (Jedis jedis = getJedis()) {
                return jedis.lindex(key, index);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 获取list某一段
         *
         * @param key
         * @param start
         * @param end
         * @return
         */
        public List<String> lrange(String key, long start, long end) {
            try (Jedis jedis = getJedis()) {
                return jedis.lrange(key, start, end);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return new ArrayList<>();
            }
        }
    
        /**
         * 设置list中index的值
         *
         * @param key
         * @param index
         * @param value
         * @return
         */
        public String lset(String key, long index, String value) {
            try (Jedis jedis = getJedis()) {
                return jedis.lset(key, index, value);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 将哈希表 key 中的域 field 的值设为 value
         *
         * @param key
         * @param hkey
         * @param value
         * @return 行数
         */
        public Long hset(String key, String hkey, String value) {
            try (Jedis jedis = getJedis()) {
                return jedis.hset(key, hkey, value);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回哈希表 key 中给定域 field 的值
         *
         * @param key
         * @param hkey
         * @return
         */
        public String hget(String key, String hkey) {
            try (Jedis jedis = getJedis()) {
                return jedis.hget(key, hkey);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略
         *
         * @param key
         * @param hkey
         * @return
         */
        public Long hdel(String key, String hkey) {
            try (Jedis jedis = getJedis()) {
                return jedis.hdel(key, hkey);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 查看哈希表 key 中,给定域 field 是否存在
         *
         * @param key
         * @param hkey
         * @return
         */
        public Boolean hexists(String key, String hkey) {
            try (Jedis jedis = getJedis()) {
                return jedis.hexists(key, hkey);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回哈希表 key 中的所有域
         *
         * @param key
         * @return
         */
        public Set<String> hkeys(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.hkeys(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回哈希表 key 中所有域的值
         *
         * @param key
         * @return
         */
        public List<String> hvals(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.hvals(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回哈希表 key 中域的数量
         *
         * @param key
         * @return
         */
        public Long hlen(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.hlen(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回哈希表 key 中,所有的域和值
         *
         * @param key
         * @return
         */
        public Map<String, String> hgetAll(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.hgetAll(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 为哈希表 key 中的域 field 的值加上增量 increment。如果域 field 不存在,域的值先被初始化为 0
         *
         * @param key
         * @param hkey
         * @param num
         * @return
         */
        public Long hincrBy(String key, String hkey, long num) {
            try (Jedis jedis = getJedis()) {
                return jedis.hincrBy(key, hkey, num);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 为哈希表 key 中的域 field 的值加上增量 increment。如果域 field 不存在,域的值先被初始化为 0
         *
         * @param key
         * @param hkey
         * @param num
         * @return
         */
        public Double hincrByFloat(String key, String hkey, double num) {
            try (Jedis jedis = getJedis()) {
                return jedis.hincrByFloat(key, hkey, num);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
    
        /**
         * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略
         *
         * @param key
         * @param value
         * @return
         */
        public Long sadd(String key, String... value) {
            try (Jedis jedis = getJedis()) {
                return jedis.sadd(key, value);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回集合 key 的基数(集合中元素的数量)
         *
         * @param key
         * @param value
         * @return
         */
        public Long scard(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.scard(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回集合 key 中的所有成员。 不存在的 key 被视为空集合
         *
         * @param key
         * @return
         */
        public Set<String> smembers(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.smembers(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 移除并返回集合中的一个随机元素
         *
         * @param key
         * @return
         */
        public String spop(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.spop(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
         * 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
         *
         * @param key
         * @param count
         * @return
         */
        public List<String> srandmember(String key, int count) {
            try (Jedis jedis = getJedis()) {
                return jedis.srandmember(key, count);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 返回集合中的一个随机元素
         *
         * @param key
         * @return
         */
        public String srandmember(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.srandmember(key);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
    
        /**移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略
         * @param key
         * @param value
         * @return
         */
        public Long srem(String key,String... value) {
            try (Jedis jedis = getJedis()) {
                return jedis.srem(key,value);
            } catch (Exception e) {
                logger.error("get key:{} error", key, e);
                return null;
            }
        }
        /**
         * 是否存在key
         *
         * @param key
         * @return
         */
        public boolean exists(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.exists(key);
            } catch (Exception e) {
                logger.error("exists key:{} error", key, e);
                return false;
            }
        }
    
        /**
         * 删除key
         * jedis返回值1表示成功,0表示失败,可能是不存在的key
         *
         * @param key
         * @return
         */
        public Long del(String... key) {
            try (Jedis jedis = getJedis()) {
                return jedis.del(key);
            } catch (Exception e) {
                logger.error("del key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 获取key对应value的类型
         *
         * @param key
         * @return
         */
        public String type(String key) {
            try (Jedis jedis = getJedis()) {
                return jedis.type(key);
            } catch (Exception e) {
                logger.error("type key:{} error", key, e);
                return null;
            }
        }
    
        /**
         * 获取符合条件的key集合
         *
         * @param pattern
         * @return
         */
        public Set<String> getKeys(String pattern) {
            try (Jedis jedis = getJedis()) {
                return jedis.keys(pattern);
            } catch (Exception e) {
                logger.error("type key:{} error", pattern, e);
                return new HashSet<String>();
            }
        }
    
        /**
         * 将 value 追加到 key 原来的值的末尾
         *
         * @param key
         * @param content
         * @return
         */
        public Long append(String key, String content) {
            try (Jedis jedis = getJedis()) {
                return jedis.append(key, content);
            } catch (Exception e) {
                logger.error("append key:{} ,content:{},error", key, content, e);
                return null;
            }
        }
    
        /**
         * 关闭连接池
         */
        public void close() {
            pool.close();
        }
    
    }
    
    

    Have Fun ~ Tester !

  • 相关阅读:
    装饰器及其应用
    信息系统项目管理师高频考点(第九章)
    信息系统项目管理师高频考点(第八章)
    信息系统项目管理师高频考点(第七章)
    系统集成项目管理工程师高频考点(第九章)
    系统集成项目管理工程师高频考点(第八章)
    信息系统项目管理师高频考点(第六章)
    信息系统项目管理师高频考点(第五章)
    信息系统项目管理师高频考点(第四章)
    信息系统项目管理师高频考点(第三章)
  • 原文地址:https://www.cnblogs.com/FunTester/p/15218125.html
Copyright © 2011-2022 走看看