├── .gitignore ├── README.md ├── docs └── README.en.md ├── go.mod ├── go.sum ├── loadbalancer ├── consistent_hash.go ├── least_connections.go ├── load_balancer.go ├── random.go ├── round_robin.go └── round_robin_test.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Go语言相关忽略文件 2 | # 二进制文件 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | *.test 9 | *.out 10 | 11 | # Go工具链生成的文件 12 | vendor/ 13 | bin/ 14 | pkg/ 15 | 16 | # Go模块缓存 17 | .cache/ 18 | 19 | # IDE相关文件 20 | .idea/ 21 | .vscode/ 22 | *.iml 23 | *.swp 24 | .DS_Store 25 | 26 | # 依赖和构建产物 27 | _output/ 28 | bazel-* 29 | 30 | # 日志文件 31 | *.log 32 | 33 | # 环境变量文件 34 | .env 35 | .env.local 36 | .env.development.local 37 | .env.test.local 38 | .env.production.local 39 | 40 | # 二进制输出目录 41 | /main 42 | /bin/ 43 | 44 | # 测试覆盖率报告 45 | coverage.txt 46 | profile.out 47 | 48 | # 临时文件 49 | *.tmp 50 | *~ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go 负载均衡算法实现 2 | 3 | [English Version](./docs/README.en.md) 4 | 5 | > 20250324:趁热输出了一篇关于负载均衡算法介绍的博客 6 | > 7 | > 希望帮助大家理解,有兴趣的同学可以看看,[博客链接](https://zhengyua.cn/new_blog/blog/2025/03/24/深入理解四种经典负载均衡算法/) 8 | > 9 | > 10 | 11 | 这个项目用 Go 语言实现了几种常见的负载均衡算法,可用于分布式系统中的负载分配。 12 | 13 | 本项目参考了 [@Zheaoli 的 Python 实现](https://github.com/Zheaoli/load-balancer-algorithm),并对算法进行了 Go 语言的重新实现和优化。 14 | 15 | ## 实现的算法 16 | 17 | 本项目实现了以下四种负载均衡算法: 18 | 19 | 1. 随机选择 (Random Selection) 20 | 2. 轮询 (Round Robin) 21 | 3. 最小连接 (Least Connections) 22 | 4. Maglev 一致性哈希 (Consistent Hashing) 23 | 24 | 每种算法都支持加权和非加权版本,以适应不同场景下的负载均衡需求。 25 | 26 | ## 算法详解 27 | 28 | ### 1. 随机选择算法 29 | 30 | 随机选择算法是最简单的负载均衡策略之一,它随机从可用服务器池中选择一个服务器来处理请求。 31 | 32 | **特点**: 33 | - 实现简单,易于理解 34 | - 支持加权随机选择,权重越大被选中的概率越高 35 | - 过滤不可用(权重为0)的服务器 36 | 37 | **适用场景**: 38 | - 服务器性能相近 39 | - 请求处理时间差异不大 40 | - 请求分布相对均衡 41 | 42 | ### 2. 轮询算法 43 | 44 | 轮询算法按照顺序依次选择服务器,实现请求的平均分配。 45 | 46 | **特点**: 47 | - 按顺序循环选择服务器 48 | - 支持加权轮询(平滑加权轮询算法),权重大的服务器处理更多请求 49 | - 可以跳过不可用的服务器 50 | 51 | **适用场景**: 52 | - 服务器配置相近 53 | - 请求负载均衡 54 | - 适合长期运行的系统中保持均衡负载 55 | 56 | ### 3. 最小连接算法 57 | 58 | 最小连接算法选择当前活动连接数最少的服务器,优化资源利用率。 59 | 60 | **特点**: 61 | - 动态跟踪每台服务器的连接数 62 | - 支持加权最小连接算法,考虑服务器权重和当前连接数的比值 63 | - 在连接完成后释放资源 64 | - 使用原子操作确保并发安全 65 | 66 | **适用场景**: 67 | - 请求处理时间差异大 68 | - 服务器处理能力不同 69 | - 需要动态平衡负载的场景 70 | 71 | ### 4. Maglev 一致性哈希算法 72 | 73 | Maglev 一致性哈希算法是 Google 设计的高效一致性哈希算法,用于大规模分布式系统。 74 | 75 | **特点**: 76 | - 使用哈希表实现 O(1) 的查找性能 77 | - 服务器变更时,最小化重新映射的请求数 78 | - 支持加权一致性哈希,权重大的服务器处理更多请求 79 | - 使用双哈希函数(murmur3 和 xxh3)提高随机性和分布均匀性 80 | 81 | **适用场景**: 82 | - 需要会话保持或缓存一致性的系统 83 | - 服务器动态增减频繁的环境 84 | - 大规模分布式系统 85 | 86 | ## 使用示例 87 | 88 | ```go 89 | package main 90 | 91 | import ( 92 | "fmt" 93 | "github.com/load-balancer-algorithm/loadbalancer" 94 | ) 95 | 96 | func main() { 97 | // 创建服务器 98 | servers := []*loadbalancer.Server{ 99 | {Address: "192.168.1.1:8080", Weight: 1}, 100 | {Address: "192.168.1.2:8080", Weight: 2}, 101 | {Address: "192.168.1.3:8080", Weight: 3}, 102 | } 103 | 104 | // 创建负载均衡器 (以一致性哈希为例) 105 | lb := loadbalancer.NewMaglevHashLoadBalancer() 106 | 107 | // 添加服务器 108 | for _, server := range servers { 109 | lb.AddServer(server) 110 | } 111 | 112 | // 使用负载均衡器选择服务器 113 | for i := 0; i < 5; i++ { 114 | key := fmt.Sprintf("user%d", i) 115 | server := lb.GetServer(key) 116 | fmt.Printf("请求 %s 被分配到服务器: %s\n", key, server.Address) 117 | } 118 | } 119 | ``` 120 | 121 | ## 性能测试与对比 122 | 123 | 各算法在不同场景下的性能对比: 124 | 125 | | 算法 | 时间复杂度 | 空间复杂度 | 一致性 | 负载均衡性 | 适合场景 | 126 | |------|------------|------------|--------|------------|----------| 127 | | 随机选择 | O(1) | O(n) | 低 | 中 | 简单系统,短连接 | 128 | | 轮询 | O(1) | O(n) | 低 | 高 | 性能接近的服务器集群 | 129 | | 最小连接 | O(n) | O(n) | 中 | 高 | 处理时间差异大的请求 | 130 | | Maglev哈希 | O(1) | O(m) | 高 | 中 | 需要会话保持的系统 | 131 | 132 | ## 项目结构 133 | 134 | ``` 135 | loadbalancer/ 136 | ├── base.go # 基础结构和接口定义 137 | ├── random.go # 随机选择算法实现 138 | ├── round_robin.go # 轮询算法实现 139 | ├── least_connections.go # 最小连接算法实现 140 | └── consistent_hash.go # Maglev一致性哈希算法实现 141 | ``` 142 | 143 | -------------------------------------------------------------------------------- /docs/README.en.md: -------------------------------------------------------------------------------- 1 | # Go Load Balancing Algorithm Implementation 2 | 3 | > 20250324: I published a blog post about load balancing algorithms 4 | > 5 | > To help everyone understand, interested readers can check the [blog link](https://zhengyua.cn/new_blog/blog/2025/03/24/深入理解四种经典负载均衡算法/) (in Chinese) 6 | > 7 | > 8 | 9 | This project implements several common load balancing algorithms in Go, which can be used for load distribution in distributed systems. 10 | 11 | This project references [@Zheaoli's Python implementation](https://github.com/Zheaoli/load-balancer-algorithm) and reimplements and optimizes the algorithms in Go. 12 | 13 | ## Implemented Algorithms 14 | 15 | This project implements the following four load balancing algorithms: 16 | 17 | 1. Random Selection 18 | 2. Round Robin 19 | 3. Least Connections 20 | 4. Maglev Consistent Hashing 21 | 22 | Each algorithm supports both weighted and non-weighted versions to meet load balancing requirements in different scenarios. 23 | 24 | ## Algorithm Details 25 | 26 | ### 1. Random Selection Algorithm 27 | 28 | Random selection algorithm is one of the simplest load balancing strategies, which randomly selects a server from the available server pool to process requests. 29 | 30 | **Features**: 31 | - Simple implementation and easy to understand 32 | - Supports weighted random selection, higher weight means higher probability of being selected 33 | - Filters unavailable (weight of 0) servers 34 | 35 | **Applicable Scenarios**: 36 | - Servers with similar performance 37 | - Requests with small processing time differences 38 | - Relatively balanced request distribution 39 | 40 | ### 2. Round Robin Algorithm 41 | 42 | Round Robin algorithm selects servers in sequence, achieving average distribution of requests. 43 | 44 | **Features**: 45 | - Selects servers in a cyclical order 46 | - Supports weighted round robin (smooth weighted round robin algorithm), servers with higher weights handle more requests 47 | - Can skip unavailable servers 48 | 49 | **Applicable Scenarios**: 50 | - Servers with similar configurations 51 | - Balanced request loads 52 | - Suitable for maintaining balanced loads in long-running systems 53 | 54 | ### 3. Least Connections Algorithm 55 | 56 | Least Connections algorithm selects the server with the fewest active connections, optimizing resource utilization. 57 | 58 | **Features**: 59 | - Dynamically tracks the number of connections for each server 60 | - Supports weighted least connections algorithm, considering the ratio of server weight to current connections 61 | - Releases resources after connection completion 62 | - Uses atomic operations to ensure concurrent safety 63 | 64 | **Applicable Scenarios**: 65 | - Requests with large processing time differences 66 | - Servers with different processing capabilities 67 | - Scenarios requiring dynamic load balancing 68 | 69 | ### 4. Maglev Consistent Hashing Algorithm 70 | 71 | Maglev consistent hashing algorithm is an efficient consistent hashing algorithm designed by Google for large-scale distributed systems. 72 | 73 | **Features**: 74 | - Uses hash tables to implement O(1) lookup performance 75 | - Minimizes request remapping when servers change 76 | - Supports weighted consistent hashing, servers with higher weights handle more requests 77 | - Uses dual hash functions (murmur3 and xxh3) to improve randomness and distribution uniformity 78 | 79 | **Applicable Scenarios**: 80 | - Systems requiring session persistence or cache consistency 81 | - Environments with frequent server additions and removals 82 | - Large-scale distributed systems 83 | 84 | ## Usage Example 85 | 86 | ```go 87 | package main 88 | 89 | import ( 90 | "fmt" 91 | "github.com/load-balancer-algorithm/loadbalancer" 92 | ) 93 | 94 | func main() { 95 | // Create servers 96 | servers := []*loadbalancer.Server{ 97 | {Address: "192.168.1.1:8080", Weight: 1}, 98 | {Address: "192.168.1.2:8080", Weight: 2}, 99 | {Address: "192.168.1.3:8080", Weight: 3}, 100 | } 101 | 102 | // Create load balancer (using consistent hashing as an example) 103 | lb := loadbalancer.NewMaglevHashLoadBalancer() 104 | 105 | // Add servers 106 | for _, server := range servers { 107 | lb.AddServer(server) 108 | } 109 | 110 | // Use the load balancer to select servers 111 | for i := 0; i < 5; i++ { 112 | key := fmt.Sprintf("user%d", i) 113 | server := lb.GetServer(key) 114 | fmt.Printf("Request %s assigned to server: %s\n", key, server.Address) 115 | } 116 | } 117 | ``` 118 | 119 | ## Performance Testing and Comparison 120 | 121 | Performance comparison of various algorithms in different scenarios: 122 | 123 | | Algorithm | Time Complexity | Space Complexity | Consistency | Load Balancing | Suitable Scenarios | 124 | |------|------------|------------|--------|------------|----------| 125 | | Random Selection | O(1) | O(n) | Low | Medium | Simple systems, short connections | 126 | | Round Robin | O(1) | O(n) | Low | High | Server clusters with similar performance | 127 | | Least Connections | O(n) | O(n) | Medium | High | Requests with large processing time differences | 128 | | Maglev Hashing | O(1) | O(m) | High | Medium | Systems requiring session persistence | 129 | 130 | ## Project Structure 131 | 132 | ``` 133 | loadbalancer/ 134 | ├── base.go # Basic structures and interface definitions 135 | ├── random.go # Random selection algorithm implementation 136 | ├── round_robin.go # Round robin algorithm implementation 137 | ├── least_connections.go # Least connections algorithm implementation 138 | └── consistent_hash.go # Maglev consistent hashing algorithm implementation 139 | ``` -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/load-balancer-algorithm 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/spaolacci/murmur3 v1.1.0 7 | github.com/zeebo/xxh3 v1.0.2 8 | ) 9 | 10 | require github.com/klauspost/cpuid/v2 v2.0.9 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= 2 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 3 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 4 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 5 | github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= 6 | github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= 7 | github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= 8 | github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= 9 | -------------------------------------------------------------------------------- /loadbalancer/consistent_hash.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/spaolacci/murmur3" 7 | "github.com/zeebo/xxh3" 8 | ) 9 | 10 | const ( 11 | // 查找表大小,应该是质数 12 | lookupTableSize = 65537 13 | ) 14 | 15 | // MaglevHashLoadBalancer Maglev一致性哈希负载均衡器 16 | type MaglevHashLoadBalancer struct { 17 | *BaseLoadBalancer 18 | lookupTable []int 19 | tableSize int 20 | mu sync.RWMutex 21 | } 22 | 23 | // NewMaglevHashLoadBalancer 创建Maglev一致性哈希负载均衡器 24 | func NewMaglevHashLoadBalancer() *MaglevHashLoadBalancer { 25 | return &MaglevHashLoadBalancer{ 26 | BaseLoadBalancer: NewBaseLoadBalancer(), 27 | tableSize: lookupTableSize, // 使用质数作为表大小 28 | lookupTable: make([]int, lookupTableSize), 29 | } 30 | } 31 | 32 | // AddServer 添加服务器 33 | func (lb *MaglevHashLoadBalancer) AddServer(server *Server) { 34 | lb.mu.Lock() 35 | defer lb.mu.Unlock() 36 | 37 | // 确保EffectiveWeight初始化 38 | if server.EffectiveWeight == 0 { 39 | server.EffectiveWeight = server.Weight 40 | } 41 | 42 | lb.Servers = append(lb.Servers, server) 43 | lb.updateLookupTable() 44 | } 45 | 46 | // RemoveServer 移除服务器 47 | func (lb *MaglevHashLoadBalancer) RemoveServer(server *Server) { 48 | lb.mu.Lock() 49 | defer lb.mu.Unlock() 50 | for i, s := range lb.Servers { 51 | if s == server { 52 | lb.Servers = append(lb.Servers[:i], lb.Servers[i+1:]...) 53 | break 54 | } 55 | } 56 | lb.updateLookupTable() 57 | } 58 | 59 | // permutation 计算服务器在查找表中的位置 60 | func (lb *MaglevHashLoadBalancer) permutation(serverIndex int) []int { 61 | // 使用服务器地址和索引结合计算哈希值,增加多样性 62 | server := lb.Servers[serverIndex] 63 | uniqueKey := server.Address + ":" + string(rune(serverIndex)) 64 | 65 | // 优化哈希种子计算 66 | offset := murmur3.Sum64([]byte(uniqueKey)) % uint64(lb.tableSize) 67 | skip := xxh3.Hash([]byte(uniqueKey))%uint64(lb.tableSize-1) + 1 // 确保skip至少为1且不超过tableSize 68 | 69 | weight := server.Weight 70 | if weight <= 0 { 71 | weight = 1 // 确保至少有权重1,避免除零错误 72 | } 73 | 74 | // 考虑权重因素,影响排列的生成 75 | perm := make([]int, lb.tableSize) 76 | for i := 0; i < lb.tableSize; i++ { 77 | // 根据权重调整偏移量,权重大的服务器有更多机会被选中 78 | adjustedOffset := (offset + uint64(i*weight)) % uint64(lb.tableSize) 79 | perm[i] = int((adjustedOffset + uint64(i)*skip) % uint64(lb.tableSize)) 80 | } 81 | return perm 82 | } 83 | 84 | // updateLookupTable 更新查找表 85 | func (lb *MaglevHashLoadBalancer) updateLookupTable() { 86 | if len(lb.Servers) == 0 { 87 | // 如果没有服务器,清空查找表 88 | for i := range lb.lookupTable { 89 | lb.lookupTable[i] = -1 90 | } 91 | return 92 | } 93 | 94 | // 筛选可用的服务器 95 | availableServers := make([]*Server, 0) 96 | serverIndexMap := make(map[*Server]int) 97 | weightSum := 0 98 | 99 | for i, server := range lb.Servers { 100 | if server.Weight > 0 { // 只考虑权重大于0的服务器为可用 101 | availableServers = append(availableServers, server) 102 | serverIndexMap[server] = i 103 | weightSum += server.Weight 104 | } 105 | } 106 | 107 | // 如果没有可用服务器,清空查找表 108 | if len(availableServers) == 0 { 109 | for i := range lb.lookupTable { 110 | lb.lookupTable[i] = -1 111 | } 112 | return 113 | } 114 | 115 | // 初始化查找表 116 | for i := range lb.lookupTable { 117 | lb.lookupTable[i] = -1 118 | } 119 | 120 | // 计算每个可用服务器的排列 121 | perms := make([][]int, len(availableServers)) 122 | for i, server := range availableServers { 123 | // 使用原始索引计算排列 124 | origIndex := serverIndexMap[server] 125 | perms[i] = lb.permutation(origIndex) 126 | } 127 | 128 | // 填充查找表 - 考虑权重因素 129 | next := make([]int, len(availableServers)) 130 | filled := 0 131 | 132 | // 确保表至少有75%填满 133 | for filled < lb.tableSize*3/4 { 134 | // 找到所有服务器中下一个未使用的位置 135 | minPos := lb.tableSize 136 | for i := range availableServers { 137 | if next[i] < lb.tableSize && perms[i][next[i]] < minPos { 138 | minPos = perms[i][next[i]] 139 | } 140 | } 141 | if minPos == lb.tableSize { 142 | break // 所有位置都已填满 143 | } 144 | 145 | // 按权重比例选择服务器 146 | selectedIndex := -1 147 | 148 | // 首先尝试按权重选择第一个到达该位置的服务器 149 | candidates := make([]int, 0) 150 | for i := range availableServers { 151 | if next[i] < lb.tableSize && perms[i][next[i]] == minPos { 152 | candidates = append(candidates, i) 153 | } 154 | } 155 | 156 | if len(candidates) > 0 { 157 | // 如果有多个候选服务器,根据权重选择 158 | if len(candidates) > 1 { 159 | // 权重大的服务器优先 160 | maxWeight := 0 161 | for _, idx := range candidates { 162 | if availableServers[idx].Weight > maxWeight { 163 | maxWeight = availableServers[idx].Weight 164 | selectedIndex = idx 165 | } 166 | } 167 | } else { 168 | selectedIndex = candidates[0] 169 | } 170 | 171 | // 使用原始索引填充查找表 172 | lb.lookupTable[minPos] = serverIndexMap[availableServers[selectedIndex]] 173 | next[selectedIndex]++ 174 | filled++ 175 | } 176 | } 177 | 178 | // 确保表完全填满 179 | for i := range lb.lookupTable { 180 | if lb.lookupTable[i] == -1 { 181 | // 如果某个位置未分配,随机选择一个可用服务器 182 | // 但倾向于选择权重更高的服务器 183 | totalWeight := 0 184 | for _, server := range availableServers { 185 | totalWeight += server.Weight 186 | } 187 | 188 | if totalWeight > 0 { 189 | // 按权重选择 190 | randomWeight := int(murmur3.Sum32([]byte(string(rune(i))))) % totalWeight 191 | cumulativeWeight := 0 192 | for _, server := range availableServers { 193 | cumulativeWeight += server.Weight 194 | if randomWeight < cumulativeWeight { 195 | lb.lookupTable[i] = serverIndexMap[server] 196 | break 197 | } 198 | } 199 | } else { 200 | // 权重和为0,直接选择第一个服务器 201 | lb.lookupTable[i] = serverIndexMap[availableServers[0]] 202 | } 203 | } 204 | } 205 | } 206 | 207 | // GetServer 根据key获取服务器 208 | func (lb *MaglevHashLoadBalancer) GetServer(key string) *Server { 209 | lb.mu.RLock() 210 | defer lb.mu.RUnlock() 211 | 212 | if len(lb.Servers) == 0 { 213 | return nil 214 | } 215 | 216 | // 使用key计算哈希值 217 | hash := murmur3.Sum64([]byte(key)) 218 | index := int(hash % uint64(lb.tableSize)) 219 | serverIndex := lb.lookupTable[index] 220 | 221 | if serverIndex == -1 || serverIndex >= len(lb.Servers) || lb.Servers[serverIndex].Weight <= 0 { 222 | // 如果查找表中没有对应的服务器,或者服务器不可用, 223 | // 尝试查找表中的其他位置 224 | for offset := 1; offset < 20; offset++ { 225 | newIndex := (index + offset) % lb.tableSize 226 | serverIndex = lb.lookupTable[newIndex] 227 | if serverIndex >= 0 && serverIndex < len(lb.Servers) && lb.Servers[serverIndex].Weight > 0 { 228 | return lb.Servers[serverIndex] 229 | } 230 | } 231 | 232 | // 如果仍未找到,回退到简单哈希 233 | availableServers := make([]*Server, 0) 234 | for _, server := range lb.Servers { 235 | if server.Weight > 0 { 236 | availableServers = append(availableServers, server) 237 | } 238 | } 239 | 240 | if len(availableServers) > 0 { 241 | return availableServers[int(hash%uint64(len(availableServers)))] 242 | } 243 | return nil 244 | } 245 | 246 | return lb.Servers[serverIndex] 247 | } 248 | -------------------------------------------------------------------------------- /loadbalancer/least_connections.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | // LeastConnectionsLoadBalancer 最小连接负载均衡器 9 | type LeastConnectionsLoadBalancer struct { 10 | *BaseLoadBalancer 11 | connections map[*Server]*int64 12 | weighted bool 13 | mu sync.RWMutex 14 | } 15 | 16 | // NewLeastConnectionsLoadBalancer 创建最小连接负载均衡器 17 | func NewLeastConnectionsLoadBalancer(weighted bool) *LeastConnectionsLoadBalancer { 18 | return &LeastConnectionsLoadBalancer{ 19 | BaseLoadBalancer: NewBaseLoadBalancer(), 20 | connections: make(map[*Server]*int64), 21 | weighted: weighted, 22 | } 23 | } 24 | 25 | // GetServer 获取连接数最少的服务器 26 | func (lb *LeastConnectionsLoadBalancer) GetServer(key string) *Server { 27 | lb.mu.Lock() 28 | defer lb.mu.Unlock() 29 | 30 | // 过滤出可用的服务器 - 检查Weight大于0的服务器(表示可用) 31 | availableServers := make([]*Server, 0) 32 | for _, server := range lb.Servers { 33 | if server.Weight > 0 { // 使用Weight > 0作为可用性判断 34 | availableServers = append(availableServers, server) 35 | } 36 | } 37 | 38 | if len(availableServers) == 0 { 39 | return nil 40 | } 41 | 42 | // 找到连接数最少的服务器 43 | var selectedServer *Server 44 | minValue := float64(1<<63 - 1) 45 | 46 | for _, server := range availableServers { 47 | connPtr := lb.connections[server] 48 | if connPtr == nil { 49 | var conn int64 50 | connPtr = &conn 51 | lb.connections[server] = connPtr 52 | } 53 | connections := atomic.LoadInt64(connPtr) 54 | 55 | var currentValue float64 56 | if lb.weighted { 57 | // 加权最小连接:考虑权重因素 58 | if server.Weight > 0 { 59 | // 权重越大,加权值越小,越容易被选中 60 | currentValue = float64(connections) / float64(server.Weight) 61 | } else { 62 | // 如果权重为0,则使用最大值,确保不会被选中 63 | currentValue = float64(1<<63 - 1) 64 | } 65 | } else { 66 | // 非加权最小连接 67 | currentValue = float64(connections) 68 | } 69 | 70 | // 如果当前值小于最小值,选择此服务器 71 | if currentValue < minValue { 72 | minValue = currentValue 73 | selectedServer = server 74 | } else if currentValue == minValue && selectedServer != nil { 75 | // 如果加权值相同,优先选择权重更高的服务器 76 | if server.Weight > selectedServer.Weight { 77 | selectedServer = server 78 | } 79 | } 80 | } 81 | 82 | if selectedServer != nil { 83 | // 增加选中服务器的连接数 84 | atomic.AddInt64(lb.connections[selectedServer], 1) 85 | // 同时更新Server结构体中的CurrentConnections字段,方便外部查看 86 | atomic.AddInt32(&selectedServer.CurrentConnections, 1) 87 | } 88 | 89 | return selectedServer 90 | } 91 | 92 | // ReleaseConnection 释放连接 93 | func (lb *LeastConnectionsLoadBalancer) ReleaseConnection(server *Server) { 94 | if server == nil { 95 | return 96 | } 97 | 98 | lb.mu.Lock() 99 | defer lb.mu.Unlock() 100 | 101 | if connPtr, exists := lb.connections[server]; exists && atomic.LoadInt64(connPtr) > 0 { 102 | atomic.AddInt64(connPtr, -1) 103 | // 同时更新Server结构体中的CurrentConnections字段 104 | if server.CurrentConnections > 0 { 105 | atomic.AddInt32(&server.CurrentConnections, -1) 106 | } 107 | } 108 | } 109 | 110 | // AddServer 添加服务器 111 | func (lb *LeastConnectionsLoadBalancer) AddServer(server *Server) { 112 | lb.mu.Lock() 113 | defer lb.mu.Unlock() 114 | lb.Servers = append(lb.Servers, server) 115 | var conn int64 116 | lb.connections[server] = &conn 117 | } 118 | 119 | // RemoveServer 移除服务器 120 | func (lb *LeastConnectionsLoadBalancer) RemoveServer(server *Server) { 121 | lb.mu.Lock() 122 | defer lb.mu.Unlock() 123 | for i, s := range lb.Servers { 124 | if s == server { 125 | lb.Servers = append(lb.Servers[:i], lb.Servers[i+1:]...) 126 | delete(lb.connections, server) 127 | break 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /loadbalancer/load_balancer.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Server 表示一个后端服务器 8 | type Server struct { 9 | Address string 10 | Weight int 11 | // 用于最小连接算法的当前连接数 12 | CurrentConnections int32 13 | // 用于轮询算法的当前权重 14 | CurrentWeight int 15 | // 用于轮询算法的有效权重 16 | EffectiveWeight int 17 | } 18 | 19 | // LoadBalancer 定义负载均衡器接口 20 | type LoadBalancer interface { 21 | // AddServer 添加服务器 22 | AddServer(server *Server) 23 | // RemoveServer 移除服务器 24 | RemoveServer(address string) 25 | // GetServer 获取下一个服务器 26 | GetServer(key string) *Server 27 | } 28 | 29 | // BaseLoadBalancer 基础负载均衡器结构 30 | type BaseLoadBalancer struct { 31 | Servers []*Server 32 | mu sync.RWMutex 33 | } 34 | 35 | // NewBaseLoadBalancer 创建基础负载均衡器 36 | func NewBaseLoadBalancer() *BaseLoadBalancer { 37 | return &BaseLoadBalancer{ 38 | Servers: make([]*Server, 0), 39 | } 40 | } 41 | 42 | // AddServer 添加服务器 43 | func (b *BaseLoadBalancer) AddServer(server *Server) { 44 | b.mu.Lock() 45 | defer b.mu.Unlock() 46 | b.Servers = append(b.Servers, server) 47 | } 48 | 49 | // RemoveServer 移除服务器 50 | func (b *BaseLoadBalancer) RemoveServer(address string) { 51 | b.mu.Lock() 52 | defer b.mu.Unlock() 53 | for i, server := range b.Servers { 54 | if server.Address == address { 55 | b.Servers = append(b.Servers[:i], b.Servers[i+1:]...) 56 | break 57 | } 58 | } 59 | } 60 | 61 | // GetServerCount 获取服务器数量 62 | func (b *BaseLoadBalancer) GetServerCount() int { 63 | b.mu.RLock() 64 | defer b.mu.RUnlock() 65 | return len(b.Servers) 66 | } 67 | -------------------------------------------------------------------------------- /loadbalancer/random.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | // RandomLoadBalancer 随机选择负载均衡器 8 | type RandomLoadBalancer struct { 9 | *BaseLoadBalancer 10 | rng *rand.Rand 11 | } 12 | 13 | // NewRandomLoadBalancer 创建随机选择负载均衡器 14 | func NewRandomLoadBalancer() *RandomLoadBalancer { 15 | return &RandomLoadBalancer{ 16 | BaseLoadBalancer: NewBaseLoadBalancer(), 17 | rng: rand.New(rand.NewSource(rand.Int63())), 18 | } 19 | } 20 | 21 | // GetServer 随机选择一个服务器 22 | func (r *RandomLoadBalancer) GetServer(key string) *Server { 23 | r.mu.RLock() 24 | defer r.mu.RUnlock() 25 | 26 | // 过滤出可用的服务器 27 | availableServers := make([]*Server, 0) 28 | for _, server := range r.Servers { 29 | if server.Weight > 0 { 30 | availableServers = append(availableServers, server) 31 | } 32 | } 33 | 34 | if len(availableServers) == 0 { 35 | return nil 36 | } 37 | 38 | // 计算总权重 39 | totalWeight := 0 40 | for _, server := range availableServers { 41 | totalWeight += server.Weight 42 | } 43 | 44 | // 随机选择一个服务器(考虑权重) 45 | randomWeight := r.rng.Intn(totalWeight) 46 | currentWeight := 0 47 | for _, server := range availableServers { 48 | currentWeight += server.Weight 49 | if randomWeight < currentWeight { 50 | return server 51 | } 52 | } 53 | 54 | // 如果因为浮点数精度问题没有选中任何服务器,返回最后一个 55 | return availableServers[len(availableServers)-1] 56 | } 57 | -------------------------------------------------------------------------------- /loadbalancer/round_robin.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | // RoundRobinLoadBalancer 轮询负载均衡器 8 | type RoundRobinLoadBalancer struct { 9 | *BaseLoadBalancer 10 | currentIndex int64 11 | weighted bool 12 | } 13 | 14 | // NewRoundRobinLoadBalancer 创建轮询负载均衡器 15 | func NewRoundRobinLoadBalancer(weighted bool) *RoundRobinLoadBalancer { 16 | return &RoundRobinLoadBalancer{ 17 | BaseLoadBalancer: NewBaseLoadBalancer(), 18 | weighted: weighted, 19 | } 20 | } 21 | 22 | // GetServer 获取下一个服务器 23 | func (lb *RoundRobinLoadBalancer) GetServer(key string) *Server { 24 | lb.mu.Lock() 25 | defer lb.mu.Unlock() 26 | 27 | if len(lb.Servers) == 0 { 28 | return nil 29 | } 30 | 31 | if !lb.weighted { 32 | // 非加权轮询 33 | index := atomic.AddInt64(&lb.currentIndex, 1) % int64(len(lb.Servers)) 34 | return lb.Servers[index] 35 | } 36 | 37 | // 调试输出 38 | /* 39 | fmt.Println("当前服务器权重状态:") 40 | for i, server := range lb.Servers { 41 | fmt.Printf("Server%d[%s] 当前权重:%d, 有效权重:%d\n", 42 | i+1, server.Address, server.CurrentWeight, server.EffectiveWeight) 43 | } 44 | */ 45 | 46 | // 实现平滑加权轮询(Smooth Weighted Round-Robin) 47 | totalWeight := 0 48 | var bestServer *Server 49 | 50 | // 计算总有效权重,并为每个服务器增加当前权重 51 | for _, server := range lb.Servers { 52 | // 确保有效权重被初始化 53 | if server.EffectiveWeight == 0 { 54 | server.EffectiveWeight = server.Weight 55 | } 56 | // 当前权重增加有效权重 57 | server.CurrentWeight += server.EffectiveWeight 58 | totalWeight += server.EffectiveWeight 59 | 60 | // 选择当前权重最大的服务器 61 | if bestServer == nil || server.CurrentWeight > bestServer.CurrentWeight { 62 | bestServer = server 63 | } 64 | } 65 | 66 | // 如果找到了最佳服务器,减少其当前权重 67 | if bestServer != nil { 68 | bestServer.CurrentWeight -= totalWeight 69 | } 70 | 71 | return bestServer 72 | } 73 | 74 | // ResetWeights 重置所有服务器的权重 75 | func (lb *RoundRobinLoadBalancer) ResetWeights() { 76 | lb.mu.Lock() 77 | defer lb.mu.Unlock() 78 | 79 | // 重置所有服务器的权重 80 | for _, server := range lb.Servers { 81 | server.CurrentWeight = 0 82 | server.EffectiveWeight = server.Weight 83 | } 84 | 85 | // 重置轮询状态 86 | atomic.StoreInt64(&lb.currentIndex, 0) 87 | } 88 | 89 | // AddServer 添加服务器 90 | func (lb *RoundRobinLoadBalancer) AddServer(server *Server) { 91 | // 只有在未设置的情况下初始化EffectiveWeight 92 | if server.EffectiveWeight == 0 { 93 | server.EffectiveWeight = server.Weight 94 | } 95 | lb.BaseLoadBalancer.AddServer(server) 96 | } 97 | -------------------------------------------------------------------------------- /loadbalancer/round_robin_test.go: -------------------------------------------------------------------------------- 1 | package loadbalancer 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestWeightedRoundRobin(t *testing.T) { 9 | // 创建加权轮询负载均衡器 10 | lb := NewRoundRobinLoadBalancer(true) 11 | 12 | // 添加3个权重不同的服务器 13 | servers := []*Server{ 14 | {Address: "Server-A", Weight: 3, EffectiveWeight: 3}, 15 | {Address: "Server-B", Weight: 2, EffectiveWeight: 2}, 16 | {Address: "Server-C", Weight: 1, EffectiveWeight: 1}, 17 | } 18 | 19 | for _, server := range servers { 20 | lb.AddServer(server) 21 | } 22 | 23 | // 平滑加权轮询算法的预期选择序列: [A,B,A,C,B,A] 24 | // 对于权重[3,2,1],这种分布更均匀 25 | fmt.Println("平滑加权轮询理论序列: [A,B,A,C,B,A]") 26 | 27 | // 连续选择12次,应该看到模式重复 28 | selections := make([]string, 12) 29 | for i := 0; i < 12; i++ { 30 | server := lb.GetServer("") 31 | if server != nil { 32 | selections[i] = fmt.Sprintf("%s", server.Address) 33 | } else { 34 | selections[i] = "nil" 35 | } 36 | } 37 | 38 | // 输出选择结果 39 | fmt.Println("加权轮询选择序列:") 40 | for i, selection := range selections { 41 | fmt.Printf("第%d次选择: %s\n", i+1, selection) 42 | } 43 | 44 | // 由于平滑加权轮询算法可能产生略有不同的结果,我们不强制检查完全匹配 45 | // 而是检查选择结果中各服务器出现的次数是否与权重比例匹配 46 | countA := 0 47 | countB := 0 48 | countC := 0 49 | 50 | for _, selection := range selections { 51 | switch selection { 52 | case "Server-A": 53 | countA++ 54 | case "Server-B": 55 | countB++ 56 | case "Server-C": 57 | countC++ 58 | } 59 | } 60 | 61 | // 检查服务器选择的比例是否大致符合权重比例 3:2:1 62 | fmt.Printf("服务器选择次数 - A: %d, B: %d, C: %d\n", countA, countB, countC) 63 | 64 | // 这里不需要严格检查模式,只要大致比例符合即可 65 | totalCount := float64(countA + countB + countC) 66 | if totalCount == 0 { 67 | t.Error("没有选择任何服务器") 68 | } else { 69 | ratioA := float64(countA) / totalCount 70 | ratioB := float64(countB) / totalCount 71 | ratioC := float64(countC) / totalCount 72 | 73 | // 理论上比例应该是 3/6, 2/6, 1/6,允许有小的偏差 74 | if ratioA < 0.4 || ratioB < 0.25 || ratioC < 0.1 { 75 | t.Errorf("服务器选择比例不符合权重比例. A: %.2f, B: %.2f, C: %.2f", ratioA, ratioB, ratioC) 76 | } 77 | } 78 | 79 | // 重置服务器权重 80 | lb.ResetWeights() 81 | } 82 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/load-balancer-algorithm/loadbalancer" 8 | ) 9 | 10 | func main() { 11 | // 创建测试服务器,使用不同的权重 12 | servers := []*loadbalancer.Server{ 13 | {Address: "192.168.1.1:8080", Weight: 1}, // 权重1 14 | {Address: "192.168.1.2:8080", Weight: 2}, // 权重2 15 | {Address: "192.168.1.3:8080", Weight: 3}, // 权重3 16 | } 17 | 18 | // 测试随机选择算法 19 | fmt.Println("测试随机选择算法:") 20 | randomLB := loadbalancer.NewRandomLoadBalancer() 21 | for _, server := range servers { 22 | randomLB.AddServer(server) 23 | } 24 | for i := 0; i < 5; i++ { 25 | server := randomLB.GetServer("") 26 | fmt.Printf("第%d次选择: %s (权重: %d)\n", i+1, server.Address, server.Weight) 27 | } 28 | 29 | // 测试轮询算法 30 | fmt.Println("\n测试轮询算法:") 31 | roundRobinLB := loadbalancer.NewRoundRobinLoadBalancer(false) // 非加权轮询 32 | for _, server := range servers { 33 | roundRobinLB.AddServer(server) 34 | } 35 | for i := 0; i < 5; i++ { 36 | server := roundRobinLB.GetServer("") 37 | fmt.Printf("第%d次选择: %s (权重: %d)\n", i+1, server.Address, server.Weight) 38 | } 39 | 40 | // 测试加权轮询算法 41 | fmt.Println("\n测试加权轮询算法:") 42 | weightedRoundRobinLB := loadbalancer.NewRoundRobinLoadBalancer(true) // 加权轮询 43 | 44 | // 创建新的服务器数组,确保CurrentWeight和EffectiveWeight正确初始化 45 | wrrServers := []*loadbalancer.Server{ 46 | {Address: "192.168.1.3:8080", Weight: 3, EffectiveWeight: 3, CurrentWeight: 0}, // 权重3, 服务器A 47 | {Address: "192.168.1.2:8080", Weight: 2, EffectiveWeight: 2, CurrentWeight: 0}, // 权重2, 服务器B 48 | {Address: "192.168.1.1:8080", Weight: 1, EffectiveWeight: 1, CurrentWeight: 0}, // 权重1, 服务器C 49 | } 50 | 51 | for _, server := range wrrServers { 52 | weightedRoundRobinLB.AddServer(server) 53 | } 54 | 55 | fmt.Println("预期序列: [3权重, 2权重, 3权重, 1权重, 2权重, 3权重] - 平滑加权轮询") 56 | for i := 0; i < 10; i++ { 57 | server := weightedRoundRobinLB.GetServer("") 58 | fmt.Printf("第%d次选择: %s (权重: %d, 当前权重: %d)\n", i+1, server.Address, server.Weight, server.CurrentWeight) 59 | } 60 | weightedRoundRobinLB.ResetWeights() // 重置权重 61 | 62 | // 测试最小连接算法 63 | fmt.Println("\n测试最小连接算法:") 64 | leastConnLB := loadbalancer.NewLeastConnectionsLoadBalancer(false) // 非加权最小连接 65 | for _, server := range servers { 66 | leastConnLB.AddServer(server) 67 | } 68 | 69 | // 先创建一些连接 70 | serversToRelease := make([]*loadbalancer.Server, 0) 71 | // 给权重为1的服务器添加1个连接 72 | s1 := leastConnLB.GetServer("") 73 | serversToRelease = append(serversToRelease, s1) 74 | fmt.Printf("预先添加连接: %s (权重: %d, 当前连接数: %d)\n", s1.Address, s1.Weight, s1.CurrentConnections) 75 | 76 | // 给权重为2的服务器添加2个连接 77 | for i := 0; i < 2; i++ { 78 | s2 := leastConnLB.GetServer("") 79 | serversToRelease = append(serversToRelease, s2) 80 | fmt.Printf("预先添加连接: %s (权重: %d, 当前连接数: %d)\n", s2.Address, s2.Weight, s2.CurrentConnections) 81 | } 82 | 83 | // 现在测试算法选择 84 | fmt.Println("\n开始测试最小连接算法选择过程:") 85 | for i := 0; i < 5; i++ { 86 | server := leastConnLB.GetServer("") 87 | fmt.Printf("第%d次选择: %s (权重: %d, 当前连接数: %d)\n", i+1, server.Address, server.Weight, server.CurrentConnections) 88 | // 每次选择后延迟释放连接 89 | go func(s *loadbalancer.Server) { 90 | time.Sleep(500 * time.Millisecond) 91 | leastConnLB.ReleaseConnection(s) 92 | }(server) 93 | time.Sleep(100 * time.Millisecond) // 等待一点时间以便观察选择效果 94 | } 95 | 96 | // 释放之前添加的连接 97 | for _, s := range serversToRelease { 98 | leastConnLB.ReleaseConnection(s) 99 | } 100 | 101 | time.Sleep(1 * time.Second) // 等待所有连接释放完成 102 | 103 | // 测试加权最小连接算法 104 | fmt.Println("\n测试加权最小连接算法:") 105 | weightedLeastConnLB := loadbalancer.NewLeastConnectionsLoadBalancer(true) // 加权最小连接 106 | for _, server := range servers { 107 | weightedLeastConnLB.AddServer(server) 108 | } 109 | 110 | // 先创建一些连接,使每个服务器的连接数/权重比不同 111 | // 权重1的服务器添加5个连接(5/1=5) 112 | s1WLC := weightedLeastConnLB.GetServer("") 113 | for i := 0; i < 4; i++ { // 已经有1个连接,再添加4个 114 | weightedLeastConnLB.GetServer("") 115 | } 116 | fmt.Printf("预先设置: %s (权重: %d, 当前连接数: %d, 比值: %.2f)\n", 117 | s1WLC.Address, s1WLC.Weight, s1WLC.CurrentConnections, float64(s1WLC.CurrentConnections)/float64(s1WLC.Weight)) 118 | 119 | // 权重2的服务器添加8个连接(8/2=4) 120 | s2WLC := weightedLeastConnLB.GetServer("") 121 | for i := 0; i < 7; i++ { // 已经有1个连接,再添加7个 122 | weightedLeastConnLB.GetServer("") 123 | } 124 | fmt.Printf("预先设置: %s (权重: %d, 当前连接数: %d, 比值: %.2f)\n", 125 | s2WLC.Address, s2WLC.Weight, s2WLC.CurrentConnections, float64(s2WLC.CurrentConnections)/float64(s2WLC.Weight)) 126 | 127 | // 权重3的服务器添加9个连接(9/3=3)- 应该是最小的比值 128 | s3WLC := weightedLeastConnLB.GetServer("") 129 | for i := 0; i < 8; i++ { // 已经有1个连接,再添加8个 130 | weightedLeastConnLB.GetServer("") 131 | } 132 | fmt.Printf("预先设置: %s (权重: %d, 当前连接数: %d, 比值: %.2f)\n", 133 | s3WLC.Address, s3WLC.Weight, s3WLC.CurrentConnections, float64(s3WLC.CurrentConnections)/float64(s3WLC.Weight)) 134 | 135 | fmt.Println("\n开始测试加权最小连接算法选择过程:") 136 | for i := 0; i < 5; i++ { 137 | server := weightedLeastConnLB.GetServer("") 138 | fmt.Printf("第%d次选择: %s (权重: %d, 当前连接数: %d, 比值: %.2f)\n", 139 | i+1, server.Address, server.Weight, server.CurrentConnections, 140 | float64(server.CurrentConnections)/float64(server.Weight)) 141 | 142 | // 每次选择后延迟释放连接 143 | go func(s *loadbalancer.Server) { 144 | time.Sleep(500 * time.Millisecond) 145 | weightedLeastConnLB.ReleaseConnection(s) 146 | }(server) 147 | time.Sleep(100 * time.Millisecond) // 等待一点时间以便观察选择效果 148 | } 149 | 150 | time.Sleep(1 * time.Second) // 等待所有连接释放完成 151 | 152 | // 测试 Maglev 一致性哈希算法 153 | fmt.Println("\n测试 Maglev 一致性哈希算法:") 154 | maglevHashLB := loadbalancer.NewMaglevHashLoadBalancer() 155 | for _, server := range servers { 156 | maglevHashLB.AddServer(server) 157 | } 158 | 159 | // 使用更多样的测试键 160 | testKeys := []string{ 161 | "user1", "user2", "user3", "user4", "user5", 162 | "customer10", "customer20", "customer30", "customer40", "customer50", 163 | "product100", "product200", "product300", "product400", "product500", 164 | "order1000", "order2000", "order3000", "order4000", "order5000", 165 | "item10000", "item20000", "item30000", "item40000", "item50000", 166 | } 167 | 168 | // 服务器选择计数 169 | serverCount := make(map[string]int) 170 | 171 | fmt.Println("各键映射到的服务器:") 172 | for _, key := range testKeys { 173 | server := maglevHashLB.GetServer(key) 174 | if server != nil { 175 | fmt.Printf("Key: %-12s 选择的服务器: %s (权重: %d)\n", key, server.Address, server.Weight) 176 | serverCount[server.Address]++ 177 | } else { 178 | fmt.Printf("Key: %-12s 无可用服务器\n", key) 179 | } 180 | } 181 | 182 | // 展示分布统计 183 | fmt.Println("\n服务器负载分布统计:") 184 | totalKeys := len(testKeys) 185 | for _, server := range servers { 186 | count := serverCount[server.Address] 187 | percentage := float64(count) / float64(totalKeys) * 100 188 | fmt.Printf("服务器: %-16s 权重: %d 分配请求数: %d 百分比: %.2f%% 期望百分比: %.2f%%\n", 189 | server.Address, server.Weight, count, percentage, 190 | float64(server.Weight)/float64(1+2+3)*100) // 1+2+3是所有服务器权重之和 191 | } 192 | 193 | // 在另一个场景测试:删除一个服务器,然后再测试键分布 194 | fmt.Println("\n移除服务器后的一致性哈希测试:") 195 | // 移除中间权重的服务器 196 | maglevHashLB.RemoveServer(servers[1]) // 移除权重为2的服务器 197 | 198 | // 记录移除服务器前的映射,检查变化 199 | fmt.Println("移除服务器后的键映射变化:") 200 | changedCount := 0 201 | newServerCount := make(map[string]int) 202 | 203 | for _, key := range testKeys { 204 | server := maglevHashLB.GetServer(key) 205 | if server != nil { 206 | newServerCount[server.Address]++ 207 | if serverCount[server.Address] != newServerCount[server.Address] { 208 | changedCount++ 209 | } 210 | } 211 | } 212 | 213 | // 展示新的分布统计 214 | fmt.Println("\n移除服务器后的负载分布:") 215 | for i, server := range servers { 216 | if i == 1 { // 跳过已移除的服务器 217 | continue 218 | } 219 | newCount := newServerCount[server.Address] 220 | newPercentage := float64(newCount) / float64(totalKeys) * 100 221 | remainingWeight := 1 + 3 // 剩余的权重总和 222 | fmt.Printf("服务器: %-16s 权重: %d 分配请求数: %d 百分比: %.2f%% 期望百分比: %.2f%%\n", 223 | server.Address, server.Weight, newCount, newPercentage, 224 | float64(server.Weight)/float64(remainingWeight)*100) 225 | } 226 | 227 | // 输出变化率 228 | changePercentage := float64(changedCount) / float64(totalKeys) * 100 229 | fmt.Printf("\n移除服务器后,%.2f%% 的请求被重新映射到其他服务器\n", changePercentage) 230 | } 231 | --------------------------------------------------------------------------------