package redis.clients.util; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Sharded> { public static final int DEFAULT_WEIGHT = 1; private TreeMap nodes; private final Hashing algo; private final Map, R> resources = new LinkedHashMap, R>(); /** * The default pattern used for extracting a key tag. The pattern must have * a group (between parenthesis), which delimits the tag to be hashed. A * null pattern avoids applying the regular expression for each lookup, * improving performance a little bit is key tags aren't being used. */ private Pattern tagPattern = null; // the tag is anything between {} public static final Pattern DEFAULT_KEY_TAG_PATTERN = Pattern .compile("\\{(.+?)\\}"); public Sharded(List shards) { this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works // with 64-bits not 128 } public Sharded(List shards, Hashing algo) { this.algo = algo; initialize(shards); } public Sharded(List shards, Pattern tagPattern) { this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good // as we works with // 64-bits not 128 } public Sharded(List shards, Hashing algo, Pattern tagPattern) { this.algo = algo; this.tagPattern = tagPattern; initialize(shards); } private void initialize(List shards) { nodes = new TreeMap(); for (int i = 0; i != shards.size(); ++i) { final S shardInfo = shards.get(i); if (shardInfo.getName() == null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo); } else for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { nodes.put( this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo); } resources.put(shardInfo, shardInfo.createResource()); } } public R getShard(byte[] key) { return resources.get(getShardInfo(key)); } public R getShard(String key) { return resources.get(getShardInfo(key)); } public S getShardInfo(byte[] key) { SortedMap tail = nodes.tailMap(algo.hash(key)); if (tail.isEmpty()) { return nodes.get(nodes.firstKey()); } return tail.get(tail.firstKey()); } public S getShardInfo(String key) { return getShardInfo(SafeEncoder.encode(getKeyTag(key))); } /** * A key tag is a special pattern inside a key that, if preset, is the only * part of the key hashed in order to select the server for this key. * * @see http://code.google.com/p/redis/wiki/FAQ#I * 'm_using_some_form_of_key_hashing_for_partitioning,_but_wh * @param key * @return The tag if it exists, or the original key */ public String getKeyTag(String key) { if (tagPattern != null) { Matcher m = tagPattern.matcher(key); if (m.find()) return m.group(1); } return key; } public Collection getAllShardInfo() { return Collections.unmodifiableCollection(nodes.values()); } public Collection getAllShards() { return Collections.unmodifiableCollection(resources.values()); } }