查看“Jedis 的编程实践”的源代码
←
Jedis 的编程实践
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Redis]] == 关于 == Jedis 是一个高性能的 Java 客户端,是 Redis 官方推荐的 Java 开发工具。要在 Java 开发中访问 Redis 缓存服务器,必须对 Jedis 熟悉才能编写出“漂亮”的代码。 Jedis 的项目地址:[https://github.com/alphazero/jredis https://github.com/alphazero/jredis]。 使用Jedis,可以在 Maven 的 pom 文件中,增加以下依赖: : <syntaxhighlight lang="xml" highlight=""> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>${redis.version}</version> </dependency> </syntaxhighlight> Jedis 基本的使用十分简单: * 在每次使用时,构建 Jedis 对象即可。 * 一个 Jedis 对象代表一条和 Redis 服务进行连接的 Socket 通道。 * 使用完 Jedis 对象之后,需要调用 '''Jedis.close()''' 方法把连接关闭,否则会占用系统资源。 创建 Jedis 对象时,可以指定 Redis 服务的 host,port 和 password。大致的伪代码如下: : <syntaxhighlight lang="Java" highlight=""> Jedis jedis = new Jedis("localhost", 6379); // 指定Redis服务的主机和端口 jedis.auth("xxxx"); // 如果Redis服务连接需要密码,就设置密码 ... // 访问Redis服务 jedis.close(); // 使用完,就关闭连接 </syntaxhighlight> == 基础实践 == Jedis 的 String操作函数、List操作函数、Hash操作函数、Set操作函数、Zset操作函数,和 Redis 客户端的相关操作命令,基本上可以一比一的相互对应。 === Jedis 操作 String(字符串) === 示例: : <syntaxhighlight lang="Java" highlight="" line> 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(); } } </syntaxhighlight> === Jedis 操作 List(列表) === 示例: : <syntaxhighlight lang="Java" highlight="" line> 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(); } } </syntaxhighlight> : 运行结果: : <syntaxhighlight lang="Java" highlight=""> [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] </syntaxhighlight> === Jedis 操作 Hash(哈希表) === 示例: : <syntaxhighlight lang="Java" highlight="23,28" line> 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(); } } </syntaxhighlight> : 运行结果: : <syntaxhighlight lang="Java" highlight=""> [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 </syntaxhighlight> === Jedis 操作 Set(集合) === 示例: : <syntaxhighlight lang="Java" highlight="" line> 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(); } } </syntaxhighlight> : 运行结果: : <syntaxhighlight lang="Java" highlight=""> [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] </syntaxhighlight> === Jedis 操作 Zset(有序集合) === 示例: : 有一个 salary 薪资的 Zset 有序集合,Zset 的 Key 键为用户id,Zset 的 score 分数值保存的是用户的薪资: : <syntaxhighlight lang="Java" highlight="17,24" line> 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(); } } </syntaxhighlight> : 运行结果: : <syntaxhighlight lang="Java" highlight=""> [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] </syntaxhighlight> == 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 的构造器中都有默认值,具体如下: #*: <syntaxhighlight lang="Java" highlight=""> 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); } } </syntaxhighlight> # '''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()”,供其他模块调用。 代码实现: <syntaxhighlight lang="Java" highlight=""> 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(); } } </syntaxhighlight> === JedisPool:使用 === JedisPool 的使用: # 可以使用前面定义好的“'''getJedis()'''”方法,间接地通过“pool.getResource()”从连接池获取连接;也可以直接通过“'''pool.getResource()'''”方法获取 Jedis 连接。 # Jedis 连接使用完后,一定要调用“'''close()'''”方法关闭连接。 #* 这个关闭操作不是真正地关闭连接,而是归还给连接池。这一点和使用数据库连接池是一样的。 #* 一般来说,关闭操作放在 '''finally''' 代码段中,确保 Jedis 的关闭最终都会被执行到。 示例: : <syntaxhighlight lang="Java" highlight=""> 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(); } } } //... } </syntaxhighlight> 由于 Jedis 类实现了'''java.io.Closeable'''接口,故而在 JDK 1.7 或者以上版本中可以使用 '''try-with-resources''' 语句,在其隐藏的 finally 部分自动调用 close 方法。 示例: : <syntaxhighlight lang="Java" highlight=""> 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); } } //... } </syntaxhighlight>
返回至“
Jedis 的编程实践
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息