转载请注明出处哈: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
SetjedisClusterNodes = 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附一个redis-cluster工厂类: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); } }