Redis-Java客户端Jedis
Redis-Java客户端Jedis
一.Jedis的下载方式
Maven管理下,在pom文件中引入如下依赖即可:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9</version>
</dependency>
二.Jedis的基本API
2.1 Jedis初始化
Jedis jedis = new jedis(String host, Stringport)
Jedis jedis = new jedis(String host,String port,intconnectionTimeout,final int soTimeout)
参数说明:
ip:redis服务器的ip地址
port:redis服务器的端口号
connectionTimeout:客户端连接超时
soTimeout:客户端读写超时
2.2 基本操作
三 Jedis对象序列化
3.1 jedis的序列化支持
Jedis本身没有提供序列化工具,但是他提供了操作字节数组的API
如下:
public String set(final String key, String value)
public String set(final byte[] key, byte[] value)
public byte[] get(final byte[] key)
public String set(final String key)
我们可以引入第三方序列化工具(如xml,json,protobuf,thrift等等,也可以自己实现,来完成对象类型数据的序列化和反序列化工作。
3.2 序列化步骤
我们可以引入第三方序列化工具(如xml,json,protobuf,thrift等等,也可以自己实现,来完成对象类型数据的序列化和反序列化工作。
下面以protostuff(ProtoBuf的java客户端)为例进行说明
1) protostuff的maven依赖
<protostuff.version>1.0.11</protostuff.version>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>${protostuff.version}</version>
</dependency>
(2)定义实体类
public classClub {
privateintid;
privateString name;
privateString info;
privateDate createDate;
privateintrank;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
}
(3)提供序列化工具进行序列化和反序列化服务
/**
*
*/
package com.example.serializationtools;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
/**
* Protistuff序列化工具
* @author shl
*
*/
public class ProtostuffSerializer {
public static <T> byte[]serialize(T obj) {
if (obj == null) {
throw newRuntimeException("序列化对象("+ obj + ")!");
}
@SuppressWarnings("unchecked")
Schema<T> schema =(Schema<T>) RuntimeSchema.getSchema(obj.getClass());
LinkedBuffer buffer =LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
try {
protostuff =ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw newRuntimeException("序列化(" +obj.getClass() + ")对象(" + obj +")发生异常!", e);
} finally {
buffer.clear();
}
return protostuff;
}
public static <T> Tdeserialize(byte[] paramArrayOfByte, Class<T> targetClass) {
if (paramArrayOfByte == null|| paramArrayOfByte.length == 0) {
throw newRuntimeException("反序列化对象发生异常,byte序列为空!");
}
T instance = null;
try {
instance =targetClass.newInstance();
} catch (InstantiationException |IllegalAccessException e) {
throw newRuntimeException("反序列化过程中依据类型创建对象失败!",e);
}
Schema<T> schema =RuntimeSchema.getSchema(targetClass);
ProtostuffIOUtil.mergeFrom(paramArrayOfByte, instance, schema);
return instance;
}
public static <T> byte[]serializeList(List<T> objList) {
if (objList == null ||objList.isEmpty()) {
throw newRuntimeException("序列化对象列表("+ objList + ")参数异常!");
}
@SuppressWarnings("unchecked")
Schema<T> schema =(Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
LinkedBuffer buffer =LinkedBuffer.allocate(1024 * 1024);
byte[] protostuff = null;
ByteArrayOutputStream bos =null;
try {
bos = newByteArrayOutputStream();
ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
protostuff =bos.toByteArray();
} catch (Exception e) {
throw new RuntimeException("序列化对象列表(" + objList + ")发生异常!",e);
} finally {
buffer.clear();
try {
if(bos!=null){
bos.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
return protostuff;
}
public static <T>List<T> deserializeList(byte[] paramArrayOfByte, Class<T>targetClass) {
if (paramArrayOfByte == null|| paramArrayOfByte.length == 0) {
throw newRuntimeException("反序列化对象发生异常,byte序列为空!");
}
Schema<T> schema =RuntimeSchema.getSchema(targetClass);
List<T> result = null;
try {
result =ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(paramArrayOfByte),schema);
} catch (IOException e) {
throw newRuntimeException("反序列化对象列表发生异常!",e);
}
return result;
}
}
(4)验证
@Test
public voidtestProtobufSerializer() {
GenericObjectPoolConfig poolConfig = newGenericObjectPoolConfig();
JedisPooljedisPool= newJedisPool(poolConfig,"10.3.34.101", 6378);
Jedisjedis= null;
jedis = jedisPool.getResource();
jedis.set("hello1".getBytes(),"worldone".getBytes());
System.out.println("-------");
Clubclub= newClub();
club.setId(1);
club.setInfo("will win");
club.setName("tian jin quan jian");
club.setRank(1);
club.setCreateDate(new Date());
ProtostuffSerializerprotostuffSerializer = newProtostuffSerializer();
byte[] clubbytes = protostuffSerializer.serialize(club);
jedis.set("winner".getBytes(),clubbytes);
byte[] winnerClubBytes = jedis.get("winner".getBytes());
Club winnerClub = protostuffSerializer.deserialize(winnerClubBytes,Club.class);
System.out.println(winnerClub.getId());
System.out.println(winnerClub.getInfo());
System.out.println(winnerClub.getName());
System.out.println(winnerClub.getRank());
System.out.println(winnerClub.getCreateDate());
//jedis.dbSize()
}
输出:
1
will win
tian jin quan jian
1
Thu Jul 13 14:51:53 CST 2017
2017-07-13 14:51:53.404 INFO16648 --- [ Thread-4]s.c.a.AnnotationConfigApplicationContext : Closingorg.spring[email protected]7c6908d7:startup date [Thu Jul 13 14:51:51 CST 2017]; root of context hierarchy
四. Jedis连接池
4.1 Jedis连接池的作用
Redis客户端与服务端的通讯会新建TCP连接,使用后再断开连接,对于频繁访问Redis的场景显然不是高效的使用方式。
Jedis可以使用连接池的方式对Jedis连接进行管理,预先初始化好一定数量的Jedis连接,并将Jedis连接预先放在池子(JedisPool)中,每次要连接Redis,都从JedisPool中获取连接,使用后在释放回连接池中,获取和释放连接的操作都是在客户端进行的,只有少量的并发同步开销,远远小于建立TCP连接的开销。另外,直连的方式还会造成创建大量的Jedis对象的风险,极端情况下会造成连接泄露,而连接池可以有效的保护和控制资源的使用。
4.2 Jedis连接池的初始化
//使用Apache的通用对象池工具common-pool做为资源管理工具
GenericObjectPoolConfig poolConfig = newGenericObjectPoolConfig();
//初始化jedis连接池
JedisPool jedisPool = new JedisPool(poolConfig, "10.3.34.101",6378);
Jedis jedis = null;
try {
//从连接池获取jedis对象
jedis = jedisPool.getResource();
jedis.get(“hello”);
} catch(Exception e) {
log.error(e.getMessage,e);
} finally {
//如果使用JedisPool,close不是关闭连接,而是将连接归还到连接池中。
If(jedis != null) {
Jedis.close();
}
}
4.3 Jedis连接池的参数说明
五. Jedis中Pipeline的支持
(1)使用jedis对象生产一个pipeline对象,直接调用jedis.pipelined();
(2)将命令封装到pipeline中,例如调用pipeline.del(key),写法和jedis.del(key)是一致的,只
不过此时并不真正执行命令
(3)执行pipeline.sync()完成此次批量命令的执行或者pipeline.syncAndReturnAll()批量执行命令并将命令的执行结果返回。
例如:
public classPipelineDemo {
private static Jedis jedis = null;
static {
GenericObjectPoolConfig poolConfig = newGenericObjectPoolConfig();
JedisPooljedisPool= newJedisPool(poolConfig,"10.3.34.101", 6378);
jedis = jedisPool.getResource();
}
public void batchSave() {
//生成pipeline对象
Pipelinepipeline= jedis.pipelined();
for (int i = 0; i < 10; i++) {
//此时并未真正执行命令
pipeline.set("key"+i,"value"+i);
}
//执行命令
pipeline.sync();
//或者List list = pipeline.syncAndReturnAll();
}
}
六. Jedis的Lua脚本
Jedis中执行Lua脚本和redis-cli十分类似,Jedis提供了三个重要的函数实现Lua脚本的执行:
(1) Object eval(String script, intkeyCount, String… params)
eval函数有三个参数:
script:Lua脚本内容
keyCount:键的个数
params:相关参数keys和argv
以一个简单Lua脚本为例进行说明:
return redis.call(‘get’,KEYS[1])
在redis-cli中执行上面的Lua脚本方法如下:
eval "returnredis.call('get',KEYS[1])" 1 key1
在jedis中执行:
Stringkey= "key1";
Stringscript= "returnredis.call('GET',KEYS[1])";
Objectobj= jedis.eval(script, 1, key);
System.out.println(obj);
(2) String scriptLoad(String script)
scriptload将脚本加载到redis中
在jedis中执行:
String script= "returnredis.call('GET',KEYS[1])";
sha = jedis.scriptLoad(script);
(3) Object evalsha(String sha1, intkeyCount, String… params)
选项:
ScriptSha:脚本的SHA1
KeyCount:键的个数
params:相关参数KEYS和ARGV
jedis实现:
Stringkey= "key1";
Stringscript= "returnredis.call('GET',KEYS[1])";
Stringsha= jedis.scriptLoad(script);
Objectobj= jedis.evalsha(sha, 1, key);
七. jedis注意事项
(1)Jedis操作放在try catch finally中更加合理
(2)生产环境中使用jedis连接池
(3)如果key和value涉及了字节数组,需要选择合适的序列化方法