使用 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,其中客户端和服务器在两个独立运行的读写数据流中发送消息。

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

可能的使用场景包括:

  • 内部微服务之间的通信。
  • 高数据负载(gRPC 使用协议缓冲区,其速度最高可比 REST 调用快七倍)。
  • 您只需要一个简单的服务定义,不需要编写完整的客户端库。

要将您的服务与 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 服务的网址或映射到该服务的自定义网域),以及端口 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)
}