gRPC での呼び出し

このページでは、gRPC を使用して Knative serving サービスを他のサービスと接続する(たとえば、内部のマイクロサービス間でシンプルかつ高パフォーマンスの通信を行う場合など)デベロッパーを対象として、Knative serving 固有の情報について詳しく説明します。Knative serving は、単項ストリーミングの両方の gRPC 呼び出しをサポートしています。

gRPC 単項

単項 RPC 呼び出しでは、クライアントは単一のリクエストをサーバーに送信し、通常の関数呼び出しと同様に単一のレスポンスを受信します。
rpc SayHello(HelloRequest) returns (HelloResponse);

gRPC ストリーミング

gRPC では次のストリーミング オプションを使用できます。サーバー ストリーミング RPC では、クライアントがサーバーにリクエストを送信し、一連のメッセージを含むストリームを受信して読み取ります。クライアントは、受信したストリームをメッセージがなくなるまで読み取ります。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

クライアント ストリーミング RPC では、クライアントは、一連のメッセージを書き込み、ストリームでサーバーに送信します。クライアントは、メッセージの書き込みを終了すると、サーバーがレスポンスを返すのを待ちます。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

双方向ストリーミング RPC では、クライアントとサーバーが独立して動作する 2 つの読み取り / 書き込みストリームでメッセージを送信します。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

考えられるユースケースには、次のようなものがあります。

  • 内部マイクロサービス間の通信。
  • 高負荷のデータ(gRPC では、プロトコル バッファを使用します。これは REST 呼び出しの最大 7 倍の速度です)。
  • 単純なサービス定義だけが必要で、完全なクライアント ライブラリは作成しないケース。

サービスを gRPC と統合するには:

  • リクエスト メッセージとレスポンスを proto ファイルに定義し、コンパイルします。
  • リクエストを処理してレスポンスを返す gRPC サーバーを作成します。このサーバーは PORT 環境変数をリッスンする必要があります。
  • リクエストを送信し、gRPC サーバーからのレスポンスを処理するクライアントを作成します。
  • 必要に応じて、認証を追加します。
  • サービスをビルドしてデプロイします。

proto ファイルでメッセージを定義してコンパイルする

proto 定義に追加するその他のものや Knative serving 固有のものはありません。gRPC の他の場合での使用と同様に、サービス定義とデータのシリアル化には gRPC プロトコル バッファを使用します。

gRPC クライアントを作成する

gRPC を使用するクライアントに追加するその他のものや Knative serving 固有のものはありません。クライアント コードのサービス定義と、言語別の gRPC チュートリアルで提示されるサンプル クライアントの使用については、gRPC ドキュメントを参考にしてください。

Knative serving サービスで gRPC リクエストをリッスンする

Knative serving で実行される gRPC サーバーの唯一の特別な要件は、コードに示されているように PORT 環境変数で指定されたポートをリッスンすることです。

Go

func main() {
	log.Printf("grpc-ping: starting server...")

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	listener, err := net.Listen("tcp", ":"+port)
	if err != nil {
		log.Fatalf("net.Listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	pb.RegisterPingServiceServer(grpcServer, &pingService{})
	if err = grpcServer.Serve(listener); err != nil {
		log.Fatal(err)
	}
}

サービスへの gRPC 接続を開く

gRPC メッセージを送信できるようにサービスへの gRPC 接続を開くには、ホストドメイン(Knative serving サービスの URL、またはサービスにマッピングされたカスタム ドメイン)を、ポート 443(gRPC で使用されると予想されるポート)とともに指定する必要があります。

Go


import (
	"crypto/tls"
	"crypto/x509"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// NewConn creates a new gRPC connection.
// host should be of the form domain:port, e.g., example.com:443
func NewConn(host string, insecure bool) (*grpc.ClientConn, error) {
	var opts []grpc.DialOption
	if host != "" {
		opts = append(opts, grpc.WithAuthority(host))
	}

	if insecure {
		opts = append(opts, grpc.WithInsecure())
	} else {
		// Note: On the Windows platform, use of x509.SystemCertPool() requires
		// go version 1.18 or higher.
		systemRoots, err := x509.SystemCertPool()
		if err != nil {
			return nil, err
		}
		cred := credentials.NewTLS(&tls.Config{
			RootCAs: systemRoots,
		})
		opts = append(opts, grpc.WithTransportCredentials(cred))
	}

	return grpc.Dial(host, opts...)
}

認証なしで gRPC リクエストを送信する

次のサンプルは、前述のように構成された gRPC 接続を使用して、認証なしでリクエストを送信する方法を示しています。

Go


import (
	"context"
	"time"

	pb "github.com/GoogleCloudPlatform/golang-samples/run/grpc-ping/pkg/api/v1"
	"google.golang.org/grpc"
)

// pingRequest sends a new gRPC ping request to the server configured in the connection.
func pingRequest(conn *grpc.ClientConn, p *pb.Request) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	client := pb.NewPingServiceClient(conn)
	return client.Send(ctx, p)
}