jedis使用指南.docx
《jedis使用指南.docx》由会员分享,可在线阅读,更多相关《jedis使用指南.docx(10页珍藏版)》请在冰豆网上搜索。
jedis使用指南
jedis使用指南
概述
本文基于jedis-2.1.0和commons-pool-1.5.5。
本文首先会剖析jedis对redis的支持,透过jedis群集之后,那些redis的能力会受到限制,因此第一个部分会重点介绍jedis的关键特性。
对象池的配置会决定jedis的最终性能,而jedis的对象池的实现是基于apache的commons-pool。
本文会重点介绍commons-pool的配置能力,以便为jedis群集提供更好的配置参数。
另外,jedis还有一些扩展包,可以在特定场景下增强jedis的能力,使我们面对特定的应用场景的时候,会有更好的选择。
所以,文档的最后会涉及如何使用jedis的扩展包。
jedis的关键特性
主要是API支持范围,pipeline和transaction的使用,以及对pub/sub的支持。
最后,介绍jedis的客户端群集:
ShardedJedis。
完整的redisAPI支持
jedis首先是redis的java客户端,jedis提供了完整的redis的javaAPI,可以支持完整的redis客户端API。
例如,通过jedis,可以设置redis的主从关系:
jedis.slaveOf("localhost",6379);//ifthemasterisonthesamePCwhichrunsyourcode
jedis.slaveOf("192.168.1.35",6379);
使用pipeline
如果希望一次发送一批redis命令,一种有效的方式是使用pipeline。
jedis使用pipeline的代码如下:
Pipelinep=jedis.pipelined();
p.set("fool","bar");
p.zadd("foo",1,"barowitch");p.zadd("foo",0,"barinsky");p.zadd("foo",0,"barikoviev");
ResponsepipeString=p.get("fool");
Response>sose=p.zrange("foo",0,-1);
p.sync();
intsoseSize=sose.get().size();
SetsetBack=sose.get();
使用transaction
如果希望一些命令一起执行而不被干扰,可以通过transaction将命令打包到一起执行:
jedis.watch(key1,key2,...);
BinaryTransactiont=jedis.multi();
t.set("foo","bar");
t.exec();
如果需要得到返回值,可以参考下面的代码:
Transactiont=jedis.multi();
t.set("fool","bar");
Responseresult1=t.get("fool");
t.zadd("foo",1,"barowitch");t.zadd("foo",0,"barinsky");t.zadd("foo",0,"barikoviev");
Response>sose=t.zrange("foo",0,-1);//gettheentiresortedset
t.exec();//dontforgetit
Stringfoolbar=result1.get();//useResponse.get()toretrievethingsfromaResponse
intsoseSize=sose.get().size();//onsose.get()youcandirectlycallSetmethods!
Publish/Subscribe
如果需要订阅redis的channel,可以创建一个JedisPubSub的派生类的实例,并在jedis上调用其subscribe方法:
classMyListenerextendsJedisPubSub{
publicvoidonMessage(Stringchannel,Stringmessage){
}
publicvoidonSubscribe(Stringchannel,intsubscribedChannels){
}
publicvoidonUnsubscribe(Stringchannel,intsubscribedChannels){
}
publicvoidonPSubscribe(Stringpattern,intsubscribedChannels){
}
publicvoidonPUnsubscribe(Stringpattern,intsubscribedChannels){
}
publicvoidonPMessage(Stringpattern,Stringchannel,
Stringmessage){
}
}
MyListenerl=newMyListener();
jedis.subscribe(l,"foo");
subscribe是一个阻塞的操作。
一个JedisPubSub的实例可以订阅多个redis的channel。
ShardedJedis
简单的说,ShardedJedis是一种帮助提高读/写并发能力的群集,群集使用一致性hash来确保一个key始终被指向相同的redisserver。
每个redisserver被称为一个shard。
因为每个shard都是一个master,因此使用sharding机制会产生一些限制:
不能在sharding中直接使用jedis的transactions、pipelining、pub/sub这些API,基本的原则是不能跨越shard。
但jedis并没有在API的层面上禁止这些行为,但是这些行为会有不确定的结果。
一种可能的方式是使用keytags来干预key的分布,当然,这需要手工的干预。
另外一个限制是正在使用的shards是不能被改变的,因为所有的sharding都是预分片的。
注:
如果希望使用可以改变的shards,可以使用yaourt-dynamicshardingimplementation(一个jedis的实现分支)。
ShardedJedis的使用方法:
1.定义shards:
Listshards=newArrayList();
JedisShardInfosi=newJedisShardInfo("localhost",6379);
si.setPassword("foobared");
shards.add(si);
si=newJedisShardInfo("localhost",6380);
si.setPassword("foobared");
shards.add(si);
2.a)直接使用:
ShardedJedisjedis=newShardedJedis(shards);
jedis.set("a","foo");
jedis.disconnect;
2.b)或使用连接池:
ShardedJedisPoolpool=newShardedJedisPool(newConfig(),shards);
ShardedJedisjedis=pool.getResource();
jedis.set("a","foo");
....//doyourworkhere
pool.returnResource(jedis);
....//afewmomentslater
ShardedJedisjedis2=pool.getResource();
jedis.set("z","bar");
pool.returnResource(jedis);
pool.destroy();
判断使用的是那个shards:
ShardInfosi=jedis.getShardInfo(key);
si.getHost/getPort/getPassword/getTimeout/getName
也可以通过keytags来确保key位于相同的shard。
如:
ShardedJedisjedis=newShardedJedis(shards,
ShardedJedis.DEFAULT_KEY_TAG_PATTERN);
这样,默认的keytags是”{}”,这表示在”{}”内的字符会用于决定使用那个shard。
如:
jedis.set("foo{bar}","12345");
和
jedis.set("car{bar}","877878");
会使用同一个shard。
注:
如果key和keytag不匹配,会使用原来的key作为选择shard的key。
使用ShardedJedisPipeline
ShardedJedisPipeline其实是一个很鸡肋的功能。
为了能在ShardedJedis中平滑的支持redis的pipeline的功能,ShardedJedis通过ShardedJedisPipeline类对pipeline提供了支持。
简单的说,ShardedJedis是通过向每个用到的shard发起pipeline来实现ShardedJedisPipeline的功能,这种方式如果累积的key不够多,很难达到提高效率的目的。
如果需要在ShardedJedis中使用pipeline,还是建议尽量通过keytag将关联的key放到同一shard之中。
ShardedJedisPipeline简单的示例代码如下:
ShardedJedisjedis=newShardedJedis(shards);
ShardedJedisPipelinep=jedis.pipelined();
p.set("foo","bar");
p.get("foo");
List
//assertEquals(2,results.size());
//assertEquals("OK",results.get(0));
//assertEquals("bar",results.get
(1));
ShardedJedisPipeline相对复杂的示例代码:
ShardedJedisjedis=newShardedJedis(shards);
jedis.set("string","foo");
jedis.lpush("list","foo");
jedis.hset("hash","foo","bar");
jedis.zadd("zset",1,"foo");
jedis.sadd("set","foo");
ShardedJedisPipelinep=jedis.pipelined();
Responsestring=p.get("string");
Responsedel=p.del("string");
ResponseemptyString=p.get("string");
Responselist=p.lpop("list");
Responsehash=p.hget("hash","foo");
Response>zset=p.zrange("zset",0,-1);
Responseset=p.spop("set");
Responseblist=p.exists("list");
Responsezincrby=p.zincrby("zset",1,"foo");
Responsezcard=p.zcard("zset");
p.lpush("list","bar");
Response>lrange=p.lrange("list",0,-1);
Response
p.sadd("set","foo");
Response>smembers=p.smembers("set");
Response>zrangeWithScores=p.zrangeWithScores("zset",0,
-1);
p.sync();
assertEquals("foo",string.get());
assertEquals(Long.valueOf
(1),del.get());
assertNull(emptyString.get());
assertEquals("foo",list.get());
assertEquals("bar",hash.get());
assertEquals("foo",zset.get().iterator().next());
assertEquals("foo",set.get());
assertFalse(blist.get());
assertEquals(Double.valueOf
(2),zincrby.get());
assertEquals(Long.valueOf
(1),zcard.get());
assertEquals(1,lrange.get().size());
assertNotNull(hgetAll.get().get("foo"));
assertEquals(1,smembers.get().size());
assertEquals(1,zrangeWithScores.get().size());
使用jedis的对象池
jedis通过commons-pool来提供其对象池的功能,其对象池类有JedisPool和ShardedJedisPool,面向普通的redis连接池和pre-sharding的redis连接池。
在连接池的使用和配置层面,这两个类基本没什么差别。
配置jedis的连接池,一般通过JedisPoolConfig类完成,其提供了一个不同于基类的默认值,当然也可以通过mons.pool.impl.GenericObjectPool.Config类来配置,这个类的默认值我们可以在commons-pool对象池配置的小节中看到。
对象池的使用
jedis创建对象池的方式:
JedisPoolpool=newJedisPool(newJedisPoolConfig(),"localhost");
使用池中的对象,是通过JedisPool的getResource和returnResource来得到和归还资源:
Jedisjedis=pool.getResource();
try{
///...dostuffhere...forexample
jedis.set("foo","bar");
Stringfoobar=jedis.get("foo");
jedis.zadd("sose",0,"car");jedis.zadd("sose",0,"bike");
Setsose=jedis.zrange("sose",0,-1);
}catch(JedisConnectionExceptione){
//returnBrokenResourcewhenthestateoftheobjectisunrecoverable
if(null!
=jedis){
pool.returnBrokenResource(jedis);
jedis=null;
}
}finally{
///...it'simportanttoreturntheJedisinstancetothepoolonceyou'vefinishedusingit
if(null!
=jedis)
pool.returnResource(jedis);
}
///...whenclosingyourapplication:
pool.destroy();
commons-pool对象池配置
jedis的对象池是通过apache的commons-pool实现的。
其对象池的配置是通过mons.pool.impl.GenericObjectPool.Config类完成。
Config是一个简单的值对象类,其成员都有预设的默认值。
我们将Config类的各个成员的配置含义描述如下:
⏹maxActive
控制池中对象的最大数量。
默认值是8,如果是负值表示没限制。
⏹maxIdle
控制池中空闲的对象的最大数量。
默认值是8,如果是负值表示没限制。
⏹minIdle
控制池中空闲的对象的最小数量。
默认值是0。
⏹whenExhaustedAction
指定池中对象被消耗完以后的行为,有下面这些选择:
>>WHEN_EXHAUSTED_FAIL0
>>WHEN_EXHAUSTED_GROW2
>>WHEN_EXHAUSTED_BLOCK1
如果是WHEN_EXHAUSTED_FAIL,当池中对象达到上限以后,继续borrowObject会抛出NoSuchElementException异常。
如果是WHEN_EXHAUSTED_GROW,当池中对象达到上限以后,会创建一个新对象,并返回它。
如果是WHEN_EXHAUSTED_BLOCK,当池中对象达到上限以后,会一直等待,直到有一个对象可用。
这个行为还与maxWait有关,如果maxWait是正数,那么会等待maxWait的毫秒的时间,超时会抛出NoSuchElementException异常;如果maxWait为负值,会永久等待。
whenExhaustedAction的默认值是WHEN_EXHAUSTED_BLOCK,maxWait的默认值是-1。
⏹maxWait
whenExhaustedAction如果是WHEN_EXHAUSTED_BLOCK,指定等待的毫秒数。
如果maxWait是正数,那么会等待maxWait的毫秒的时间,超时会抛出NoSuchElementException异常;如果maxWait为负值,会永久等待。
maxWait的默认值是-1。
⏹testOnBorrow
如果testOnBorrow被设置,pool会在borrowObject返回对象之前使用PoolableObjectFactory的validateObject来验证这个对象是否有效,要是对象没通过验证,这个对象会被丢弃,然后重新选择一个新的对象。
testOnBorrow的默认值是false。
⏹testOnReturn
如果testOnReturn被设置,pool会在returnObject的时候通过PoolableObjectFactory的validateObject方法验证对象,如果对象没通过验证,对象会被丢弃,不会被放到池中。
testOnReturn的默认值是false。
⏹testWhileIdle
指定idle对象是否应该使用PoolableObjectFactory的validateObject校验,如果校验失败,这个对象会从对象池中被清除。
这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。
testWhileIdle的默认值是false。
⏹timeBetweenEvictionRunsMillis
指定驱逐线程的休眠时间。
如果这个值不是正数(>0),不会有驱逐线程运行。
timeBetweenEvictionRunsMillis的默认值是-1。
⏹numTestsPerEvictionRun
设置驱逐线程每次检测对象的数量。
这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。
numTestsPerEvictionRun的默认值是3。
⏹minEvictableIdleTimeMillis
指定最小的空闲驱逐的时间间隔(空闲超过指定的时间的对象,会被清除掉)。
这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效。
minEvictableIdleTimeMillis默认值是30分钟。
⏹softMinEvictableIdleTimeMillis
与minEvictableIdleTimeMillis类似,也是指定最小的空闲驱逐的时间间隔(空闲超过指定的时间的对象,会被清除掉),不过会参考minIdle的值,只有idle对象的数量超过minIdle的值,对象才会被清除。
这个设置仅在timeBetweenEvictionRunsMillis被设置成正值(>0)的时候才会生效,并且这个配置能被minEvictableIdleTimeMillis配置取代(minEvictableIdleTimeMillis配置项的优先级更高)。
softMinEvictableIdleTimeMillis的默认值是-1。
⏹lifo
pool可以被配置成LIFO队列(last-in-first-out)或FIFO队列(first-in-first-out),来指定空闲对象被使用的次序。
lifo的默认值是true。
JedisPoolConfig的调整
jedis的对象池是通过commons-pool实现的,对对象池的配置应该通过JedisPoolConfig来完成,jedis提供了自己的配置参数:
publicclassJedisPoolConfigextendsConfig{
publicJedisPoolConfig(){
//defaultstomakeyourlifewithconnectionpooleasier:
)
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerE