配置 gRPC 服务

如需创建 gRPC 服务(无论您是否在使用 Cloud Endpoints),请在一个或多个 proto 文件(即扩展名为 .proto 的文本文件)中指定接口定义。您可以在 proto 文件中定义 API 的表面,包括数据结构、方法、方法参数以及返回类型。然后,您可以使用特定于语言的协议缓冲区编译器 protoc 编译 proto 文件。如需了解详情,请参阅 gRPC 简介gRPC 概念

如需让您的 gRPC 服务由 Endpoints 管理,除了已编译的 proto 文件,您还必须在一个或多个 YAML 文件中指定“服务配置”。服务配置是一种规范,可让您定义 gRPC 服务的行为,包括身份验证、配额等。

服务配置概述

在 YAML 文件的顶部,您可以指定有关服务的基本信息,例如其名称和标题。在 YAML 文件的子部分,您可以配置服务的其他方面,例如:

例如:

type: google.api.Service
config_version: 3
name: calendar.googleapis.com
title: Google Calendar API
apis:
- name: google.calendar.v3.Calendar
authentication:
  providers:
  - id: google_calendar_auth
    jwks_uri: https://www.googleapis.com/oauth2/v1/certs
    issuer: https://securetoken.google.com
  rules:
  - selector: "*"
    requirements:
      provider_id: google_calendar_auth
backend:
  rules:
    - selector: "*"
      address: grpcs://my-service-98sdf8sd0-uc.a.run.app

通常每个子部分对应于一个 .proto 消息,您可在其中配置服务的各个方面。例如:

  • authentication:指定如何对调用者进行身份验证。
  • backend:控制远程后端路由。
  • http:定义用于将 RPC 方法映射到一个或多个 HTTP REST API 方法的规则。
  • usage:可让您有选择性地启用和停用 API 密钥验证。

服务配置的官方架构由 .proto 消息 google.api.Service 定义。

基本配置

gRPC 教程中使用的 Bookstore 示例包含一个基本接口定义文件和一个服务配置文件。

Bookstore 接口定义bookstore.proto

syntax = "proto3";

package endpoints.examples.bookstore;

option java_multiple_files = true;
option java_outer_classname = "BookstoreProto";
option java_package = "com.google.endpoints.examples.bookstore";

import "google/protobuf/empty.proto";

service Bookstore {
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {}
  rpc CreateShelf(CreateShelfRequest) returns (Shelf) {}
  rpc GetShelf(GetShelfRequest) returns (Shelf) {}
  rpc DeleteShelf(DeleteShelfRequest) returns (google.protobuf.Empty) {}
  rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {}
  rpc CreateBook(CreateBookRequest) returns (Book) {}
  rpc GetBook(GetBookRequest) returns (Book) {}
  rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {}
}

message Shelf {
  int64 id = 1;
  string theme = 2;
}

message Book {
  int64 id = 1;
  string author = 2;
  string title = 3;
}

Bookstore 服务配置api_config.yaml

type: google.api.Service
config_version: 3

name: bookstore.endpoints.MY_PROJECT_ID.cloud.goog

title: Bookstore gRPC API
apis:
- name: endpoints.examples.bookstore.Bookstore

上述代码示例是最简单的服务配置,因为它:

  • 定义名为 bookstore.endpoints.MY_PROJECT_ID.cloud.goog 的服务,其中 MY_PROJECT_ID 表示您的 Google Cloud 项目 ID。
  • 指定该服务公开 bookstore.proto 文件中定义的 API endpoints.examples.bookstore.Bookstore
  • 部署配置后,提供在 Google Cloud Console 的 Endpoints > 服务页面上显示的描述性标题。如需详细了解注释信息,请参看每个文件的完整 GitHub 版本。

规则和选择器

在有些情况下,您可能需要将配置与服务的各个元素相关联,例如,对某些方法强制执行身份验证,而对其他方法不执行身份验证。如需在服务配置中对此进行配置,您可以通过 authenticationhttp 服务配置的某些部分来指定“规则”和“选择器”。选择器可标识与规则关联的配置适用的服务元素(例如方法名称)。

选择器是服务中定义的以逗号分隔的限定名称列表:方法、消息、字段、枚举或枚举值。名称的最后一部分可以是通配符 *(可与任何后缀匹配)。通配符仅可用在名称的末尾,并且只能用在完整的名称末尾。例如:foo.* 没问题,但 foo.b*foo.*.bar 则不行。如需为所有适用元素配置默认值,请单独指定 *

示例 1(影响整个服务):

usage:
  rules:
  # Allow unregistered calls for all methods.
  - selector: "*"
    allow_unregistered_calls: true

示例 2(影响单个方法):

