From 630b678d5f2f3022b07b3bf7cc2a5378663d30bf Mon Sep 17 00:00:00 2001 From: Calvin Date: Wed, 18 Jun 2014 17:23:35 +0800 Subject: [PATCH] =?UTF-8?q?Jedis=20=E6=94=AF=E6=8C=81=20=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AFSharding=EF=BC=8CJedis=E6=8F=90=E4=BE=9B=E7=9A=84Shard?= =?UTF-8?q?ingPool=E4=B8=8D=E8=83=BD=E5=90=8C=E6=97=B6=E5=85=BC=E5=AE=B9Se?= =?UTF-8?q?ntinel=20Pool=EF=BC=8CSpringSide=E6=8F=90=E4=BE=9B=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=94=AF=E6=8C=81=E4=B8=A4=E8=80=85=E7=9A=84=E5=81=9A?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=8D=B3=E6=96=B0=E7=9A=84JedisShardedTempla?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nosql/redis/JedisShardedTemplate.java | 231 ++++++++++++++++++ .../modules/nosql/redis/JedisTemplate.java | 70 +++++- .../nosql/redis/JedisTemplateTest.java | 25 ++ 3 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisShardedTemplate.java diff --git a/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisShardedTemplate.java b/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisShardedTemplate.java new file mode 100644 index 00000000..5810097f --- /dev/null +++ b/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisShardedTemplate.java @@ -0,0 +1,231 @@ +/*------------------------------------------------------------------------------ + * COPYRIGHT Ericsson 2013 + * + * The copyright to the computer program(s) herein is the property of + * Ericsson Inc. The programs may be used and/or copied only with written + * permission from Ericsson Inc. or in accordance with the terms and + * conditions stipulated in the agreement/contract under which the + * program(s) have been supplied. + *----------------------------------------------------------------------------*/ +package org.springside.modules.nosql.redis; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.springside.modules.nosql.redis.JedisTemplate.JedisAction; +import org.springside.modules.nosql.redis.JedisTemplate.JedisActionNoResult; +import org.springside.modules.nosql.redis.pool.JedisPool; + +import redis.clients.jedis.exceptions.JedisException; +import redis.clients.util.Hashing; + +/** + * JedisShardTemplate, 在JedisTemplate的基础上支持按key进行sharding分区, + * 如果只传入一个JedisPool,则会退化到JedisTemplate, 不会进行sharding运算。 + */ +public class JedisShardedTemplate { + + private final Hashing algo = Hashing.MURMUR_HASH; + private TreeMap nodes = new TreeMap(); + private JedisTemplate singleTemplate = null; + + public JedisShardedTemplate(JedisPool[] jedisPools) { + if (jedisPools.length == 1) { + singleTemplate = new JedisTemplate(jedisPools[0]); + } else { + initNodes(jedisPools); + } + } + + private void initNodes(JedisPool... jedisPools) { + for (int i = 0; i != jedisPools.length; i++) { + // 环上更多的点让负载更均衡 + for (int n = 0; n < 5; n++) { + final JedisPool jedisPool = jedisPools[i]; + nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), new JedisTemplate(jedisPool)); + } + } + } + + /** + * 对Key进行哈希并返回哈希环上对应的节点。 + * 参考了Jedis的Sharded.java + */ + public JedisTemplate getShard(String key) { + + if (singleTemplate != null) { + return singleTemplate; + } + + SortedMap tail = nodes.tailMap(algo.hash(key)); + // 已到最后,返回第一个点. + if (tail.isEmpty()) { + return nodes.get(nodes.firstKey()); + } + + return tail.get(tail.firstKey()); + } + + /** + * 包含key以帮助sharding的execute 版本. + */ + public T execute(String key, JedisAction jedisAction) throws JedisException { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.execute(jedisAction); + } + + /** + * 包含key以帮助sharding的execute 版本. + */ + public void execute(String key, JedisActionNoResult jedisAction) throws JedisException { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.execute(jedisAction); + } + + // / Common Actions /// + + public Boolean del(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.del(key); + } + + // / String Actions /// + + public String get(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.get(key); + } + + public void set(final String key, final String value) { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.set(key, value); + } + + public void setex(String key, String value, int seconds) { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.setex(key, value, seconds); + } + + public Boolean setnx(String key, String value) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.setnx(key, value); + } + + public Boolean setnxex(String key, String value, int seconds) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.setnxex(key, value, seconds); + } + + public Long incr(String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.incr(key); + } + + public Long decr(String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.decr(key); + } + + // / Hash Actions /// + + public String hget(final String key, final String field) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.hget(key, field); + } + + public void hset(final String key, final String field, final String value) { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.hset(key, field, value); + } + + public List hmget(String key, String[] fields) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.hmget(key, fields); + } + + public void hmset(String key, Map map) { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.hmset(key, map); + } + + public Long hdel(final String key, final String... fieldsName) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.hdel(key, fieldsName); + } + + public Set hkeys(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.hkeys(key); + } + + // ////////////// 关于List /////////////////////////// + public void lpush(final String key, final String... values) { + JedisTemplate jedisTemplate = getShard(key); + jedisTemplate.lpush(key, values); + } + + public String rpop(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.rpop(key); + } + + /** + * 返回List长度, key不存在时返回0,key类型不是list时抛出异常. + */ + public Long llen(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.llen(key); + } + + /** + * 删除List中的第一个等于value的元素,value不存在或key不存在时返回false. + */ + public Boolean lremOne(final String key, final String value) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.lremOne(key, value); + } + + /** + * 删除List中的所有等于value的元素,value不存在或key不存在时返回false. + */ + public Boolean lremAll(final String key, final String value) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.lremAll(key, value); + } + + // ////////////// 关于Sorted Set /////////////////////////// + /** + * 加入Sorted set, 如果member在Set里已存在, 只更新score并返回false, 否则返回true. + */ + public Boolean zadd(final String key, final String member, final double score) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.zadd(key, member, score); + } + + /** + * 删除sorted set中的元素,成功删除返回true,key或member不存在返回false。 + */ + public Boolean zrem(final String key, final String member) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.zrem(key, member); + } + + /** + * 当key不存在时返回null. + */ + public Double zscore(final String key, final String member) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.zscore(key, member); + } + + /** + * 返回sorted set长度, key不存在时返回0. + */ + public Long zcard(final String key) { + JedisTemplate jedisTemplate = getShard(key); + return jedisTemplate.zcard(key); + } +} diff --git a/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisTemplate.java b/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisTemplate.java index fb973f20..0f81d6c1 100644 --- a/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisTemplate.java +++ b/modules/extension/src/main/java/org/springside/modules/nosql/redis/JedisTemplate.java @@ -5,6 +5,10 @@ *******************************************************************************/ package org.springside.modules.nosql.redis; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +20,7 @@ import redis.clients.util.Pool; /** * JedisTemplate 提供了一个template方法,负责对Jedis连接的获取与归还。 * JedisAction 和 JedisActionNoResult两种回调接口,适用于有无返回值两种情况。 - * 同时提供一些最常用函数的封装, 如get/set/zadd等。 + * 同时提供一些JedisOperation中定义的 最常用函数的封装, 如get/set/zadd等。 */ public class JedisTemplate { private static Logger logger = LoggerFactory.getLogger(JedisTemplate.class); @@ -108,7 +112,8 @@ public class JedisTemplate { /** * 删除key, 如果key存在返回true, 否则返回false。 */ - public Boolean del(final String... keys) { + + public Boolean del(final String keys) { return execute(new JedisAction() { @Override @@ -132,6 +137,7 @@ public class JedisTemplate { /** * 如果key不存在, 返回null. */ + public String get(final String key) { return execute(new JedisAction() { @@ -181,6 +187,7 @@ public class JedisTemplate { /** * 如果key还不存在则进行设置,返回true,否则返回false. */ + public Boolean setnx(final String key, final String value) { return execute(new JedisAction() { @@ -194,6 +201,7 @@ public class JedisTemplate { /** * 综合setNX与setEx的效果。 */ + public Boolean setnxex(final String key, final String value, final int seconds) { return execute(new JedisAction() { @@ -223,6 +231,64 @@ public class JedisTemplate { }); } + // ////////////// 关于Hash /////////////////////////// + + public String hget(final String key, final String field) { + return execute(new JedisAction() { + @Override + public String action(Jedis jedis) { + return jedis.hget(key, field); + } + }); + } + + public void hset(final String key, final String field, final String value) { + execute(new JedisActionNoResult() { + + @Override + public void action(Jedis jedis) { + jedis.hset(key, field, value); + } + }); + } + + public List hmget(final String key, final String[] fields) { + return execute(new JedisAction>() { + @Override + public List action(Jedis jedis) { + return jedis.hmget(key, fields); + } + }); + } + + public void hmset(final String key, final Map map) { + execute(new JedisActionNoResult() { + + @Override + public void action(Jedis jedis) { + jedis.hmset(key, map); + } + }); + } + + public Long hdel(final String key, final String... fieldsName) { + return execute(new JedisAction() { + @Override + public Long action(Jedis jedis) { + return jedis.hdel(key, fieldsName); + } + }); + } + + public Set hkeys(final String key) { + return execute(new JedisAction>() { + @Override + public Set action(Jedis jedis) { + return jedis.hkeys(key); + } + }); + } + // ////////////// 关于List /////////////////////////// public void lpush(final String key, final String... values) { execute(new JedisActionNoResult() { diff --git a/modules/extension/src/test/java/org/springside/modules/nosql/redis/JedisTemplateTest.java b/modules/extension/src/test/java/org/springside/modules/nosql/redis/JedisTemplateTest.java index 41772759..3539b874 100644 --- a/modules/extension/src/test/java/org/springside/modules/nosql/redis/JedisTemplateTest.java +++ b/modules/extension/src/test/java/org/springside/modules/nosql/redis/JedisTemplateTest.java @@ -7,6 +7,9 @@ package org.springside.modules.nosql.redis; import static org.assertj.core.api.Assertions.*; +import java.util.HashMap; +import java.util.Map; + import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; @@ -60,6 +63,28 @@ public class JedisTemplateTest { assertThat(jedisTemplate.setnx(key + "nx", value)).isTrue(); } + @Test + public void hashActions() { + String key = "test.string.key"; + String field1 = "aa"; + String field2 = "bb"; + String notExistField = field1 + "not.exist"; + String value1 = "123"; + String value2 = "456"; + + jedisTemplate.hset(key, field1, value1); + assertThat(jedisTemplate.hget(key, field1)).isEqualTo(value1); + assertThat(jedisTemplate.hget(key, notExistField)).isNull(); + + Map map = new HashMap(); + map.put(field1, value1); + map.put(field2, value2); + jedisTemplate.hmset(key, map); + + assertThat(jedisTemplate.hmget(key, new String[] { field1, field2 })).containsSequence(value1, value2); + + } + @Test public void listActions() { String key = "test.list.key";