负载均衡
概述
负载均衡是分布式系统中的关键机制,它将请求合理分配到多个服务实例上,以提高系统的吞吐量、可用性和容错能力。在 CloudWeGo 生态中,Kitex 提供了丰富的负载均衡策略,支持多种算法和自定义扩展。
为什么需要负载均衡?
在微服务架构中,服务通常有多个实例运行:
- 单个实例无法承受所有流量压力
- 需要避免某些实例过载而其他实例空闲
- 实例故障时需要自动切换到健康实例
- 不同实例可能具有不同的处理能力
负载均衡通过合理的流量分配策略,确保系统资源得到充分利用,提升整体服务质量。
核心内容
1. 负载均衡原理
基本概念
- 服务实例(Instance):提供服务的具体节点,包含地址、权重、标签等信息
- 负载均衡器(LoadBalancer):根据策略从实例列表中选择一个实例
- 权重(Weight):实例的处理能力权重,权重越高分配的流量越多
- 健康检查(Health Check):检测实例是否可用,剔除不健康实例
工作流程
1. 服务发现 → 获取服务实例列表
2. 健康检查 → 过滤不健康实例
3. 权重计算 → 根据策略计算实例权重
4. 实例选择 → 选择一个实例进行调用
5. 调用执行 → 向选中实例发送请求2. Kitex 负载均衡配置
基本使用
Kitex 客户端通过 loadbalance 扩展点配置负载均衡策略:
go
package main
import (
"github.com/cloudwego/kitex/client"
"github.com/cloudwego/kitex/pkg/loadbalance"
)
func main() {
lb := loadbalance.NewWeightedRoundRobinBalancer()
c, err := echo.NewClient(
"echo",
client.WithLoadBalancer(lb),
)
if err != nil {
panic(err)
}
ctx := context.Background()
for i := 0; i < 10; i++ {
resp, err := c.Echo(ctx, &api.Request{Message: "Hello"})
if err != nil {
log.Printf("call failed: %v", err)
continue
}
log.Println(resp.Message)
}
}3. 内置负载均衡策略
轮询(Round Robin)
轮询策略按顺序依次选择实例,是最简单的负载均衡算法:
go
import (
"github.com/cloudwego/kitex/pkg/loadbalance"
)
lb := loadbalance.NewRoundRobinBalancer()特点:
- 实现简单,性能高
- 适用于实例性能相近的场景
- 不考虑实例权重
加权轮询(Weighted Round Robin)
加权轮询根据实例权重分配流量,权重高的实例获得更多请求:
go
import (
"github.com/cloudwego/kitex/pkg/loadbalance"
)
lb := loadbalance.NewWeightedRoundRobinBalancer()特点:
- 考虑实例处理能力差异
- 权重配置灵活
- 适用于异构环境
随机(Random)
随机策略随机选择实例:
go
import (
"github.com/cloudwego/kitex/pkg/loadbalance"
)
lb := loadbalance.NewRandomBalancer()特点:
- 实现简单
- 大量请求时分布均匀
- 不考虑实例权重
加权随机(Weighted Random)
加权随机根据权重进行随机选择:
go
import (
"github.com/cloudwego/kitex/pkg/loadbalance"
)
lb := loadbalance.NewWeightedRandomBalancer()特点:
- 结合权重和随机性
- 避免加权轮询的请求聚集
- 适用于需要打散请求的场景
4. 自定义负载均衡策略
Kitex 支持实现自定义的负载均衡策略:
go
package myloadbalance
import (
"github.com/cloudwego/kitex/pkg/discovery"
"github.com/cloudwego/kitex/pkg/loadbalance"
)
type MyLoadBalancer struct{}
func NewMyLoadBalancer() *MyLoadBalancer {
return &MyLoadBalancer{}
}
func (lb *MyLoadBalancer) GetPicker(insts []discovery.Instance) loadbalance.Picker {
return &MyPicker{insts: insts}
}
type MyPicker struct {
insts []discovery.Instance
index int
}
func (p *MyPicker) Next() discovery.Instance {
if len(p.insts) == 0 {
return nil
}
inst := p.insts[p.index%len(p.insts)]
p.index++
return inst
}
func (p *MyPicker) Reset() {
p.index = 0
}使用自定义负载均衡器
go
package main
import (
"myloadbalance"
"github.com/cloudwego/kitex/client"
)
func main() {
lb := myloadbalance.NewMyLoadBalancer()
c, err := echo.NewClient(
"echo",
client.WithLoadBalancer(lb),
)
if err != nil {
panic(err)
}
ctx := context.Background()
resp, err := c.Echo(ctx, &api.Request{Message: "Hello"})
if err != nil {
panic(err)
}
log.Println(resp.Message)
}5. 实例权重配置
服务端配置权重
服务注册时可以设置实例权重:
go
package main
import (
"github.com/cloudwego/kitex/server"
"github.com/cloudwego/kitex/pkg/registry"
)
func main() {
info := ®istry.Info{
ServiceName: "echo",
Addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8888},
Weight: 100,
}
svr := echo.NewServer(
new(EchoImpl),
server.WithRegistryInfo(info),
)
if err := svr.Run(); err != nil {
log.Println("server stopped with error:", err)
}
}动态调整权重
根据实例负载动态调整权重:
go
package main
import (
"runtime"
"time"
)
type DynamicWeightRegistry struct {
registry.Registry
}
func (r *DynamicWeightRegistry) updateWeight(info *registry.Info) {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)
memUsage := float64(memStats.Alloc) / float64(memStats.Sys)
if memUsage > 0.8 {
info.Weight = 10
} else if memUsage > 0.6 {
info.Weight = 50
} else {
info.Weight = 100
}
}6. 一致性哈希
一致性哈希适用于需要会话保持的场景:
go
package main
import (
"github.com/cloudwego/kitex/client"
"github.com/cloudwego/kitex/pkg/loadbalance"
)
func main() {
lb := loadbalance.NewConsistentHashBalancer(
loadbalance.NewConsistentHashPickerFactory(
loadbalance.DefaultConsistentHashKey,
),
)
c, err := echo.NewClient(
"echo",
client.WithLoadBalancer(lb),
)
if err != nil {
panic(err)
}
ctx := context.Background()
resp, err := c.Echo(ctx, &api.Request{Message: "Hello"})
if err != nil {
panic(err)
}
log.Println(resp.Message)
}7. 负载均衡与重试
负载均衡与重试机制配合使用:
go
package main
import (
"github.com/cloudwego/kitex/client"
"github.com/cloudwego/kitex/pkg/loadbalance"
"github.com/cloudwego/kitex/pkg/retry"
)
func main() {
lb := loadbalance.NewWeightedRoundRobinBalancer()
fp := retry.NewFailurePolicy()
fp.MaxRetryTimes = 3
fp.RetrySameNode = false
c, err := echo.NewClient(
"echo",
client.WithLoadBalancer(lb),
client.WithFailureRetry(fp),
)
if err != nil {
panic(err)
}
ctx := context.Background()
resp, err := c.Echo(ctx, &api.Request{Message: "Hello"})
if err != nil {
panic(err)
}
log.Println(resp.Message)
}最佳实践
1. 策略选择
| 策略 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| 轮询 | 实例性能相近 | 简单高效 | 不考虑权重 |
| 加权轮询 | 异构环境 | 权重灵活 | 可能请求聚集 |
| 随机 | 大流量场景 | 分布均匀 | 不考虑权重 |
| 加权随机 | 异构环境 | 权重+随机性 | 实现稍复杂 |
| 一致性哈希 | 会话保持 | 相同请求路由到同一实例 | 节点变化时影响较大 |
2. 权重设置建议
go
info := ®istry.Info{
ServiceName: "echo",
Addr: addr,
Weight: calculateWeight(),
}
func calculateWeight() int {
cpuUsage := getCPUUsage()
memUsage := getMemoryUsage()
if cpuUsage > 80 || memUsage > 80 {
return 10
} else if cpuUsage > 60 || memUsage > 60 {
return 50
}
return 100
}3. 健康检查集成
go
lb := loadbalance.NewWeightedRoundRobinBalancer(
loadbalance.WithRespectInstanceWeight(),
)小结
本章介绍了 CloudWeGo 生态中的负载均衡机制:
- 核心概念:理解负载均衡的工作原理和基本流程
- 内置策略:掌握轮询、加权轮询、随机、加权随机等策略的使用
- 自定义扩展:学会实现自定义的负载均衡器
- 权重配置:了解实例权重的配置和动态调整方法
- 一致性哈希:掌握会话保持场景下的负载均衡方案
- 最佳实践:学会根据场景选择合适的负载均衡策略
负载均衡是提升系统性能和可靠性的重要手段,合理选择和配置负载均衡策略对于构建高性能微服务系统至关重要。