转载请注明出处哈:http://carlosfu.iteye.com/blog/2240426
通过jedis来连接操作redis总体来说比较简单,按照redis单机、redis-sentinel、redis-cluster略有不同。
一、Jedis相关依赖
1. jedis依赖(选择最新的稳定版本,支持redis-cluster)
2.7.2
redis.clients
jedis
${jedis.version}
2. logback和junit依赖
1.0.13
4.11
ch.qos.logback
logback-core
${logback.version}
ch.qos.logback
logback-classic
${logback.version}
junit
junit
${junit.version}
二、Jedis单机测试:
1. Jedis-简单Kv
Jedis jedis = new Jedis("127.0.0.1");
jedis.set("foo", "bar");
String value = jedis.get("foo");
建议所有的jedis都放在try catch finally(jedis.close操作)中
package com.sohu.tv.test.jedis;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
/**
* 第一个jedis测试
*
* @author leifu
* @Date 2015年8月24日
* @Time 下午1:35:26
*/
public class JedisFirstTest {
private Logger logger = LoggerFactory.getLogger(JedisFirstTest.class);
/**
* redis单机host
*/
private final static String JEDIS_HOST = "127.0.0.1";
/**
* redis单机port
*/
private final static int JEDIS_PORT = 6379;
/**
* 超时时间(毫秒)
*/
private final static int JEDIS_TIME_OUT = 300;
@Test
public void testJedis() {
Jedis jedis = null;
try {
jedis = new Jedis(JEDIS_HOST, JEDIS_PORT, JEDIS_TIME_OUT);
String key = "sohuKey";
jedis.set(key, "sohuValue");
String value = jedis.get(key);
logger.info("get key {} from redis, value is {}", key, value);
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
2. Jedis-序列化Kv:
我们使用protostuff(Protostuff是基于大名鼎鼎的Google protobuff技术的Java版本)作为序列化工具:
(1)pom依赖:
1.0.8
com.dyuproject.protostuff
protostuff-runtime
${protostuff.version}
com.dyuproject.protostuff
protostuff-core
${protostuff.version}
(2)Club实体类:
package com.sohu.tv.bean;
import java.io.Serializable;
import java.util.Date;
/**
* 俱乐部
*
* @author leifu
* @Date 2015年7月28日
* @Time 下午1:43:53
*/
public class Club implements Serializable {
/**
* 俱乐部id
*/
private int id;
/**
* 俱乐部名
*/
private String clubName;
/**
* 俱乐部描述
*/
private String clubInfo;
/**
* 创建日期
*/
private Date createDate;
/**
* 排名
*/
private int rank;
public Club(int id, String clubName, String clubInfo, Date createDate, int rank) {
super();
this.id = id;
this.clubName = clubName;
this.clubInfo = clubInfo;
this.createDate = createDate;
this.rank = rank;
}
public Club() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getClubName() {
return clubName;
}
public void setClubName(String clubName) {
this.clubName = clubName;
}
public String getClubInfo() {
return clubInfo;
}
public void setClubInfo(String clubInfo) {
this.clubInfo = clubInfo;
}
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;
}
@Override
public String toString() {
return "Club [id=" + id + ", clubName=" + clubName + ", clubInfo=" + clubInfo + ", createDate=" + createDate
+ ", rank=" + rank + "]";
}
}
(3)序列化工具:
package com.sohu.tv.serializer;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import java.util.concurrent.ConcurrentHashMap;
/**
* protostuff序列化工具
*
* @author leifu
* @Date 2015-8-22
* @Time 上午10:05:20
*/
public class ProtostuffSerializer {
private static ConcurrentHashMap, Schema>> cachedSchema = new ConcurrentHashMap, Schema>>();
public byte[] serialize(final T source) {
VO vo = new VO(source);
final LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
final Schema schema = getSchema(VO.class);
return serializeInternal(vo, schema, buffer);
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
public T deserialize(final byte[] bytes) {
try {
Schema schema = getSchema(VO.class);
VO vo = deserializeInternal(bytes, schema.newMessage(), schema);
if (vo != null && vo.getValue() != null) {
return (T) vo.getValue();
}
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
return null;
}
private byte[] serializeInternal(final T source, final Schema schema, final LinkedBuffer buffer) {
return ProtostuffIOUtil.toByteArray(source, schema, buffer);
}
private T deserializeInternal(final byte[] bytes, final T result, final Schema schema) {
ProtostuffIOUtil.mergeFrom(bytes, result, schema);
return result;
}
private static Schema getSchema(Class clazz) {
@SuppressWarnings("unchecked")
Schema schema = (Schema) cachedSchema.get(clazz);
if (schema == null) {
schema = RuntimeSchema.createFrom(clazz);
cachedSchema.put(clazz, schema);
}
return schema;
}
}
package com.sohu.tv.serializer;
import java.io.Serializable;
/**
* @author leifu
* @Date 2015-8-22
* @Time 上午10:05:44
* @param
*/
public class VO implements Serializable {
private T value;
public VO(T value) {
this.value = value;
}
public VO() {
}
public T getValue() {
return value;
}
@Override
public String toString() {
return "VO{" +
"value=" + value +
'}';
}
}
(4)测试代码:
@Test
public void testJedisSerializable() {
ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();
Jedis jedis = null;
try {
jedis = new Jedis(JEDIS_HOST, JEDIS_PORT, JEDIS_TIME_OUT);
String key = "sohuKeySerializable";
// 序列化
Club club = new Club(1, "AC", "米兰", new Date(), 1);
byte[] clubBtyes = protostuffSerializer.serialize(club);
jedis.set(key.getBytes(), clubBtyes);
// 反序列化
byte[] resultBtyes = jedis.get(key.getBytes());
Club resultClub = protostuffSerializer.deserialize(resultBtyes);
logger.info("get key {} from redis, value is {}", key, resultClub);
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null) {
jedis.close();
}
}
}
(5)测试结果:
3. 连接池(推荐使用方式):一般线上系统的连接资源都是通过资源池的形式进行管理的。
package com.sohu.tv.test.jedis;
import java.util.Date;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sohu.tv.serializer.ProtostuffSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* 第一个jedisPool测试
*
* @author leifu
* @Date 2015年8月24日
* @Time 下午1:35:26
*/
public class JedisPoolTest {
private Logger logger = LoggerFactory.getLogger(JedisPoolTest.class);
private static JedisPool jedisPool;
/**
* redis单机host
*/
private final static String REDIS_HOST = "127.0.0.1";
/**
* redis单机port
*/
private final static int REDIS_PORT = 6379;
/**
* 超时时间(毫秒)
*/
private final static int JEDIS_POOL_TIME_OUT = 1000;
@BeforeClass
public static void testBeforeClass() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3);
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2);
poolConfig.setJmxEnabled(true);
poolConfig.setMaxWaitMillis(3000);
jedisPool = new JedisPool(poolConfig, REDIS_HOST, REDIS_PORT, JEDIS_POOL_TIME_OUT);
}
@AfterClass
public static void testAfterClass() {
if (jedisPool != null) {
jedisPool.destroy();
}
}
@Test
public void testJedisPool() {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String key = "sohuKeyPool";
jedis.set(key, "sohuValue");
String value = jedis.get(key);
logger.info("get key {} from redis, value is {}", key, value);
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (jedis != null) {
// 如果使用JedisPool,close操作不是关闭连接,代表归还资源池
jedis.close();
}
}
}
}
(3.1) 注意:jedis.close的实现:
(2) dataSource=null代表直连,jedis.close代表关闭连接
(3) jedis.close放到finally里面做
Jedis源码中:
@Override
public void close() {
if (dataSource != null) {
if (client.isBroken()) {
this.dataSource.returnBrokenResource(this);
} else {
this.dataSource.returnResource(this);
}
} else {
client.close();
}
}
(3.2) GenericObjectPoolConfig参数说明如下:
其余配置如下:
- minEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除。默认-1
#借资源时候是否要验证,比如jedis对象验证是 ip:port是否发生改变,且执行一个ping命令
#还资源时候是否要验证,同上。
6. timeBetweenEvictionRunsMillis: “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
-> 0 : 抛出异常,
-> 1 : 阻塞,直到有可用链接资源
-> 2 : 强制创建新的链接资源
四、Redis-Sentinel
JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout);
//获取jedis的方法和JedisPool一样的,不在赘述
sentinelSet: sentinel实例列表
poolConfig: common-pool包中的GenericObjectPoolConfig
timeout: 超时
有一点需要注意的是:sentinelSet: sentinel实例列表,而不是具体的redis实例列表,这是因为为了实现高可用,jedis屏蔽了redis实例信息,所有实例信息(主从信息)都是通过sentinel获取。
五、Redis-Cluster
Set jedisClusterNodes = new HashSet();
jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
....
PipelineCluster pipelineCluster = new PipelineCluster(jedisPoolConfig, nodeList, timeout);
//获取jedis的方法和JedisPool一样的,不在赘述
jedisPoolConfig: common-pool包中的GenericObjectPoolConfig
timeout: 超时时间
有一点需要注意的是:nodeList尽可能写入所有的redis实例信息(虽然jedis可以从任一redis实例获取到集群的信息。)
有兴趣的可以一下jedis源码中JedisClusterConnectionHandler这个类的initializeSlotsCache方法:
private void initializeSlotsCache(Set startNodes, GenericObjectPoolConfig poolConfig) {
for (HostAndPort hostAndPort : startNodes) {
Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort());
try {
cache.discoverClusterNodesAndSlots(jedis);
break;
} catch (JedisConnectionException e) {
// try next nodes
} finally {
if (jedis != null) {
jedis.close();
}
}
}
for (HostAndPort node : startNodes) {
cache.setNodeIfNotExist(node);
}
}
附一个redis-cluster工厂类: