使用 gRPC

本页面向希望使用 gRPC 将 Cloud Run 服务与其他服务连接起来(例如在内部微服务之间提供简单的高性能通信)的开发者介绍了特定于 Cloud Run 的详细信息。您可以将所有 gRPC 类型(流式传输或一元)与 Cloud Run 搭配使用。

可能的使用场景包括:

  • 内部微服务之间的通信。
  • 高数据负载(gRPC 使用协议缓冲区,其速度最高可比 REST 调用快七倍)。
  • 您只需要一个简单的服务定义,不需要编写完整的客户端库。
  • 在 gRPC 服务器中使用流式传输 gRPC 来构建响应更快的应用和 API。

要将您的服务与 gRPC 集成,请执行以下操作:

  • 如果使用流式传输 gRPC,将服务配置为使用 HTTP/2。 HTTP/2 是 gRPC 流式传输的传输方法。
  • 在 proto 文件中定义请求消息和响应,并对其进行编译。
  • 创建一台 gRPC 服务器以处理请求并返回响应:它应该侦听 PORT 环境变量。
  • 创建一个客户端,用于发送请求并处理来自 gRPC 服务器的响应。
  • (可选)添加身份验证。
  • 构建和部署服务。

配置服务以使用 HTTP/2

如果您使用的是流式传输 gRPC,请配置服务以使用 HTTP/2

在 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 服务的网址或映射到该服务的自定义网域),以及端口 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 {
		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 请求

以下示例展示了如何在服务之间使用身份验证(如果调用服务具有对接收服务的调用方权限)。请注意,此代码会创建一个具有正确身份令牌的授权标头:这是必需的。服务到服务的身份验证中详细介绍了所需的权限和授权标头。

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

gRPC 流式传输的示例代码

如需查看示例代码,请参阅 gRPC 基础教程中您所选的语言的 RouteGuide 实现。如需查看 Go 的示例,请参阅实现 RouteGuide