gRPC 命名与发现
go-grpc:使用 etcd 后端解析 gRPC 端点
etcd 提供了一个 gRPC 解析器,用于支持一种替代名称系统,该系统从 etcd 获取端点以发现 gRPC 服务。其底层机制基于监听以服务名称为前缀的键的更新。
请注意,此功能为实验性功能,因为它依赖于 google.golang.org/grpc/resolver
包,而该包在 grpc-go 中仍处于实验阶段。
使用 go-grpc 实现 etcd 发现
etcd 客户端为使用 etcd 后端解析 gRPC 端点提供了 gRPC 解析器。该解析器通过一个 etcd 客户端进行初始化:
import (
clientv3 "go.etcd.io/etcd/client/v3"
etcdnaming "go.etcd.io/etcd/client/v3/naming/resolver"
"google.golang.org/grpc"
)
...
cli, err := clientv3.NewFromURL("http://localhost:2379")
if err != nil {
// ...
}
r, err := etcdnaming.NewBuilder(cli)
if err != nil {
// ...
}
conn, gerr := grpc.NewClient("my-service", grpc.WithResolvers(r), ...)
管理服务端点
etcd 解析器将解析目标前缀下所有以 “/” 分隔的键(例如 “foo/bar/my-service/")视为潜在服务端点,这些键对应的值需为 JSON 编码格式(历史版本为 go-grpc naming.Update)。通过创建新键将端点添加至服务,通过删除键将端点从服务中移除。
添加端点
可通过 etcdctl 向服务添加新的端点:
ETCDCTL_API=3 etcdctl put foo/bar/my-service/1.2.3.4 '{"Addr":"1.2.3.4"}'
etcd 客户端的 endpoints.Manager 方法还可注册新的端点,其键与 Addr 匹配:
em := endpoints.NewManager(client, "foo/bar/my-service")
err := em.AddEndpoint(context.TODO(),"foo/bar/my-service/e1", endpoints.Endpoint{Addr:"1.2.3.4"})
当通过多个端点连接服务时,若要启用轮询负载均衡,可使用 gRPC 内置的轮询负载均衡器配置连接:
conn, gerr := grpc.NewClient("etcd:///foo", grpc.WithResolvers(etcdResolver),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`))
删除端点
可通过 etcdctl 从服务中删除主机:
ETCDCTL_API=3 etcdctl del foo/bar/my-service/1.2.3.4
etcd 客户端的 endpoints.Manager 方法还支持删除端点:
em := endpoints.NewManager(client, "foo/bar/my-service")
err := em.DeleteEndpoint(context.TODO(), "foo/bar/my-service/e1")
使用租约注册端点
使用租约注册端点可确保,若主机无法维持保活心跳(例如其所在机器发生故障),该端点将从服务中移除:
lease=`ETCDCTL_API=3 etcdctl lease grant 5 | cut -f2 -d' '`
ETCDCTL_API=3 etcdctl put --lease=$lease my-service/1.2.3.4 '{"Addr":"1.2.3.4"}'
ETCDCTL_API=3 etcdctl lease keep-alive $lease
在 Go 语言中:
em := endpoints.NewManager(client, "foo/bar/my-service")
err := em.AddEndpoint(context.TODO(), "foo/bar/my-service/e1", endpoints.Endpoint{Addr:"1.2.3.4"})
原子性更新端点
若需在单个事务中修改多个端点,可直接使用 endpoints.Manager:
em := endpoints.NewManager(c, "foo")
err := em.Update(context.TODO(), []*endpoints.UpdateWithOpts{
endpoints.NewDeleteUpdateOpts("foo/bar/my-service/e1", endpoints.Endpoint{Addr: "1.2.3.4"}),
endpoints.NewAddUpdateOpts("foo/bar/my-service/e1", endpoints.Endpoint{Addr: "1.2.3.14"})})