Jedis 的编程实践
跳到导航
跳到搜索
关于
Jedis 是一个高性能的 Java 客户端,是 Redis 官方推荐的 Java 开发工具。要在 Java 开发中访问 Redis 缓存服务器,必须对 Jedis 熟悉才能编写出“漂亮”的代码。 Jedis 的项目地址:https://github.com/alphazero/jredis。
使用Jedis,可以在 Maven 的 pom 文件中,增加以下依赖:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${redis.version}</version> </dependency>
Jedis 基本的使用十分简单:
- 在每次使用时,构建 Jedis 对象即可。
- 一个 Jedis 对象代表一条和 Redis 服务进行连接的 Socket 通道。
- 使用完 Jedis 对象之后,需要调用 Jedis.close() 方法把连接关闭,否则会占用系统资源。
创建 Jedis 对象时,可以指定 Redis 服务的 host,port 和 password。大致的伪代码如下:
Jedis jedis = new Jedis("localhost", 6379); // 指定Redis服务的主机和端口 jedis.auth("xxxx"); // 如果Redis服务连接需要密码,就设置密码 ... // 访问Redis服务 jedis.close(); // 使用完,就关闭连接
基础实践
Jedis 的 String操作函数、List操作函数、Hash操作函数、Set操作函数、Zset操作函数,和 Redis 客户端的相关操作命令,基本上可以一比一的相互对应。
Jedis 操作 String(字符串)
示例:
package com.crazymakercircle.redis.jedis; //...省略import public class StringDemo { /** * Jedis字符串数据类型的相关命令 */ @Test public void operateString() { Jedis jedis = new Jedis("localhost", 6379); //如果返回 pong 代表链接成功 Logger.info("jedis.ping():" + jedis.ping()); //设置key0的值为 123456 jedis.set("key0", "123456"); //返回数据类型:string Logger.info("jedis.type(key0): " + jedis.type("key0")); //取得值 Logger.info("jedis.get(key0): " + jedis.get("key0")); // key是否存在 Logger.info("jedis.exists(key0):" + jedis.exists("key0")); //返回key的长度 Logger.info("jedis.strlen(key0): " + jedis.strlen("key0")); //返回截取字符串, 范围“0,-1”表示截取全部 Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 0, -1)); //返回截取字符串, 范围“1,4”表示区间[1,4] Logger.info("jedis.getrange(key0): " + jedis.getrange("key0", 1, 4)); //追加字符串 Logger.info("jedis.append(key0): " + jedis.append("key0", "appendStr")); Logger.info("jedis.get(key0): " + jedis.get("key0")); //重命名 jedis.rename("key0", "key0_new"); //判断key 是否存在 Logger.info("jedis.exists(key0): " + jedis.exists("key0")); //批量插入 jedis.mset("key1", "val1", "key2", "val2", "key3", "100"); //批量取出 Logger.info("jedis.mget(key1,key2,key3): " + jedis.mget("key1", "key2", "key3")); //删除 Logger.info("jedis.del(key1): " + jedis.del("key1")); Logger.info("jedis.exists(key1): " + jedis.exists("key1")); //取出旧值并设置新值 Logger.info("jedis.getSet(key2): " + jedis.getSet("key2", "value3")); //自增1 Logger.info("jedis.incr(key3): " + jedis.incr("key3")); //自增15 Logger.info("jedis.incrBy(key3): " + jedis.incrBy("key3", 15)); //自减1 Logger.info("jedis.decr(key3): " + jedis.decr("key3")); //自减15 Logger.info("jedis.decrBy(key3): " + jedis.decrBy("key3", 15)); //浮点数加 Logger.info("jedis.incrByFloat(key3): " + jedis.incrByFloat("key3", 1.1)); //返回0 只有在key不存在的时候才设置 Logger.info("jedis.setnx(key3): " + jedis.setnx("key3", "existVal")); Logger.info("jedis.get(key3): " + jedis.get("key3"));// 3.1 //只有key都不存在的时候才设置,这里返回 null Logger.info("jedis.msetnx(key2,key3): " + jedis.msetnx("key2", "exists1", "key3", "exists2")); Logger.info("jedis.mget(key2,key3): " + jedis.mget("key2", "key3")); //设置key,2 秒后失效 jedis.setex("key4", 2, "2 seconds is no Val"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 2 seconds is no Val Logger.info("jedis.get(key4): " + jedis.get("key4")); jedis.set("key6", "123456789"); //第三位之后,用新值覆盖旧值(下标从0开始) jedis.setrange("key6", 3, "abcdefg"); //返回:123abcdefg Logger.info("jedis.get(key6): " + jedis.get("key6")); //返回所有匹配的key Logger.info("jedis.get(key*): " + jedis.keys("key*")); // 关闭连接 jedis.close(); } }
Jedis 操作 List(列表)
示例:
package com.crazymakercircle.redis.jedis; //... public class ListDemo { /** * Redis列表是简单的字符串列表,按照插入顺序排序。 */ @Test public void operateList() { Jedis jedis = new Jedis("localhost"); Logger.info("jedis.ping(): " +jedis.ping()); jedis.del("list1"); //从list列表尾部添加3个元素 jedis.rpush("list1", "zhangsan", "lisi", "wangwu"); //取得类型:list Logger.info("jedis.type(): " +jedis.type("list1")); //遍历区间[0,-1],取得全部的元素 Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1)); //遍历区间[1,2],取得区间的元素 Logger.info("jedis.lrange(1,2): " +jedis.lrange("list1", 1, 2)); //获取list列表的长度 Logger.info("jedis.llen(list1): " +jedis.llen("list1")); //获取下标为 1 的元素 Logger.info("jedis.lindex(list1,1): " +jedis.lindex("list1", 1)); //左侧弹出元素 Logger.info("jedis.lpop(): " +jedis.lpop("list1")); //右侧弹出元素 Logger.info("jedis.rpop(): " +jedis.rpop("list1")); //设置下标为0的元素val jedis.lset("list1", 0, "lisi2"); //最后,遍历区间[0,-1],取得全部的元素 Logger.info("jedis.lrange(0,-1): " +jedis.lrange("list1", 0, -1)); // 关闭连接 jedis.close(); } }
- 运行结果:
[main|ListDemo.operateList] |>jedis.ping(): PONG [main|ListDemo.operateList] |>jedis.type(): list [main|ListDemo.operateList] |>jedis.lrange(0,-1): [zhangsan, lisi, wangwu] [main|ListDemo.operateList] |>jedis.lrange(1,2): [lisi, wangwu] [main|ListDemo.operateList] |>jedis.llen(list1): 3 [main|ListDemo.operateList] |>jedis.lindex(list1,1): lisi [main|ListDemo.operateList] |>jedis.lpop(): zhangsan [main|ListDemo.operateList] |>jedis.rpop(): wangwu [main|ListDemo.operateList] |>jedis.lrange(0,-1): [lisi2]
Jedis 操作 Hash(哈希表)
示例:
package com.crazymakercircle.redis.jedis; //… public class HashDemo { /** * Redis hash 哈希表是一个string类型的field字段和value值的映射表, * hash特别适合用于存储对象。 * Redis 中每个hash可以存储 2^32 - 1 键-值对(40多亿) */ @Test public void operateHash() { Jedis jedis = new Jedis("localhost"); jedis.del("config"); //设置hash的 field-value对:ip=127.0.0.1 jedis.hset("config", "ip", "127.0.0.1"); //取得hash的 field关联的value值 Logger.info("jedis.hget(): " + jedis.hget("config", "ip")); //取得类型:hash Logger.info("jedis.type(): " + jedis.type("config")); //批量添加 field-value 对,参数为java map Map<String, String> configFields = new HashMap<String, String>(); configFields.put("port", "8080"); configFields.put("maxalive", "3600"); configFields.put("weight", "1.0"); //执行批量添加 jedis.hmset("config", configFields); //批量获取:取得全部 field-value 对,返回 java map映射表 Logger.info("jedis.hgetAll(): " + jedis.hgetAll("config")); //批量获取:取得部分 field对应的value,返回 java map Logger.info("jedis.hmget(): " + jedis.hmget("config", "ip", "port")); //浮点数加: 类似于String的incrByFloat jedis.hincrByFloat("config", "weight", 1.2); Logger.info("jedis.hget(weight): " + jedis.hget("config", "weight")); //获取所有的 field 字段 Logger.info("jedis.hkeys(config): " + jedis.hkeys("config")); //获取所有的 value 字段 Logger.info("jedis.hvals(config): " + jedis.hvals("config")); //获取长度 Logger.info("jedis.hlen(): " + jedis.hlen("config")); //判断field是否存在 Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight")); //删除一个field jedis.hdel("config", "weight"); Logger.info("jedis.hexists(weight): " + jedis.hexists("config", "weight")); // 关闭连接 jedis.close(); } }
- 运行结果:
[main|HashDemo.operateHash] |>jedis.hget(): 127.0.0.1 [main|HashDemo.operateHash] |>jedis.type(): hash [main|HashDemo.operateHash] |>jedis.hgetAll(): {port=8080, weight=1.0, maxalive=3600, ip=127.0.0.1} [main|HashDemo.operateHash] |>jedis.hmget(): [127.0.0.1, 8080] [main|HashDemo.operateHash] |>jedis.hget(): 2.2 [main|HashDemo.operateHash] |>jedis.hkeys(): [weight, maxalive, port, ip] [main|HashDemo.operateHash] |>jedis.hvals(): [127.0.0.1, 8080, 2.2, 3600] [main|HashDemo.operateHash] |>jedis.hlen(): 4 [main|HashDemo.operateHash] |>jedis.hexists(weight): true [main|HashDemo.operateHash] |>jedis.hexists(weight): false
Jedis 操作 Set(集合)
示例:
package com.crazymakercircle.redis.jedis; //....省略import public class SetDemo { /** * Redis 的 Set集合是 String 类型的无序集合。 * 集合成员是唯一的,集合中不能出现重复的元素。 * Set集合是通过哈希表实现的,添加,删除,查找的复杂度都是 O(1)。 */ @Test public void operateSet() { Jedis jedis = new Jedis("localhost"); jedis.del("set1"); Logger.info("jedis.ping(): " + jedis.ping()); Logger.info("jedis.type(): " + jedis.type("set1")); //sadd函数: 向集合添加元素 jedis.sadd("set1", "user01", "user02", "user03"); //smembers函数: 遍历所有元素 Logger.info("jedis.smembers(): " + jedis.smembers("set1")); //scard函数: 获取集合元素个数 Logger.info("jedis.scard(): " + jedis.scard("set1")); //sismember判断是否是集合元素 Logger.info("jedis.sismember(user04): " + jedis.sismember("set1", "user04")); //srem函数:移除元素 Logger.info("jedis.srem(): " + jedis.srem("set1", "user02", "user01")); //smembers函数: 遍历所有元素 Logger.info("jedis.smembers(): " + jedis.smembers("set1")); // 关闭连接 jedis.close(); } }
- 运行结果:
[main|SetDemo.operateSet] |>jedis.ping(): PONG [main|SetDemo.operateSet] |>jedis.type(): none [main|SetDemo.operateSet] |>jedis.smembers(): [user02, user03, user01] [main|SetDemo.operateSet] |>jedis.scard(): 3 [main|SetDemo.operateSet] |>jedis.sismember(user04): false [main|SetDemo.operateSet] |>jedis.srem(): 2 [main|SetDemo.operateSet] |>jedis.smembers(): [user03]
Jedis 操作 Zset(有序集合)
示例:
- 有一个 salary 薪资的 Zset 有序集合,Zset 的 Key 键为用户id,Zset 的 score 分数值保存的是用户的薪资:
package com.crazymakercircle.redis.jedis; //... public class ZSetDemo { /** * Zset有序集合和Set集合都是string类型元素的集合,且不允许重复的元素。 * 不同的是Zset的每个元素都会关联一个double类型的分数,用于从小到大进行排序。 * 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 * 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个元素)。 */ @Test public void operateZset() { Jedis jedis = new Jedis("localhost"); Logger.info("jedis.ping(): " + jedis.ping()); jedis.del("salary"); Map<String, Double> members = new HashMap<String, Double>(); members.put("u01", 1000.0); members.put("u02", 2000.0); members.put("u03", 3000.0); members.put("u04", 13000.0); members.put("u05", 23000.0); //批量添加元素,类型为java map映射表 jedis.zadd("salary", members); //type类型:Zset Logger.info("jedis.type(): " + jedis.type("salary")); //获取集合元素的个数 Logger.info("jedis.zcard(): " + jedis.zcard("salary")); //按照下标[起,止]遍历元素 Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1)); //按照下标[起,止]倒序遍历元素 Logger.info("jedis.zrevrange(): " + jedis.zrevrange("salary", 0, -1)); //按照分数(薪资)[起,止]遍历元素 Logger.info("jedis.zrangeByScore(): " + jedis.zrangeByScore("salary", 1000, 10000)); //按照薪资[起,止]遍历元素,带分数返回 Set<Tuple> res0 = jedis.zrangeByScoreWithScores("salary", 1000, 10000); for (Tuple temp : res0) { Logger.info("Tuple.get(): " + temp.getElement() + " -> " + temp.getScore()); } //按照分数[起,止]倒序遍历元素 Logger.info("jedis.zrevrangeByScore(): " + jedis.zrevrangeByScore("salary", 1000, 4000)); //获取元素[起,止]分数区间的元素数量 Logger.info("jedis.zcount(): " + jedis.zcount("salary", 1000, 4000)); //获取元素score值:薪资 Logger.info("jedis.zscore(): " + jedis.zscore("salary", "u01")); //获取元素的下标 Logger.info("jedis.zrank(u01): " + jedis.zrank("salary", "u01")); //倒序获取元素的下标 Logger.info("jedis.zrevrank(u01): " + jedis.zrevrank("salary", "u01")); //删除元素 Logger.info("jedis.zrem(): " + jedis.zrem("salary", "u01", "u02")); //删除元素,通过下标范围 Logger.info("jedis.zremrangeByRank(): " + jedis.zremrangeByRank("salary", 0, 1)); //删除元素,通过分数范围 Logger.info("jedis.zremrangeByScore(): " + jedis.zremrangeByScore("salary", 20000, 30000)); //按照下标[起,止]遍历元素 Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1)); Map<String, Double> members2 = new HashMap<String, Double>(); members2.put("u11", 1136.0); members2.put("u12", 2212.0); members2.put("u13", 3324.0); //批量添加元素 jedis.zadd("salary", members2); //增加指定分数 Logger.info("jedis.zincrby(10000): " + jedis.zincrby("salary", 10000, "u13")); //按照下标[起,止]遍历元素 Logger.info("jedis.zrange(): " + jedis.zrange("salary", 0, -1)); // 关闭连接 jedis.close(); } }
- 运行结果:
[main|ZSetDemo.operateZset] |>jedis.ping(): PONG [main|ZSetDemo.operateZset] |>jedis.type(): zset [main|ZSetDemo.operateZset] |>jedis.zcard(): 5 [main|ZSetDemo.operateZset] |>jedis.zrange(): [u01, u02, u03, u04, u05] [main|ZSetDemo.operateZset] |>jedis.zrangeByScore(): [u01, u02, u03] [main|ZSetDemo.operateZset] |>Tuple.get(): u01 -> 1000.0 [main|ZSetDemo.operateZset] |>Tuple.get(): u02 -> 2000.0 [main|ZSetDemo.operateZset] |>Tuple.get(): u03 -> 3000.0 [main|ZSetDemo.operateZset] |>jedis.zrevrange(): [u05, u04, u03, u02, u01] [main|ZSetDemo.operateZset] |>jedis.zrevrangeByScore(): [] [main|ZSetDemo.operateZset] |>jedis.zscore(): 1000.0 [main|ZSetDemo.operateZset] |>jedis.zcount(): 3 [main|ZSetDemo.operateZset] |>jedis.zrank(u01): 0 [main|ZSetDemo.operateZset] |>jedis.zrevrank(u01): 4 [main|ZSetDemo.operateZset] |>jedis.zrem(): 2 [main|ZSetDemo.operateZset] |>jedis.zremrangeByRank(): 2 [main|ZSetDemo.operateZset] |>jedis.zremrangeByScore(): 1 [main|ZSetDemo.operateZset] |>jedis.zrange(): [] [main|ZSetDemo.operateZset] |>jedis.get(): 13324.0 [main|ZSetDemo.operateZset] |>jedis.zrange(): [u11, u12, u13]
JedisPool连接池
使用 Jedis API 可以方便地在 Java 程序中操作 Redis,就像通过 JDBC API 操作数据库一样。但是,仅仅实现这一点还是不够的。 为什么呢? 大家知道,数据库连接的底层是一条 Socket 通道,创建和销毁很耗时。 在数据库连接过程中,为了防止数据库连接的频繁创建、销毁带来的性能损耗,常常会用到连接池(Connection Pool),例如淘宝的 Druid 连接池、Tomcat 的 DBCP 连接池。 Jedis 连接和数据库连接一样,也需要使用连接池(Connection Pool)来管理。
Jedis 开源库提供了一个负责管理 Jedis 连接对象的池,名为JedisPool 类,位于 redis.clients.jedis 包中。
JedisPool:配置
在使用 JedisPool 类创建 Jedis 连接池之前,首先要了解一个很重要的配置类——JedisPoolConfig 配置类,它也位于 redis.clients.jedis 包中。这个连接池的配置类负责配置 JedisPool 的参数。
JedisPoolConfig 配置类涉及到很多与连接管理和使用有关的参数:
- maxTotal:资源池中最大的连接数,默认值为 8。
- maxIdle:资源池允许最大空闲的连接数,默认值为 8。
- minIdle:资源池确保最少空闲的连接数,默认值为 0。
- 如果 JedisPool 开启了空闲连接的有效性检测,如果空闲连接无效,就销毁。销毁连接后,连接数量就少了,如果小于 minIdle 数量,就新建连接,维护数量不少于 minIdle 的数量。
- minIdle 确保了线程池中有最小的空闲 Jedis 实例的数量。
- blockWhenExhausted:当资源池用尽后,调用者是否要等待,默认值为 true。
- 当为 true 时,maxWaitMillis 才会生效。
- maxWaitMillis:当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)。
- 默认值为 -1,表示永不超时,不建议使用默认值。
- testOnBorrow:向资源池借用连接时,是否做有效性检测(ping命令):如果是无效连接,会被移除。
- 默认值为 false,表示不做检测。
- 如果为 true,则得到的 Jedis 实例均是可用的。
- 在业务量小的应用场景,建议设置为 true,确保连接可用;在业务量很大的应用场景,建议设置为 false(默认值),少一次 ping 命令的开销,有助于提升性能。
- testOnReturn:向资源池归还连接时,是否做有效性检测(ping命令):如果是无效连接,会被移除。
- 默认值为 false,表示不做检测。
- 同样,在业务量很大的应用场景,建议设置为 false(默认值),少一次 ping 命令的开销。
- testWhileIdle:如果为 true,表示用一个专门的线程对空闲的连接进行有效性的检测扫描,如果有效性检测失败,即表示无效连接,会从资源池中移除。
- 默认值为true,表示进行空闲连接的检测。
- 这个选项存在一个附加条件,需要配置项 timeBetweenEvictionRunsMillis 的值大于 0;否则,testWhileIdle 不会生效。
- timeBetweenEvictionRunsMillis:表示两次空闲连接扫描的活动之间,要睡眠的毫秒数,默认为30000毫秒,也就是30秒钟。
- minEvictableIdleTimeMillis:表示一个 Jedis 连接至少停留在空闲状态的最短时间,然后才能被空闲连接扫描线程进行有效性检测,默认值为 60000 毫秒,即 60 秒。
- 也就是说在默认情况下,一条 Jedis 连接只有在空闲 60 秒后,才会参与空闲线程的有效性检测。
- 这个选项存在一个附加条件,需要在 timeBetweenEvictionRunsMillis 大于 0 时才会生效。也就是说,如果不启动空闲检测线程,这个参数也没有什么意义。
- numTestsPerEvictionRun:表示空闲检测线程每次最多扫描的 Jedis 连接数,默认值为 -1,表示扫描全部的空闲连接。
- 空闲扫描的选项在 JedisPoolConfig 的构造器中都有默认值,具体如下:
package redis.clients.jedis; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { this.setTestWhileIdle(true); this.setMinEvictableIdleTimeMillis(60000L); this.setTimeBetweenEvictionRunsMillis(30000L); this.setNumTestsPerEvictionRun(-1); } }
- 空闲扫描的选项在 JedisPoolConfig 的构造器中都有默认值,具体如下:
- jmxEnabled:是否开启 jmx 监控,默认值为 true,建议开启。
关于“maxTotal”
有个实际的问题:如何推算一个连接池的最大连接数 maxTotal 呢?
实际上,这是一个很难精准回答的问题,主要是依赖的因素比较多。大致的推算方法是:“业务QPS / 单连接的QPS = 最大连接数”。 如何推算单个 Jedis 连接的 QPS 呢? 假设一个Jedis命令操作的时间约为 5ms(包含:borrow + return + Jedis执行命令 + 网络延迟),那么,单个 Jedis 连接的 QPS 大约是“1000 / 5 = 200”。 所以,如果业务期望的 QPS 是 100000,则需要的最大连接数为“100000 / 200 = 500”。
事实上,上面的估算仅仅是个理论值。在实际的生产场景中,还要预留一些资源,通常来讲所配置的 maxTotal 要比理论值大一些。
- 如果连接数确实太多,可以考虑 Redis 集群,那么单个 Redis 节点的最大连接数的公式为:“maxTotal = 预估的连接数 / nodes节点数”。
在并发量不大时,maxTotal 设置过高会导致不必要的连接资源的浪费。可以根据实际总 QPS 和 nodes 节点数,合理评估每个节点所使用的最大连接数。
关于“maxIdle”
再看一个问题:如何推算连接池的最大空闲连接数 maxIdle 值呢?
实际上,maxTotal 只是给出了一个连接数量的上限,maxIdle 实际上才是业务可用的最大连接数,从这个层面来说,maxIdle不能设置过小,否则会有创建、销毁连接的开销。 使得连接池达到最佳性能的设置是“maxTotal = maxIdle”,应尽可能地避免由于频繁地创建和销毁 Jedis 连接所带来的连接池性能的下降。
JedisPool:创建、预热
关于预热: 虽然 JedisPool 定义了最大空闲资源数、最小空闲资源数,但是在创建的时候,不会真的创建好 Jedis 连接并放到 JedisPool 池子里。这样会导致一个问题: 刚创建好的连接池,池子没有 Jedis 连接资源在使用,在初次访问请求到来的时候,才开始创建新的连接,不过,这样会导致一定的时间开销。 为了提升初次访问的性能,可以考虑在 JedisPool 创建后,为 JedisPool 提前进行预热,一般以最小空闲数量作为预热数量。
JedisPool 的创建和预热:
- 以 JedisPoolConfig 实例、Redis IP、Redis端口和其他可选选项(如超时时间、Auth密码)为参数,构造一个 JedisPool 连接池实例;
- 以最小空闲数量(即:minIdle 配置项)对 JedisPool 进行预热;
- 定义一个从连接池中获取 Jedis 连接的新方法——“getJedis()”,供其他模块调用。
代码实现:
package com.crazymakercircle.redis.jedisPool;
//...
public class JredisPoolBuilder {
public static final int MAX_IDLE = 50;
public static final int MAX_TOTAL = 50;
private static JedisPool pool = null;
static {
// 创建连接池
buildPool();
// 预热连接池
hotPool();
}
// 创建连接池
private static JedisPoolbuildPool() {
if (pool == null) {
long start = System.currentTimeMillis();
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(MAX_TOTAL);
config.setMaxIdle(MAX_IDLE);
config.setMaxWaitMillis(1000 * 10);
// 在borrow一个jedis实例时,是否提前进行有效检测操作;
// 如果为true,则得到的jedis实例均是可用的;
config.setTestOnBorrow(true);
//new JedisPool(config, ADDR, PORT, TIMEOUT);
pool = new JedisPool(config, "127.0.0.1", 6379, 10000);
long end = System.currentTimeMillis();
Logger.info("buildPool毫秒数:", end - start);
}
return pool;
}
// 连接池的预热
public static void hotPool() {
long start = System.currentTimeMillis();
List<Jedis> minIdleJedisList = new ArrayList<Jedis>(MAX_IDLE);
Jedis jedis = null;
for (int i = 0; i< MAX_IDLE; i++) {
try {
jedis = pool.getResource();
minIdleJedisList.add(jedis);
jedis.ping();
} catch (Exception e) {
Logger.error(e.getMessage());
} finally {
}
}
for (int i = 0; i< MAX_IDLE; i++) {
try {
jedis = minIdleJedisList.get(i);
jedis.close();
} catch (Exception e) {
Logger.error(e.getMessage());
} finally {
}
}
long end = System.currentTimeMillis();
Logger.info(" hotPool毫秒数:", end - start);
}
//获取连接
public static JedisgetJedis() {
return pool.getResource();
}
}
JedisPool:使用
JedisPool 的使用:
- 可以使用前面定义好的“getJedis()”方法,间接地通过“pool.getResource()”从连接池获取连接;也可以直接通过“pool.getResource()”方法获取 Jedis 连接。
- Jedis 连接使用完后,一定要调用“close()”方法关闭连接。
- 这个关闭操作不是真正地关闭连接,而是归还给连接池。这一点和使用数据库连接池是一样的。
- 一般来说,关闭操作放在 finally 代码段中,确保 Jedis 的关闭最终都会被执行到。
示例:
package com.crazymakercircle.redis.jedisPool; //... public class JredisPoolTester { public static final int NUM = 200; public static final String ZSET_KEY = "zset1"; // 测试删除 @Test public void testDel() { Jedisredis =null; try { redis = JredisPoolBuilder.getJedis(); long start = System.currentTimeMillis(); redis.del(ZSET_KEY); long end = System.currentTimeMillis(); Logger.info("删除 zset1 毫秒数:", end - start); } finally { // 使用后一定关闭,还给连接池 if (redis != null) { redis.close(); } } } //... }
由于 Jedis 类实现了java.io.Closeable接口,故而在 JDK 1.7 或者以上版本中可以使用 try-with-resources 语句,在其隐藏的 finally 部分自动调用 close 方法。
示例:
package com.crazymakercircle.redis.jedisPool; //... public class JredisPoolTester { public static final int NUM = 200; public static final String ZSET_KEY = "zset1"; // 测试创建Zset @Test public void testSet() { testDel(); // 首先删除之前创建的Zset try (Jedis redis = JredisPoolBuilder.getJedis()) { int loop = 0; long start = System.currentTimeMillis(); while (loop < NUM) { redis.zadd(ZSET_KEY, loop, "field-" + loop); loop++; } long end = System.currentTimeMillis(); Logger.info("设置Zset :", loop, "次, 毫秒数:", end - start); } } //... }