emahiro/b.log

日々の勉強の記録とか育児の記録とか。

gRPC の service config でリトライの設定をする

Overview

proto から gRPC クライアントを生成して、サービスを呼び出すときにリトライ設定を入れたいケースで service config を使った話です。

gRPC の service config とは

grpc.io

サービスを呼び出す時に必要になる timeout や retry の設定を gRPC のコネクションレベルで設定できます。

設定は https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto#L44 を見ると分かりやすいです。

実装としては以下のようになります。

service config 側

// ref: https://github.com/grpc/grpc-proto/blob/master/grpc/service_config/service_config.proto
type ServiceConfig struct {
    MethodConfig []MethodConfig `json:"methodConfig"`
}

// MethodConfig はメソッドごとの設定です。
type MethodConfig struct {
    Name        []MethodName `json:"name"`
    RetryPolicy *RetryPolicy `json:"retryPolicy,omitempty"`
}

// MethodName は対象となるサービス/メソッドを指定します。
type MethodName struct {
    Service string `json:"service"`
    Method  string `json:"method,omitempty"`
}

// RetryPolicy はリトライポリシーの設定です。
type RetryPolicy struct {
    MaxAttempts          int      `json:"maxAttempts"`
    InitialBackoff       string   `json:"initialBackoff"`
    MaxBackoff           string   `json:"maxBackoff"`
    BackoffMultiplier    float64  `json:"backoffMultiplier"`
    RetryableStatusCodes []string `json:"retryableStatusCodes"`
}

// DefaultRetryableStatusCodes は一般的にリトライ可能なステータスコードです。
var DefaultRetryableStatusCodes = []string{
    "UNAVAILABLE",
    "RESOURCE_EXHAUSTED",
    "DEADLINE_EXCEEDED",
    "ABORTED",
    "INTERNAL",
    "UNKNOWN",
}

// WithRetryPolicy は ServiceConfig から grpc.DialOption を生成します。
func WithRetryPolicy(cfg ServiceConfig) grpc.DialOption {
    b, _ := json.Marshal(cfg)
    return grpc.WithDefaultServiceConfig(string(b))
}

grpc.DialOption を connection にアタッチすることで retry 設定をサービスに入れることができます。

conn = libsgrpc.NewClient(addr, "serviceName",
    libsgrpc.WithRetryPolicy(libsgrpc.ServiceConfig{
        MethodConfig: []libsgrpc.MethodConfig{
            {
                Name: []libsgrpc.MethodName{
                    {Service: "サービス名は完全修飾名にする (後述)"},
                },
                RetryPolicy: &libsgrpc.RetryPolicy{
                    // ここに設定する
                },
            },
        },
    }),
)

ハマったところ

指定するサービス名は proto の完全修飾名にする

これがハマりました。ServiceConfig を設定する対象のサービスを指定するときは proto の完全修飾名 にする必要があります。

最初リトライ設定しても動かず、テストコードを確認すると proto に定義されたサービスの設定の仕方がわかりました。

https://github.com/grpc/grpc-proto/blob/master/grpc/testing/test.proto#L24-L30 で定義されたサービスへの Service Config の設定は https://github.com/grpc/grpc-go/blob/master/test/retry_test.go#L64-L74 のように grpc.testing.TestService になります。

まとめ

リトライ処理自体は自作することもできますが、gRPC を使っているなら gRPC が用意してる機能に乗っかるのもいいなと思いました。