grpc 自带负载均衡
核心 : “google.golang.org/grpc/resolver”
bp
$ protoc -I ./ pb/*.proto —plugin=protoc-gen-go=protoc-gen-go —go_out=plugins=grpc:pb
syntax = "proto3";
option go_package = "./echo";
package echo;
// Request
message EchoRequest {
string message = 1;
}
// Response
message EchoResponse {
string message = 1;
}
// Echo is the echo service.
service Echo {
// UnaryEcho is unary echo.
rpc UnaryEcho(EchoRequest) returns (EchoResponse) {}
// ServerStreamingEcho is server side streaming.
rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {}
// ClientStreamingEcho is client side streaming.
rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {}
// BidirectionalStreamingEcho is bidi streaming.
rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse) {}
}
server端
package main
import (
"context"
"fmt"
"go_script/grpc_2/pb/echo"
"google.golang.org/grpc"
"log"
"net"
"sync"
)
var (
addrs = []string{":50051", ":50052", ":50053", ":50054"}
)
type ecServer struct {
echo.UnimplementedEchoServer
addr string
}
func (s *ecServer) UnaryEcho(ctx context.Context, req *echo.EchoRequest) (*echo.EchoResponse, error) {
log.Println(req.Message, s.addr)
return &echo.EchoResponse{Message: fmt.Sprintf("%s (from %s)", req.Message, s.addr)}, nil
}
// 启动grpc服务
func startServer(addr string) {
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
echo.RegisterEchoServer(s, &ecServer{addr: addr})
log.Printf("serving on %s\n", addr)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func main() {
var wg sync.WaitGroup
for _, addr := range addrs {
wg.Add(1)
go func(addr string) {
defer wg.Done()
startServer(addr)
}(addr)
}
wg.Wait()
}
client端
package main
import (
"context"
"fmt"
"go_script/grpc_2/pb/echo"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"log"
"time"
)
const (
Scheme = "scheme"
ServiceName = "service_name"
)
var addrs = []string{"localhost:50051", "localhost:50052", "localhost:50053", "localhost:50054"}
// 负载均衡实现
type ResolverBuilder struct{}
func (*ResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r := &exampleResolver{
target: target,
cc: cc,
addrsStore: map[string][]string{
ServiceName: addrs,
},
}
r.start()
return r, nil
}
func (*ResolverBuilder) Scheme() string { return Scheme }
type exampleResolver struct {
target resolver.Target
cc resolver.ClientConn
addrsStore map[string][]string
}
func (r *exampleResolver) start() {
addrStrs := r.addrsStore[r.target.Endpoint]
addrs := make([]resolver.Address, len(addrStrs))
for i, s := range addrStrs {
addrs[i] = resolver.Address{Addr: s}
}
r.cc.UpdateState(resolver.State{Addresses: addrs})
}
func (*exampleResolver) ResolveNow(o resolver.ResolveNowOptions) {}
func (*exampleResolver) Close() {}
func init() {
resolver.Register(&ResolverBuilder{})
}
func main() {
roundrobinConn, err := grpc.Dial(
fmt.Sprintf("%s:///%s", Scheme, ServiceName),
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin":{}}]}`), // https://github.com/grpc/grpc/blob/master/doc/service_config.md
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer roundrobinConn.Close()
fmt.Println("--- calling helloworld.Greeter/SayHello with round_robin ---")
hwc := echo.NewEchoClient(roundrobinConn)
for i := 0; i < 20; i++ {
callUnaryEcho(hwc, "this is load_balancing")
}
}
func callUnaryEcho(c echo.EchoClient, message string) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.UnaryEcho(ctx, &echo.EchoRequest{Message: message})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
fmt.Println(r.Message)
}
代码地址
https://github.com/mangenotwork/man/tree/master/core/grpc_2