usage:
  rules: # IMPORTANT: ONLY LAST MATCHING RULE IS APPLIED
  # Disable API key validation on just the ListShelves method
  # while requiring an API key for the rest of the API.
  - selector: "*"
    allow_unregistered_calls: false
  - selector: "endpoints.examples.bookstore.BookStore.ListShelves"
    allow_unregistered_calls: true

上面的示例展示如何要求对除了 ListShelves 方法之外的所有方法进行 API 密钥验证。

系统将按顺序评估规则,并会应用按声明顺序排列的最后一个匹配的规则。

使用多个 YAML 文件

您可能会发现,使用多个 YAML 文件为同一服务配置不同功能十分有用。使用多个 YAML 文件可让您更轻松地重用文件并针对不同的环境修改这些文件。例如,在 Bookstore 示例中,基本配置是在 api_config.yaml 文件中指定的,其 HTTP 规则是在 api_config_http.yaml 文件中指定的。这样一来,您可以仅在想要为 Bookstore 开启 JSON/HTTP 到 gRPC 转码时部署 HTTP 规则。

如果您使用多个 YAML 文件进行服务配置,则在您部署配置时,每个文件都将转换为 google.api.Service proto 消息,然后再通过 proto 合并语义合并所有消息。也就是说,后一实例中的所有奇异标量字段会替换前一实例中的字段。因此,如果您在两个文件中为同一规则提供不同的奇异值,则系统会使用您在部署配置时指定的第二个文件中的值。奇异嵌入式消息会合并,重复的字段也会合并。

与规则一样,合并与顺序有关。如果有两种服务配置,则第二种服务配置会替换第一种服务配置。

注释(仅限 HTTP 规则)

除了使用 YAML 文件配置 HTTP 选项外,您还可以使用选项机制直接在 proto 文件中配置这些选项。API 注释是在 annotations.proto 中定义的。

如果要将配置选项设计为在协议缓冲区接口定义的所有用法中都保持不变,请使用注释。例如,如果某个 API 只会使用一次,或者每次使用 API 时都需要具备相同的 HTTP 接口,那么您可以在 proto 文件中注释 HTTP 配置。

如果 proto 文件和服务配置 YAML 文件中都提供了配置选项,则服务配置会替换注释。

proto 文件中的示例注释:

rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
    option (google.api.http) = { get: "/v1/shelves" };
}

# The preceding annotation is equivalent to the following service configuration:

http:
  rules:
  - selector: endpoints.examples.bookstore.BookStore.ListShelves
    get: '/v1/shelves'

如需了解详情,请参阅将 HTTP/JSON 转码为 gRPC

配置验证

您可以使用 gcloud endpoints services deploy 部署您的服务配置和已编译的 proto 文件,以创建服务的运行时配置。gcloud endpoints services deploy 命令可验证服务配置并标记任何错误和警告。

警告分为常规警告和 linter 警告。Linter 警告使用 <group>-<rule> 形式的标识符,其中 <group> 表示配置组,而 <rule> 表示特定的 linter 规则。例如,组下方为 versioning,而规则为 http-version-prefix

WARNING: bookstore.proto:51:3: (lint) versioning-http-version-prefix: method
'endpoints.examples.bookstore.BookStore.ListShelves' has a different version
prefix in HTTP path ('v2') than api version 'v1'.

您可以通过将指令添加到 API 元素的文档来禁止 linter 警告。您还可以将指令添加到 proto 文件或服务配置 YAML 文件中。例如,以下指令会禁止上述警告:

service Bookstore {
  // Returns an object.
  // (== suppress_warning versioning-http-version-prefix ==)
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
      option (google.api.http) = { get: "/v1/shelves" };
  }
}

如需对整个组禁止警告,您可以使用通配符 (*),而不是规则名称。例如,versioning-* 会禁止与 versioning 组有关的所有警告。

附加到父元素的禁止指令也会应用于所有子元素。例如,以下示例展示了如何针对服务内的所有方法禁止所有 versioning 组警告:

// (== suppress_warning versioning-* ==)
service Bookstore {
  // Returns an object.
  rpc ListShelves(google.protobuf.Empty) returns (ListShelvesResponse) {
      option (google.api.http) = { get: "/v1/shelves" };
  }
}

如需全局禁止警告,请将其附加到服务配置的概览文档中:

type: google.api.Service
name: your_api.endpoints.<producer-project-id>.cloud.goog
title: Bookstore gRPC API
apis:
- name: endpoints.examples.bookstore.BookStore
documentation:
    overview: |
      A simple Bookstore gRPC API.
      (== suppress_warning documentation-presence ==)

请注意,suppress_warning 等文档中的指令必须独占一行,否则将无法识别。> 等 YAML 文字块标记会移除所有换行符,所以如果您在以上示例中使用了 overview: >,禁止指令将不起作用。如需详细了解 YAML 中的文字块,请参阅缩进分隔

后续步骤