LoadBalance 中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。 避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。 通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。 在为高负载服务器分流的同时,还可以避免资源浪费,一举两得。
应用层的负载均衡器,例如nginx
,可以将请求发散到后端多个服务,实现负载均衡。
实现方式
集中式
在客户端和服务端之间有一个独立的LB(通常是专门的硬件设备如FS,或者基于软件如LVS,HAproxy等实现)
LB上有所有服务的地址映射表,通过这个映射表实现流量转发
进程内(gRPC采用的方式就是这种)
由客户端内的一个子进程管理,进程获取所有服务端信息,并且保持所有服务端的连接,通过某个LB策略访问某个服务端。
独立进程
Service Mesh的思想,使用一个本地的proxy 进程,通过与本地proxy进程通信,由proxy服务实现LB
负载均衡算法
负载均衡算法决定了后端的哪些健康服务器会被选中。几个常用的算法:
Round Robin(轮询): 为第一个请求选择列表中的第一个服务器,然后按顺序向下移动列表直到结尾,然后循环。
Least Connections(最小连接): 优先选择连接数最少的服务器,在普遍会话较长的情况下推荐使用。
Source: 根据请求源的 IP 的散列(hash)来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。
在k8s
环境上,k8s
通过svc
和probe
实现服务发现,但是没有提供七层负载均衡能力,在pod扩容时,负载均衡能力就失效了。因此,需要gRPC自己实现负载均衡。
具体实现方案可以查看推荐文档:gRPC(Go)教程(十三)— Kubernetes 环境下的 gRPC 负载均衡
相比较之下,更加推荐使用专门提供负载能力的consul
实现
gRPC使用负载均衡 官方文档:Load Balancing in gRPC
使用consul配合gRPC
安装
1 go get github.com/mbobakov/grpc-consul-resolver
客户端实例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "time" "log" _ "github.com/mbobakov/grpc-consul-resolver" "google.golang.org/grpc" ) func main () { conn, err := grpc.Dial( "consul://127.0.0.1:8500/mitaka-grpc?wait=14s" , grpc.WithInsecure(), grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}` ), ) if err != nil { log.Fatal(err) } defer conn.Close() ... client := minegrpc.NewGreeterClient(dial) reply, err := client.SayHello(ctx, &minegrpc.HelloRequest{Name: "mitaka" }) }
用法:
consul://[user:password@]127.0.0.127:8555/my-service?[healthy=]&[wait=]&[near=]&[insecure=]&[limit=]&[tag=]&[token=]
真实测试案例
启动两个gRPC
服务,都注册到consul
中
负载均衡的现象
1 2 3 4 5 6 7 8 ➜ client go run main.go reply: message:"i am service 1" ➜ client go run main.go reply: message:"i am service" ➜ client go run main.go reply: message:"i am service" ➜ client go run main.go reply: message:"i am service 1"
关于长连接:
发送请求时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@n206 ~]# netstat -an | grep -E "36937|40941|8500" tcp 0 0 10.0.0.1:37638 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:33788 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:34094 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:60304 10.0.0.161:40941 TIME_WAIT tcp 0 0 127.0.0.1:43786 127.0.0.1:8500 FIN_WAIT2 tcp 0 0 10.0.0.1:38040 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:33284 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:32842 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:59526 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:35834 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:36790 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:34748 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:59916 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:40278 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:39350 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:40042 10.0.0.206:36937 TIME_WAIT tcp 0 0 10.0.0.1:60674 10.0.0.161:40941 TIME_WAIT tcp 0 0 10.0.0.1:38968 10.0.0.206:36937 TIME_WAIT tcp6 0 0 :::8500 :::* LISTEN tcp6 0 0 127.0.0.1:8500 127.0.0.1:43786 CLOSE_WAIT
客户端在本地,127.0.0.1:43786
请求127.0.0.1:8500
,也就是rpc
客户端请求consul
服务端而不是rpc
客户端与rpc
服务端直接通信。consul
服务端与rpc
服务端保持长连接,中间过程类似于一个代理的作用。
推荐阅读:
什么是负载均衡?
gRPC(Go)教程(十三)— Kubernetes 环境下的 gRPC 负载均衡