zoukankan      html  css  js  c++  java
  • Redis(四)Jedis客户端

      一、客户端通信协议

      二、Java客户端Jedis

      1.获取Jedis

      Jedis属于Java的第三方开发包,在Java中获取第三方开发包通常有两种方式:

    • 直接下载目标版本的Jedis-${version}.jar包加入到项目中。
    • 使用集成构建工具,例如maven、gradle等将Jedis目标版本的配置加入到项目中。

      2.Jedis的基本使用方法

      本次使用的jar包为:jedis-2.9.0.jar

    bigjun@myubuntu:/home/workspace/JedisTest$ tree
    .
    ├── bin
    │   └── ubuntuJedis.class
    ├── lib
    │   └── jedis-2.9.0.jar
    └── src
        └── ubuntuJedis.java
    
    3 directories, 3 files

      Jedis构造器:

    Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout)
    host:Redis实例的所在机器的IP
    port:Redis实例的端口
    connectionTimeout:客户端连接超时
    soTimeout:客户端读写超时

      先看一个简单的代码实现set和get功能:

    package JedisTest;
    import redis.clients.jedis.Jedis;
    public class JedisTest {
    
        public static void main(String[] args) {
            @SuppressWarnings("resource")
            Jedis jedis = new Jedis("localhost");
            
            jedis.set("eclipse", "eclipseJedis");
            String getResult = jedis.get("eclipse");
            System.out.println(getResult);
        }
    }

      在实际项目中,比较推荐使用try catch finally的形式来进行代码的书写:一方面可以在Jedis出现异常的时候(本身是网络操作),将异常进行捕获或者抛出;另一个方面无论执行成功或者失败,将Jedis连接关闭掉,在开发中关闭不用的连接资源是一种好的习惯,代码类似如下:

    package JedisTest;
    import redis.clients.jedis.Jedis;
    public class JedisTest {
    
        public static void main(String[] args) {
            Jedis jedis = null;
            try {
                jedis = new Jedis("127.0.0.1", 6379);
                String string = jedis.get("hello");
                System.out.println(string);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
    }

      给出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);

      3.序列化和反序列化:

      有了上述这些API的支持,就可以将Java对象序列化为二进制,当应用需要获取Java对象时,使用get(final byte[]key)函数将字节数组取出,然后反序列化为Java对象即可。

      和很多NoSQL数据库(例如Memcache、Ehcache)的客户端不同,Jedis本身没有提供序列化的工具,也就是说开发者需要自己引入序列化的工具。

      常用的序列化工具为JDK、XML、JSON和Protostuff,这几种序列化工具在Jedis中的使用见另外的一篇博客。

      4.Jedis连接池的使用方法

      (1)直连方式

      直连是指Jedis每次都会新建TCP连接,使用后再断开连接,对于频繁访问Redis的场景显然不是高效的使用方式。

      

      (2)连接池方式

      生产环境中一般使用连接池的方式对Jedis连接进行管理,Jedis对象预先放在连接池(JedisPool)中,每次要连接Redis,只需要在池子中借,用完了再归还给池子。

      

      (3)Jedis直连方式和连接池方式对比

      

      (4)Jedis连接池方式示例

      JedisPool版本:commons-pool2-2.5.0.jar

    • 使用默认连接池配置来实现Redis的连接
    package bigjun.iplab.jedisPoolTest;
    
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    
    public class JedisPoolTest {
        public static void main(String[] args) {
            // 连接池配置,这里使用默认配置
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            @SuppressWarnings("resource")
            // 初始化Jedis连接池
            JedisPool jedisPool = new JedisPool(poolConfig, "192.168.131.130", 6379);
            Jedis jedis = null;
            try {
                // 从连接池获取jedis对象
                jedis = jedisPool.getResource();
                // 执行操作
                String getResult = jedis.get("Lua");
                System.out.println(getResult);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
    }
    • Jedis中的close()方法
      @Override
      public void close() {
       // 如果连接池正在使用
    if (dataSource != null) {
        // 如果连接断开,就返还连接给连接池
    if (client.isBroken()) { this.dataSource.returnBrokenResource(this); } else { this.dataSource.returnResource(this); }
       // 直连方式的时候,关闭连接 }
    else { client.close(); } }
    • 连接池配置的配置方法
    // 默认配置
    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); // 设置最大连接数为默认值的 5 倍 poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5); // 设置最大空闲连接数为默认值的 3 倍 poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3); // 设置最小空闲连接数为默认值的 2 倍 poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2); // 设置开启 jmx 功能 poolConfig.setJmxEnabled(true); // 设置连接池没有连接后客户端的最大等待时间 ( 单位为毫秒 ) poolConfig.setMaxWaitMillis(3000);

      5.Jedis中Pipeline使用方法

      示例背景:Redis提供了mget、mset方法,但是没有提供mdel方法,可以用Pipeline来模拟批量删除,虽然不想mset和mget是原子命令,但是绝大时候可以使用。

      (1)使用sync()执行命令

    public void mdel(List<String> keys) {
    Jedis jedis = new Jedis("127.0.0.1");
    // 1) 生成 pipeline 对象
    Pipeline pipeline = jedis.pipelined();
    // 2)pipeline 执行命令,注意此时命令并未真正执行
    for (String key : keys) {
    pipeline.del(key);
    }
    // 3) 执行命令
    pipeline.sync();
    }

      (2)使用syncAndReturnAll()方法执行命令并获得返回值

    Jedis jedis = new Jedis("127.0.0.1");
    Pipeline pipeline = jedis.pipelined();
    pipeline.set("hello", "world");
    pipeline.incr("counter");
    List<Object> resultList = pipeline.syncAndReturnAll();
    for (Object object : resultList) {
    System.out.println(object);
    }

      6.Jedis的Lua脚本

      (1)Jedis的三个重要的函数:

    Object eval(String script, int keyCount, String... params)
    Object evalsha(String sha1, int keyCount, String... params)
    String scriptLoad(String script)
    
    script:Lua脚本内容。
    sha1:脚本的SHA1. keyCount:键的个数。 params:相关参数KEYS和ARGV。

      (2)使用eval()方法

      在Redis中执行Lua脚本:

    192.168.131.130:6379> get Lua
    "Redis"
    192.168.131.130:6379> eval 'return redis.call("get", KEYS[1])' 1 Lua
    "Redis"

      在Jedis中执行相同的Lua脚本:

            try {
                // 从连接池获取jedis对象
                jedis = jedisPool.getResource();
                // 执行操作
                String key = "Lua";
                String script = "return redis.call('get', KEYS[1])";
                Object result = jedis.eval(script, 1, key);
                System.out.println(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            

      (3)使用scriptLoad()和evalsha()方法

            try {
                // 从连接池获取jedis对象
                jedis = jedisPool.getResource();
                // 执行操作
                String key = "Lua";
                String script = "return redis.call('get', KEYS[1])";
                String scriptSha1 = jedis.scriptLoad(script);
                Object result = jedis.evalsha(scriptSha1, 1, key);
                System.out.println(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }

      四、客户端管理

      1.客户端API

      (1)client list

      

      

    192.168.131.130:6379> client list
    id=3 addr=192.168.131.130:35096 fd=7 name= age=14770 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
    id=7 addr=192.168.131.130:35104 fd=8 name= age=11574 idle=11465 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=keys
    
    id:客户端连接的唯一标识
    addr:客户端连接的ip和端口
    fd:socket的文件描述符
    name:客户端的名字

    age:当前客户端已经连接的时间
    idle:当前客户端最近一次的空闲时间

    flags:标识当前客户端的类型(N:普通客户端;S:slave客户端等11种客户端类型) qbuf:输入缓冲区的总容量 qbuf
    -free:输入缓冲区的剩余容量 obl:输出缓冲区固定缓冲区的长度
    oll:输出缓冲区动态缓冲区列表的长度
    omen:输出缓冲区使用的字节
    • 输入缓冲区

      

      监控输入缓冲区异常的两种方法:

      

    • 输出缓冲区

      

      按照客户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端

      输出缓冲区的容量可以通过参数client-output-buffer-limit来进行设置

      和输入缓冲区一样有两种监控方法:client list和info clients

      (2)client setName和client getName

      在Redis只有一个应用方使用的情况下,IP和端口作为标识会更加清晰;当多个应用方共同使用一个Redis,client setName可以作为标识客户端的一个依据。

    192.168.131.130:6379> client setName haveName_client
    OK
    192.168.131.130:6379> client list
    id=3 addr=192.168.131.130:35096 fd=7 name=haveName_client age=16157 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
    id=7 addr=192.168.131.130:35104 fd=8 name= age=12961 idle=12852 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=keys
    192.168.131.130:6379> client getName
    "haveName_client"

      (3)client kill ip:port

    192.168.131.130:6379> client list
    id=3 addr=192.168.131.130:35096 fd=7 name=haveName_client age=16213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
    id=7 addr=192.168.131.130:35104 fd=8 name= age=13017 idle=12908 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=keys
    192.168.131.130:6379> client kill 192.168.131.130:35104
    OK
    192.168.131.130:6379> client list
    id=3 addr=192.168.131.130:35096 fd=7 name=haveName_client age=16239 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

      (4)阻塞客户端连接:client pause timeout(毫秒)

    客户端1:
    192.168.131.130:6379> client pause 10000
    OK
    
    客户端2:
    192.168.131.130:6379> ping
    PONG
    (9.00s)

      (5)监控Redis正在执行的命令:monitor

    客户端1:
    192.168.131.130:6379> monitor
    OK
    
    客户端2:
    (9.00s)
    192.168.131.130:6379> get Lua
    "Redis"
    192.168.131.130:6379> ping 
    PONG
    
    客户端1:
    192.168.131.130:6379> monitor
    OK
    1528182107.437256 [0 192.168.131.130:35108] "get" "Lua"
    1528182112.634521 [0 192.168.131.130:35108] "ping"
    ...

      2.客户端相关配置

    timeout:检测客户端空闲连接的超时时间,一旦idle时间达到了timeout,客户端将会被关闭,如果设置为0就不进行检测。
    maxclients:客户端最大连接数赘述,这个参数会受到操作系统设置的限制,
    tcp-keepalive:检测TCP连接活性的周期,默认值为0,也就是不进行检测,如果需要设置,建议为60,那么Redis会每隔60秒对它创建的TCP连接进行活性检测,防止大量死连接占用系统资源。
    tcp-backlog:TCP三次握手后,会将接受的连接放入队列中,tcp-backlog就是队列的大小,它在Redis中的默认值是511

      3.客户端统计片段 

    192.168.131.130:6379> info clients
    # Clients
    connected_clients:2
    client_longest_output_list:0
    client_biggest_input_buf:0
    blocked_clients:0
    
    connected_clients:当前Redis节点的客户端连接数
    client_longest_output_list:当前所有输出缓冲区中队列对象个数的最大值
    client_biggest_input_buf:当前所有输入缓冲区中占用的最大容量
    blocked_clients:正在执行阻塞命令的客户端的个数
    
    
    192.168.131.130:6379> info stats
    # Stats
    total_connections_received:11
    total_commands_processed:73
    instantaneous_ops_per_sec:0
    total_net_input_bytes:3618
    total_net_output_bytes:25444
    instantaneous_input_kbps:0.00
    instantaneous_output_kbps:0.00
    rejected_connections:0
    ...

      五、客户端常见异常

      1.无法从连接池获取到连接

      2.客户端读写超时

      3.客户端连接超时

      4.客户端缓冲区异常

      5.Lua脚本正在执行

      6.Redis正在加载持久化文件

      7.Redis使用的内存超过maxmemory配置

      8.客户端连接数过大

  • 相关阅读:
    正则获取HTML代码中img的src地址
    System.Diagnostics.Process 启动进程资源或调用外部的命令的使用
    按位取反运算符~
    Nhibernate Query By Criteria 条件查询
    Unit Test测试框架中的测试的执行顺序
    Jquery自定义插件之$.extend()、$.fn和$.fn.extend()
    如何采集QQ群中所有成员QQ号码
    Sql server使用Merge关键字做插入或更新操作
    c#类库和可移值类库的区别
    VS代码管理插件AnkhSvn
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9115095.html
Copyright © 2011-2022 走看看