在new GRPC client的时候,可以传一个解析器resolver, 它主要用途是当有注册中心时候, 服务端启动多个实例进行注册, 客户端传一个服务名字, 解析器按照一定的方式(resolver要干的事)解析出一些ip,客户端通过负载均衡算法挑选出一个去链接
GRPC默认支持三种解析器, etcd sdk实现了grpc的resolver接口
根据grpc文档, 当client拨号时, 采用的target格式支持这几种. 上面说了用不同的拨号函数scheme默认值不同.新版本推荐用NewClient, Dial过期了.
grpc doc: custom-name-resolution
客户端
passthroughConn, err := grpc.NewClient(
fmt.Sprintf("passthrough:///%s", backendAddr), // Dial to "passthrough:///localhost:50051"
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
当用grpc.NewClient时, schema默认为dns, 当用grpc.Dial时, schema默认为passthrough
// withDefaultScheme is used to allow Dial to use "passthrough" as the default
// name resolver, while NewClient uses "dns" otherwise.
func withDefaultScheme(s string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.defaultScheme = s
})
}
etcd实现了grpc的resolver接口, client在连接etcd时候, scheme必须为etcd://
client, err := clientv3.NewFromURL(url)
etcdResolver, err := resolver.NewBuilder(client)
conn, err := grpc.Dial("etcd://localhost:2379/"+service.GetPathServerName("s1"),
grpc.WithInsecure(),
grpc.WithResolvers(etcdResolver),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
grpc.WithBlock(),
)
目录
├── client
│ └── main.go
├── go.mod
├── go.sum
├── main.go
├── proto
│ ├── helloworld.pb.go
│ ├── helloworld.proto
│ └── helloworld_grpc.pb.go
├── readme.txt
└── server
└── main.go
代码
protp/helloworld.proto
syntax = "proto3";
option go_package=".;pb";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
//protoc --go_out=./proto --go-grpc_out=./proto ./proto/*.proto
server/main.go
server注册使用etcd sdk提供的grpcproxy, key ttl是60
// Package main implements a server for Greeter service.
package main
import (
"context"
"flag"
"fmt"
"go.uber.org/zap"
pb "grpc-demo/proto"
"log"
"net"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/proxy/grpcproxy"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
flag.Parse()
config := clientv3.Config{
Endpoints: []string{"http://127.0.0.1:2379"},
DialTimeout: 60,
}
etcdClient, err := clientv3.New(config)
if err != nil {
log.Fatal(err)
}
defer etcdClient.Close()
lg, _ := zap.NewDevelopment()
grpcproxy.Register(lg, etcdClient, "grpc-demo2/svc", fmt.Sprintf("127.0.0.1:%d", *port), 60)
lis, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
client/main.go
// Package main implements a client for Greeter service.
package main
import (
"context"
"flag"
"fmt"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/resolver"
pb "grpc-demo/proto"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
config := clientv3.Config{
Endpoints: []string{"http://127.0.0.1:2379"},
DialTimeout: 60,
}
etcdClient, err := clientv3.New(config)
if err != nil {
log.Fatal(err)
}
defer etcdClient.Close()
etcdResolver, err := resolver.NewBuilder(etcdClient)
conn, err := grpc.Dial("etcd://localhost:2379/"+"grpc-demo2/svc",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithResolvers(etcdResolver),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
grpc.WithBlock(),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
for i := 0; i < 10; i++ {
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: fmt.Sprintf("%s %d", *name, i)})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
}
测试
go run server/main.go --port 9001
go run server/main.go --port 9002
go run server/main.go --port 9003
go run client/main.go