哈希环的虚拟节点简单场景使用

关于哈希环的虚拟节点,可以考虑一个分布式缓存的场景。在这个场景中,我们使用Spring Boot搭建一个简单的分布式缓存系统,采用哈希环来分配缓存请求。

场景描述

假设我们有多个缓存服务器,需要均匀地分配缓存请求。使用哈希环和虚拟节点的方式,可以有效地处理服务器的动态增加或减少。

代码示例

  1. 引入依赖

    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>
  2. 哈希环实现

    java 复制代码
    import 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);
            }
        }
    }
  3. 控制器示例

    java 复制代码
    import 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请求获取某个键对应的节点。

这个示例通过哈希环和虚拟节点实现了简单的负载均衡,接下来进一步扩展和优化。

我们可以扩展这个分布式缓存场景,加入以下复杂性:

  1. 多种数据类型支持:缓存支持字符串、对象等多种数据类型。
  2. 过期策略:实现缓存的过期机制。
  3. 集群节点状态监控:对节点状态进行健康检查,自动从哈希环中移除失效节点。
  4. 负载均衡算法:支持不同的负载均衡策略,比如轮询和随机。

代码示例扩展

  1. 缓存数据结构

    java 复制代码
    import 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;
            }
        }
    }
  2. 集群状态监控

    java 复制代码
    import 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; // 实际逻辑需实现
        }
    }
  3. 控制器更新

    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,获取值或确认过期。

这种设计让系统更复杂、更健壮。

相关推荐
小池先生38 分钟前
springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失
java·spring boot·后端
苹果醋32 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
小蜗牛慢慢爬行2 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
azhou的代码园2 小时前
基于JAVA+SpringBoot+Vue的制造装备物联及生产管理ERP系统
java·spring boot·制造
wm10432 小时前
java web springboot
java·spring boot·后端
小扳4 小时前
微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)
java·服务器·分布式·微服务·云原生·架构
路在脚下@10 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一10 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien10 小时前
Spring Boot常用注解
java·spring boot·后端
苹果醋311 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx