关于哈希环的虚拟节点,可以考虑一个分布式缓存的场景。在这个场景中,我们使用Spring Boot搭建一个简单的分布式缓存系统,采用哈希环来分配缓存请求。
场景描述
假设我们有多个缓存服务器,需要均匀地分配缓存请求。使用哈希环和虚拟节点的方式,可以有效地处理服务器的动态增加或减少。
代码示例
-
引入依赖
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
哈希环实现
javaimport java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.SortedMap; import java.util.TreeMap; public class ConsistentHashing { private final SortedMap<Integer, String> circle = new TreeMap<>(); private final int VIRTUAL_NODES = 5; public void addNode(String node) { for (int i = 0; i < VIRTUAL_NODES; i++) { int hash = getHash(node + i); circle.put(hash, node); } } public void removeNode(String node) { for (int i = 0; i < VIRTUAL_NODES; i++) { int hash = getHash(node + i); circle.remove(hash); } } public String getNode(String key) { if (circle.isEmpty()) return null; int hash = getHash(key); SortedMap<Integer, String> tailMap = circle.tailMap(hash); Integer nodeHash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); return circle.get(nodeHash); } private int getHash(String key) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(key.getBytes(StandardCharsets.UTF_8)); return Math.abs(java.util.Arrays.hashCode(digest)); } catch (Exception e) { throw new RuntimeException(e); } } }
-
控制器示例
javaimport org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; @RestController @RequestMapping("/cache") public class CacheController { private final ConsistentHashing consistentHashing = new ConsistentHashing(); private final List<String> nodes = new ArrayList<>(); @PostMapping("/addNode") public String addNode(@RequestParam String node) { consistentHashing.addNode(node); nodes.add(node); return "Node added: " + node; } @PostMapping("/removeNode") public String removeNode(@RequestParam String node) { consistentHashing.removeNode(node); nodes.remove(node); return "Node removed: " + node; } @GetMapping("/getNode") public String getNode(@RequestParam String key) { return "Key " + key + " is routed to node: " + consistentHashing.getNode(key); } }
使用说明
- 启动Spring Boot应用后,可以通过POST请求添加或移除节点,并通过GET请求获取某个键对应的节点。
这个示例通过哈希环和虚拟节点实现了简单的负载均衡,接下来进一步扩展和优化。
我们可以扩展这个分布式缓存场景,加入以下复杂性:
- 多种数据类型支持:缓存支持字符串、对象等多种数据类型。
- 过期策略:实现缓存的过期机制。
- 集群节点状态监控:对节点状态进行健康检查,自动从哈希环中移除失效节点。
- 负载均衡算法:支持不同的负载均衡策略,比如轮询和随机。
代码示例扩展
-
缓存数据结构
javaimport java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class Cache { private final ConcurrentMap<String, CacheItem> cache = new ConcurrentHashMap<>(); public void put(String key, Object value, long ttl) { long expiryTime = System.currentTimeMillis() + ttl; cache.put(key, new CacheItem(value, expiryTime)); } public Object get(String key) { CacheItem item = cache.get(key); if (item != null && System.currentTimeMillis() < item.expiryTime) { return item.value; } cache.remove(key); return null; } private static class CacheItem { Object value; long expiryTime; CacheItem(Object value, long expiryTime) { this.value = value; this.expiryTime = expiryTime; } } }
-
集群状态监控
javaimport java.util.Timer; import java.util.TimerTask; public class NodeHealthMonitor { private final ConsistentHashing hashing; private final Timer timer = new Timer(); public NodeHealthMonitor(ConsistentHashing hashing) { this.hashing = hashing; startMonitoring(); } private void startMonitoring() { timer.schedule(new TimerTask() { @Override public void run() { // 健康检查逻辑,移除失效节点 for (String node : hashing.getNodes()) { if (!isHealthy(node)) { hashing.removeNode(node); } } } }, 0, 60000); // 每分钟检查一次 } private boolean isHealthy(String node) { // 假设的健康检查逻辑 return true; // 实际逻辑需实现 } }
-
控制器更新
java@RestController @RequestMapping("/cache") public class CacheController { private final ConsistentHashing consistentHashing = new ConsistentHashing(); private final Cache cache = new Cache(); private final NodeHealthMonitor healthMonitor = new NodeHealthMonitor(consistentHashing); @PostMapping("/addNode") public String addNode(@RequestParam String node) { consistentHashing.addNode(node); return "Node added: " + node; } @PostMapping("/removeNode") public String removeNode(@RequestParam String node) { consistentHashing.removeNode(node); return "Node removed: " + node; } @PostMapping("/put") public String put(@RequestParam String key, @RequestParam String value, @RequestParam long ttl) { String node = consistentHashing.getNode(key); cache.put(key, value, ttl); return "Stored in node: " + node; } @GetMapping("/get") public String get(@RequestParam String key) { Object value = cache.get(key); return value != null ? "Value: " + value : "Value not found or expired"; } }
使用说明
- 添加节点 :使用
/cache/addNode
。 - 存储缓存 :使用
/cache/put
,并设置过期时间。 - 获取缓存 :使用
/cache/get
,获取值或确认过期。
这种设计让系统更复杂、更健壮。