Redis的Java客户端Jedis
Jedis所需jar包 commons-pool-1.6.jar jedis-2.1.0.jar
1.Jedis常用操作(jedis中的api 和 我们在 linux中使用的命令差不多)
a)测试连通性
1 public static void main(String[] args) { 2 //连接虚拟机中的 redis 服务(如果是在linux 本机中,即为192.168.89.128) 3 //注意:连接之前需要关闭linux防火墙 4 Jedis jedis = new Jedis("192.168.89.128",6379); 5 6 //查看服务是否运行,打出pong表示OK 7 System.out.println(jedis.ping()); 8 }
b)1+5 (一个Key 五大数据类型)
1 //测试 对 key 和 五大数据类型的 操作API 2 public class TestAPI { 3 public static void main(String[] args) { 4 5 //jedis中的api 和 我们在 linux中使用的命令差不多 6 Jedis jedis = new Jedis("192.168.89.128",6379); 7 8 jedis.set("k1", "v1"); //set命令 9 jedis.set("k2", "v2"); 10 jedis.set("k3", "v3"); 11 12 System.out.println(jedis.get("k3"));//get命令 13 14 15 Set<String> keysSet = jedis.keys("*"); //类似于 keys * 16 Iterator<String> iterator = keysSet.iterator(); 17 while(iterator.hasNext()) { 18 String key = iterator.next(); 19 System.out.println(key); 20 } 21 22 System.out.println("k1 键是否存在 :" + jedis.exists("k1")); 23 System.out.println("k1 键 剩余生存时间:" + jedis.ttl("k1")); 24 25 26 } 27 28 @Test 29 public void testString() { 30 Jedis jedis = new Jedis("192.168.89.128",6379); 31 //jedis.append("k1", "v1v1"); 32 //System.out.println("append后k1的值:" + jedis.get("k1")); 33 34 System.out.println("k1 的value的长度:" + jedis.strlen("k1")); 35 36 System.out.println("按指定下标取值:" + jedis.getrange("k1", 0, -1)); 37 //如果不存在 则赋值 38 jedis.setnx("k1", "v1"); 39 } 40 41 @Test 42 public void testList() { 43 Jedis jedis = new Jedis("192.168.89.128",6379); 44 jedis.lpush("list1", "1","2","3","4","5"); //List 类型的数据的创建,按照入栈顺序放入 这个双向链表 45 jedis.lset("list1", 0, "100"); //将 list1 的 第一个 元素 设置为100 46 System.out.println("list1:" + jedis.lrange("list1", 0, -1)); 47 System.out.println("lpop:" + jedis.lpop("list1")); //弹出 48 } 49 50 @Test 51 public void testSet() { 52 Jedis jedis = new Jedis("192.168.89.128",6379); 53 jedis.sadd("set1", "1","2","3","4","5","9"); 54 jedis.sadd("set2", "1","2","3","4","5","6"); 55 System.out.println(jedis.smembers("set1")); 56 System.out.println(jedis.smembers("set2")); 57 //差集 在第一个set里面而不在后面任何一个set里面的项 58 System.out.println(jedis.sdiff("set1","set2")); 59 } 60 61 @Test 62 public void testHash() { 63 Jedis jedis = new Jedis("192.168.89.128",6379); 64 jedis.hset("user1", "name", "xzk"); 65 jedis.hset("user1", "age", "22"); 66 67 Map<String,String> map = new HashMap<String, String>(); 68 69 //可以一次性为某个键 赋值多个 KV 键值对 70 map.put("name", "xzk2"); 71 map.put("age", "23"); 72 jedis.hmset("user2", map); //直接存放一个map 73 74 System.out.println(jedis.hget("user1", "age")); 75 76 //可以一次性取出多个 KV键值对 77 List<String> results = jedis.hmget("user2", "name","age"); 78 System.out.println(results); 79 } 80 81 @Test 82 public void testZSet() { 83 Jedis jedis = new Jedis("192.168.89.128",6379); 84 /*jedis.zadd("zset01",60,"v1"); 85 jedis.zadd("zset1",70,"v2"); 86 jedis.zadd("zset1",80,"v3"); 87 jedis.zadd("zset1",90,"v4");*/ 88 89 Map<Double, String> scoreMembers = new HashMap<Double, String>(); 90 scoreMembers.put(60d, "v1"); //key 为 score 91 scoreMembers.put(70d, "v2"); 92 scoreMembers.put(80d, "v3"); 93 scoreMembers.put(90d, "v4"); 94 jedis.zadd("zset1", scoreMembers ); 95 96 Set<String> resultSet = jedis.zrange("zset1", 0, -1); 97 System.out.println(resultSet); 98 } 99 }
c)事务提交
1 //关于 jedis 事务的 2 public class TestTX { 3 public static void main(String[] args) throws InterruptedException { 4 boolean retValue = transMethod(); 5 System.out.println("main retValue-------: " + retValue); 6 } 7 8 9 /** 10 * 通俗点讲,watch命令就是标记一个键,如果标记了一个键, 11 * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中 12 * 重新再尝试一次。 13 * 14 * 首先标记了键balance,然后检查 键balance是否发生了修改,修改了取消标记, 15 * 没有被修改的话,就启动事务进行更新操作, 16 * 如果在此期间键balance被其它人修改, 那在提交事务(执行exec)时就会报错,(但是事务失败后,只是不执行事务而已啊,这里不会报错啊) 17 * 程序中通常可以捕获这类错误再重新执行一次,直到成功。 18 * @throws InterruptedException 19 */ 20 private static boolean transMethod() throws InterruptedException { 21 int balance = 100;// 可用余额 22 int debt = 0;// 欠额 23 int amtToSubtract = 10;// 实刷额 24 25 Jedis jedis = new Jedis("192.168.89.128", 6379); 26 jedis.set("balance", ""+ balance); 27 jedis.set("debt", "" + debt); 28 29 //监听balance,如果在此期间键balance被其它人修改, 则提交的事务 不会被执行(不会报错) 30 jedis.watch("balance"); 31 32 //模拟故障,在等待的时间要去修改下 balance 这个 key的值 33 Thread.sleep(8000); 34 35 //虽然不会报错,但是可以找到这类错误,让其再重新执行一次,直到成功。 36 //检查 键balance 是否被他人修改,如果修改了,则取消标记,返回错误 37 if(balance != Integer.parseInt(jedis.get("balance"))) { 38 jedis.unwatch(); 39 return false; 40 } 41 42 //事务的执行 43 Transaction transaction = jedis.multi(); 44 transaction.decrBy("balance", amtToSubtract); 45 transaction.incrBy("debt", amtToSubtract); 46 //transaction.discard(); 取消某个事务的执行 47 transaction.exec(); 48 49 //从redis 中拿回 新的balance 和 debt 50 balance = Integer.parseInt(jedis.get("balance")); 51 debt = Integer.parseInt(jedis.get("debt")); 52 53 System.out.println("事务执行后,balance = " + balance + " debt = " + debt); 54 return true; 55 } 56 }
2.JedisPool
获取Jedis实例需要从JedisPool中获取
用完Jedis实例需要返回给JedisPool
如果Jedis在使用过程中出错,则也需要还给JedisPool
1 public class TestJedisPool { 2 public static void main(String[] args) { 3 //通过单例模式创建 redis 连接池 4 JedisPool jedisPool = JedisPoolUtil.getJedisPool(); 5 Jedis jedis = null; 6 7 //通过拿到连接 8 try{ 9 jedis = jedisPool.getResource(); 10 System.out.println(jedis.get("k1")); 11 } catch(Exception e) { 12 e.printStackTrace(); 13 }finally { 14 //最后将创建的 jedis 连接放回 池中 15 JedisPoolUtil.releaseJedis(jedisPool, jedis); 16 } 17 18 19 } 20 }
JedisPool配置总结
1 public class JedisPoolUtil { 2 //被volatile 修饰的/*变量*/不会被本地线程缓存,对该变量的操作都是直接操作共享内存的 3 // JUC 里面也用到了 volatile 来保证 内存可见性 4 private static volatile JedisPool jedisPool = null; 5 6 public static JedisPool getJedisPool() { 7 if (null == jedisPool) { 8 synchronized (JedisPoolUtil.class) { 9 if (null == jedisPool) { 10 // 创建连接池的配置文件,JedisPool的配置参数大部分是由这个配置文件决定的 11 JedisPoolConfig poolConfig = new JedisPoolConfig(); 12 // 控制一个pool最多有多少个jedis实例 13 poolConfig.setMaxActive(1000); 14 // 控制一个pool最多有多少个状态为idle的jedis实例(最大空闲连接数)(要有这么多空闲的连接,不然不给创建) 15 poolConfig.setMaxIdle(32); 16 // 创建实例的最大等待时间(因为是上了锁的,所以可能会等待) 17 poolConfig.setMaxWait(100 * 1000); 18 // 在获取连接时 检查有效性,一般为false 19 poolConfig.setTestOnBorrow(true); 20 21 // 这个连接池 从 6379 这个端口获取连接 22 jedisPool = new JedisPool(poolConfig, "192.168.89.128", 23 6379); 24 } 25 } 26 } 27 return jedisPool; 28 } 29 30 // 将 jedis 释放回 jedisPool 31 public static void releaseJedis(JedisPool jedisPool, Jedis jedis) { 32 if (jedis != null) { 33 jedisPool.returnResourceObject(jedis); 34 } 35 } 36 }
1 JedisPool的配置参数大部分是由JedisPoolConfig的对应项来赋值的。 2 3 4 maxActive:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted。 5 maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例; 6 whenExhaustedAction:表示当pool中的jedis实例都被allocated完时,pool要采取的操作;默认有三种。 7 WHEN_EXHAUSTED_FAIL --> 表示无jedis实例时,直接抛出NoSuchElementException; 8 WHEN_EXHAUSTED_BLOCK --> 则表示阻塞住,或者达到maxWait时抛出JedisConnectionException; 9 WHEN_EXHAUSTED_GROW --> 则表示新建一个jedis实例,也就说设置的maxActive无用; 10 maxWait:表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛JedisConnectionException; 11 testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的; 12 13 14 testOnReturn:return 一个jedis实例给pool时,是否检查连接可用性(ping()); 15 16 17 testWhileIdle:如果为true,表示有一个idle object evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义; 18 19 20 timeBetweenEvictionRunsMillis:表示idle object evitor两次扫描之间要sleep的毫秒数; 21 22 23 numTestsPerEvictionRun:表示idle object evitor每次扫描的最多的对象数; 24 25 26 minEvictableIdleTimeMillis:表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义; 27 28 29 softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基础上,加入了至少minIdle个对象已经在pool里面了。如果为-1,evicted不会根据idle time驱逐任何对象。如果minEvictableIdleTimeMillis>0,则此项设置无意义,且只有在timeBetweenEvictionRunsMillis大于0时才有意义; 30 31 32 lifo:borrowObject返回对象时,是采用DEFAULT_LIFO(last in first out,即类似cache的最频繁使用队列),如果为False,则表示FIFO队列; 33 34 35 ================================================================================================================== 36 其中JedisPoolConfig对一些参数的默认设置如下: 37 testWhileIdle=true 38 minEvictableIdleTimeMills=60000 39 timeBetweenEvictionRunsMillis=30000 40 numTestsPerEvictionRun=-1