Sharded实现学习-我们到底能走多远系列(32)
我们到底能走多远系列(32)
扯淡:
工作是容易的赚钱是困难的
恋爱是容易的成家是困难的
相爱是容易的相处是困难的
决定是容易的可是等待是困难的
主题:
1,Sharded的实现
Memcached 和 redis 都使用了该算法来实现自己的多服务器均匀分派存储值的。
shardedJedisPool的配置如下:(具体可以参考《spring和redis的整合》)
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool" scope="singleton"> <constructor-arg index="0" ref="jedisPoolConfig" /> <constructor-arg index="1"> <list> <bean class="redis.clients.jedis.JedisShardInfo"> <constructor-arg name="host" value="${redis.host}" /> <constructor-arg name="port" value="${redis.port}" /> <constructor-arg name="timeout" value="${redis.timeout}" /> <constructor-arg name="weight" value="1" /> </bean> </list> </constructor-arg> </bean>
注入了两个对象:jedisPoolConfig 和 JedisShardInfo
然后产生ShardedJedis:
public ShardedJedis getRedisClient() { try { ShardedJedis shardJedis = shardedJedisPool.getResource(); return shardJedis; } catch (Exception e) { log.error("getRedisClent error", e); } return null; }
ShardedJedis 继承 BinaryShardedJedis 继承 Sharded<Jedis, JedisShardInfo>
而Sharded的实现就是前面一致性哈希算法的实现啦~
// 使用TreeMap来完成构造出一个很多节点的环形 private TreeMap<Long, S> nodes; // 构造方法 public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) { this.algo = algo; this.tagPattern = tagPattern; // 初始化方法,建立一个个节点 initialize(shards); }
initialize方法:
private void initialize(List<S> shards) { nodes = new TreeMap<Long, S>(); 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 // 将设置的权重放大160倍,产生更多的节点,因为hash一下就散落到各道各处了,如此就是所谓的虚拟节点,以保证均匀分布 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()); } }
redis放key value的时候,需要判断应该放在那个服务器上,就是判断hash后更靠近哪个节点。
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) { // 首先判断是不是tree中最大的key,及最后一个,注意我们是环,所以最大的后面就要从头开始。 SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key)); // 是最后一个key了,所以取第一个节点对应的服务器 if (tail.size() == 0) { return nodes.get(nodes.firstKey()); } // 不是最后一个就是比自己离自己最近的大的key对应的服务器 return tail.get(tail.firstKey()); } public S getShardInfo(String key) { return getShardInfo(SafeEncoder.encode(getKeyTag(key))); }
到这里基本明白了如何抽象实现一个环状的排序的数据结构了。值得借鉴。
2,实践中的一个例子
TreeMap<Integer, AwardConfigDO> extentTree = new TreeMap<Integer, AwardConfigDO>(); // 获奖区间划分 for (AwardConfigDO awardConfig : configList) { //Probability是区间节点,如100,500 extentTree.put(awardConfig.getProbability(), awardConfig); } // 进入中奖区 random 是随机产生的数字,首先判断是否进入中奖区 if (random < extentTree.lastKey()) { //然后判断 中奖奖项 是哪个 AwardConfigDO awardConfig = extentTree.higherEntry(random).getValue(); }
所以TreeMap可以来抽象实现这种区间的结构。关于TreeMap可以看API哦。
--------------------20130827补充-----------------------
需要注意的是 使用了TreeMap 需要考虑key相同的情况,这种情况就需要接受前一个映射关系会被替换的情况。
public static void main(String[] args) { TreeMap<Integer, String> extentTree = new TreeMap<Integer, String>(); extentTree.put(1, "1"); extentTree.put(10, "2"); extentTree.put(10, "3"); extentTree.put(100, "4"); String value = extentTree.higherEntry(5).getValue(); System.out.println(value);//output:3 }
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
共勉。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
2012-08-22 BufferedInputStream-我们到底能走多远系列(3)