美文网首页
Jedis的使用及配置优化

Jedis的使用及配置优化

作者: 若兮缘 | 来源:发表于2019-02-27 07:27 被阅读71次

    Jedis是什么

    jedis就是基于java语言的redis客户端,集成了redis的命令操作,提供了连接池管理。
    redis-cli是redis官方提供的客户端,可以看作一个shell程序,它可以发送命令对redis进行操作。
    对于jedis同理是使用java语言操作redis,双方都遵循redis提供的协议,按照协议开发对应的客户端。

    Maven依赖
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
            <scope>compile</scope>
        </dependency>
    

    Jedis直连

    jedis直连,本质是定义一个tcp连接,然后使用socket技术进行通信

        //1.生成一个jedis对象,这个对象负责和指定Redis节点进行通信
        Jedis jedis = new Jedis("119.23.226.29", 6379);
        //带密码需要执行认证方法
        //jedis.auth("123456");
        //2.jedis执行set操作
        jedis.set("hello", "world");
        //3.jedis执行get操作,value="world"
        String value = jedis.get("hello");
    
    构造方法参数介绍

    Jedis简单使用

    字符串
    // 1.string
    //输出结果: OK
    jedis.set("hello", "world");
    //输出结果: world
    jedis.get("hello");
    //输出结果:1
    jedis.incr("counter");
    
    哈希
    // 2.hash
    jedis.hset("myhash", "f1", "v1");
    jedis.hset("myhash", "f2", "v2");
    //输出结果 : {f1=v1, f2=v2}
    jedis.hgetAll("myhash");
    
    列表
    // 3.list
    jedis.rpush("mylist", "1");
    jedis.rpush("mylist", "2");
    jedis.rpush(" mylist", "3");
    //输出结果 : [1, 2, 3]
    jedis.lrange("mylist", 0, -1);
    
    集合
    // 4.set
    jedis.sadd(" myset", "a");
    jedis.sadd(" myset", "b");
    jedis.sadd(" myset", "a");
    //输出结果 : [b, a]
    jedis.smembers("myset");
    
    有序集合
    // 5.zset
    jedis.zadd("myzset", 99, "tom");
    jedis.zadd("myzset", 66, "peter");
    jedis.zadd("myzset", 33, "james");
    //输出结果 : [[["james"],33.0], [["peter"],66.0], [["tom"],99.0]]
    jedis.zrangeWithScores("myzset", 0, -1);
    

    Jedis连接池

    jedis直连

    每次操作创建一个jedis对象,执行完毕后关闭连接,对应的就是一次Tcp连接。

    jedis连接池

    预先生成一批jedis连接对象放入连接池中,当需要对redis进行操作时从连接池中借用jedis对象,操作完成后归还。这样jedis对象可以重复使用,避免了频繁创建socket连接,节省了连接开销。

    方案对比

    连接池简单使用

    这里只是对连接池进行一个简单使用,实际开发通常会对JedisPool进行封装,进行一些参数配置和方法定义等,在使用Jedis API时,也会对常用API进行封装,方便程序调用

    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    public class Demo {
        
        public static void main(String[] args) {
            //连接池配置对象,包含了很多默认配置
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            //初始化Jedis连接池,通常来讲JedisPool是单例的
            JedisPool jedisPool = new JedisPool(poolConfig, "119.23.226.29", 6379);
            Jedis jedis = null;
            try {
                //1.从连接池获取jedis对象
                jedis = jedisPool.getResource();
                //2.执行操作
                jedis.set("hello", "jedis");
                System.out.println(jedis.get("hello"));
            } catch (Exception e) {
                e.printStackTrace();
            } finally{
                //如果使用JedisPool,那么close操作不是关闭连接,代表归还连接池
                if(jedis != null){
                    jedis.close();
                }
            }   
        }
    }
    

    Jedis配置优化

    对于企业级开发来说,连接池的合理使用是非常重要的,如果设置不当会引起很多不必要的麻烦,容易造成线上的故障。
    其实关于配置是一个比较难或者说没有确定答案的部分,这里只能给出一些思路和解决一些异常的方法。

    连接池重要配置

    为了方便使用,Jedis提供了JedisPoolConfig,它本身继承了GenericObjectPoolConfig设置了一些空闲监测设置

    资源数控制
    借还参数
    适合的maxTotal

    其实这个参数是比较难确定的,举个例子:

    • 命令平均执行时间0.1ms = 0.001s
    • 业务需要50000 QPS
    • maxTotal理论值 = 0.001 * 50000 = 50个。实际值要偏大一些

    对于适合的maxTotal而言,我们需要考虑

    • 业务希望Redis并发量
    • 客户端执行命令时间
    • Redis资源:例如 nodes(例如应用个数) * maxTotal 是不能超过redis的最大连接数
    • 资源开销:例如虽然希望控制空闲连接,但是不希望因为连接池的频繁释放创建连接造成不必靠开销
    适合的maxIdle和minIdle
    • 建议maxIdle = maxTotal,减少创建新连接的开销
    • 建议预热minIdle,减少第一次启动后的新连接开销
    常见问题

    无法从资源池获取到资源,原因是获取空闲连接超时了。

    redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    …
    Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
    

    无法从资源池获取到资源,原因是池子中资源已经耗尽了。

    redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    …
    Caused by: java.util.NoSuchElementException: Pool exhausted
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
    
    解决思路
    1. 慢查询阻塞:池子连接都被hang住。
    2. 资源池参数不合理:例如QPS高、池子小。
    3. 连接泄露(没有close()):此类问题比较难定位,例如client list、netstat等,最重要的是写合理的代码。
    4. DNS异常等。
    例如连接泄漏
        public static void main(String[] args){
    
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setMaxTotal(10);
            jedisPoolConfig.setMaxWaitMillis(1000);
        
            JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
        
            for(int i = 0; i < 10; i++){
                Jedis jedis = null;
                try{
                    jedis = jedisPool.getResource();
                    jedis.ping();
                    //没有进行连接的归还
                }catch(Exception e){
                    e.printStackTrace();
                }
                //再次获取资源就会出错
                jedisPool.getResource().ping();
            }
        }
    

    相关文章

      网友评论

          本文标题:Jedis的使用及配置优化

          本文链接:https://www.haomeiwen.com/subject/pjbzyqtx.html