gRPC 命名与发现

go-grpc:用于通过 etcd 后端解析 gRPC 端点

etcd 提供了一个 gRPC 解析器,用于支持一种替代的命名系统,该系统从 etcd 中获取端点以发现 gRPC 服务。其底层机制基于监听以服务名称为前缀的键的更新。

请注意,此功能尚处于实验阶段,因为它依赖于 google.golang.org/grpc/resolver 包,而该包在 grpc-go 中仍处于实验阶段。

使用 etcd 发现功能与 go-grpc 配合

etcd 客户端提供了一个 gRPC 解析器,用于通过 etcd 后端解析 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"})})

最后更新于 2025 年 6 月 3 日:递归地将 v3.6 的内容复制到 v3.7(a90b2a6)