gRPC로 호출

이 페이지에서는 gRPC를 사용하여 Cloud Run 서비스를 다른 서비스(예: 내부 마이크로서비스 간의 간단하면서 고성능의 통신 제공)와 연결하려는 개발자를 위한 Cloud Run 관련 세부정보를 보여줍니다. Cloud Run(완전 관리형)은 단항 gRPC 호출만 지원합니다. Cloud Run for Anthos on Google Cloud 단항 및 스트리밍 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의 경우 클라이언트와 서버가 독립적으로 작동하는 두 개의 읽기-쓰기 스트림에서 메시지를 보냅니다.

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

사용 가능한 사용 사례는 다음과 같습니다.

  • 내부 마이크로서비스 간의 통신
  • 대량의 데이터. 이 경우 gRPC는 REST 호출보다 최대 7배 빠른 프로토콜 버퍼를 사용합니다.
  • 간단한 서비스 정의만 필요한 경우에는 전체 클라이언트 라이브러리를 작성하지 않아도 됩니다.

서비스를 gRPC와 통합하려면 다음 안내를 따르세요.

  • proto 파일에서 요청 메시지와 응답을 정의하고 컴파일합니다.
  • 요청을 처리하고 응답을 반환하는 gRPC 서버를 만듭니다. 이 서버는 PORT 환경 변수를 리슨해야 합니다.
  • gRPC 서버에서 요청을 보내고 응답을 처리하는 클라이언트를 만듭니다.
  • 원할 경우 인증을 추가합니다.
  • 서비스를 빌드하고 배포합니다.

proto 파일에서 메시지 정의 및 컴파일

proto 정의에 추가할 추가 또는 Cloud Run 관련 항목이 없습니다. 다른 용도로 gRPC를 사용할 때와 마찬가지로 서비스 정의 및 데이터 직렬화에는 gRPC 프로토콜 버퍼를 사용합니다.

gRPC 클라이언트 만들기

gRPC를 사용하는 클라이언트에 추가할 추가 또는 Cloud Run 관련 항목이 없습니다. 클라이언트 코드의 서비스 정의 사용에 관한 gRPC 문서나 언어별 gRPC 가이드에 제공된 샘플 클라이언트를 따르세요.

Cloud Run 서비스에서 gRPC 요청 리슨

Cloud Run에서 실행 중인 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 메시지를 보내려면 Cloud Run 서비스의 URL 또는 해당 서비스에 매핑된 커스텀 도메인인 호스트 도메인을 gRPC에서 사용할 것으로 예상되는 포트 443과 함께 지정해야 합니다.

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 {
		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)
}

인증을 사용하여 gRPC 요청 전송(Cloud Run(완전 관리형)만 해당)

다음 샘플은 호출 서비스에 수신 서비스에 대한 호출자 권한이 있는 경우 서비스 간 인증을 사용하는 방법을 보여줍니다. 이 코드는 적절한 ID 토큰을 가진 승인 헤더를 생성합니다. 이 헤더는 필수입니다. 필수 권한 및 승인 헤더는 서비스 간 인증에 자세히 설명되어 있습니다.

Go


import (
	"context"
	"fmt"
	"time"

	"google.golang.org/api/idtoken"
	"google.golang.org/grpc"
	grpcMetadata "google.golang.org/grpc/metadata"

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

// pingRequestWithAuth mints a new Identity Token for each request.
// This token has a 1 hour expiry and should be reused.
// audience must be the auto-assigned URL of a Cloud Run service or HTTP Cloud Function without port number.
func pingRequestWithAuth(conn *grpc.ClientConn, p *pb.Request, audience string) (*pb.Response, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Create an identity token.
	// With a global TokenSource tokens would be reused and auto-refreshed at need.
	// A given TokenSource is specific to the audience.
	tokenSource, err := idtoken.NewTokenSource(ctx, audience)
	if err != nil {
		return nil, fmt.Errorf("idtoken.NewTokenSource: %v", err)
	}
	token, err := tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %v", err)
	}

	// Add token to gRPC Request.
	ctx = grpcMetadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token.AccessToken)

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