gRPCの名前解決と検出
go-grpc:etcdをバックエンドとしてgRPCエンドポイントを解決する
etcdは、etcdからエンドポイントを取得してgRPCサービスを検出する代替の名前システムを、gRPCリゾルバーで提供します。その仕組みは、サービス名を接頭辞に持つキーの更新の監視に基づいています。
この機能は、grpc-goでまだ実験的なgoogle.golang.org/grpc/resolver
パッケージに依存しているため、実験的機能です。
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"})
エンドポイントのアトミックな更新
1つのトランザクションで複数のエンドポイントを変更するには、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"})})