配置使用无代理 gRPC 服务的高级流量管理

本文档介绍如何使用以下流量管理功能来配置 Traffic Director:

  • 路由匹配
  • 流量拆分
  • 熔断
  • 故障注入
  • 流时长上限
  • 重试
  • 会话亲和性
  • 离群值检测
  • 局部负载均衡

虽然本文档重点介绍在 Compute Engine 上使用无代理 gRPC 设置高级流量管理,但在 Google Kubernetes Engine (GKE) 上使用无代理 gRPC 时也支持高级流量管理。

准备工作

在配置高级流量管理之前,请查看准备使用无代理 gRPC 服务设置 Traffic Director 中的要求。必须满足所有要求才能配置高级流量管理。

如需了解有关这些功能的概念信息,请参阅高级流量管理

关于 gRPC Wallet 示例

为了说明这些功能,您将部署一个 gRPC Wallet 示例。在此示例中,有三个 gRPC 服务(grpc.examples.wallet.Walletgrpc.examples.wallet.stats.Statsgrpc.examples.wallet.account.Account)部署为三个单独的应用。

如下图所示,您将创建一个 gRPC 客户端,该客户端调用 Wallet 服务来获取账号余额,并调用 Stats 服务来获取货币的价格。Wallet 服务会调用 StatsAccount 服务来计算余额。Stats 服务还会调用 Account 服务来获取用户信息。

gRPC Wallet 流量路由配置示例。
gRPC Wallet 流量路由配置示例(点击可放大)

在此示例中,您将部署两个版本的 WalletStats 实现,以演示基于您配置的规则的请求路由。为了模拟服务的不同版本的构建和部署,您将使用服务器标志来更改仅构建一次的二进制文件的行为。

  • --port 标志指定虚拟机实例上的服务侦听的端口。
  • --hostname_suffix 标志指定附加到响应请求的虚拟机实例主机名的值。结果值将作为 hostname 元数据添加到响应中。这有助于您确定实例组中的哪个实例响应客户端请求。
  • 值为 true--premium_only 标志指定该服务是 stats 服务的高级版本。
  • 值为 true--v1_behavior 标志指定 Wallet 二进制文件充当 v1 版本。

下表展示了运行其中一个 gRPC 服务的每个虚拟机实例的这些标志的值、实例组中的实例数量以及这些实例组所属的后端服务。

后端服务 实例组 实例 服务器标志
account account 2 --port=50053
--hostname_suffix="account"
stats stats 2 --port=50052
--hostname_suffix="stats"
--account_server="xds:///account.grpcwallet.io"
stats-premium stats-premium 2 --port=50052
--hostname_suffix="stats_premium"
--account_server="xds:///account.grpcwallet.io"
--premium_only=true
wallet-v1
wallet-v1-affinity
wallet-v1 2 --port=50051
--hostname_suffix="wallet_v1"
--v1_behavior=true
--account_server="xds:///account.grpcwallet.io"
--stats_server="xds:///stats.grpcwallet.io"
wallet-v2 wallet-v2 1 --port=50051
--hostname_suffix "wallet_v2"
--account_server="xds:///account.grpcwallet.io"
--stats_server="xds:///stats.grpcwallet.io"

部署这些服务后,您可以配置 Traffic Director 以根据下表中的路由规则将请求从测试客户端路由到这些后端服务。客户端会连接到服务的虚拟主机名,如“主机”列中所示。

主机 匹配规则 路由操作
wallet.grpcwallet.io 路径前缀:"/"
标头存在:"session_id"
路由到 wallet-v1-affinity
路径前缀:"/"
标头:{"route": "timeout"}
设置 5 秒超时并
路由到 wallet-v2
路径前缀:"/"
标头:{"route": "fault"}
让 50% 的请求失败并
将其余的路由到 wallet-v2
路径前缀:"/"
标头:{"membership": "premium"}
路由到 wallet-v1
在失败时最多重试 3 次
完整路径:/grpc.examples.wallet.Wallet/FetchBalance 路由到:
wallet-v1:70%
wallet-v2:30%
默认 路由到 wallet-v1
stats.grpcwallet.io 路径前缀:"/"
标头:{"membership": "premium"}
路由到 stats-premium
默认 路由到 stats
account.grpcwallet.io 路径前缀:"/"
标头:{"route": "account-fault"}
让 30% 的请求失败并
将其的路由到 account
默认 account

此示例在两个现有服务之间使用 70/30 的流量分配比例。如果您要将流量拆分到网址映射之前未引用的新服务,请先将新服务添加到 weightedBackendServices 并赋予 0 权重。然后,逐步增加分配给该服务的权重。

测试客户端提供了以下选项,可让您生成相应的请求来演示流量管理功能。

选项 说明
--watch=true 调用流式传输方法以查看余额/价格
--unary_watch=true 反复调用一元方法以查看余额/价格
--user=Alice 注入标头 {"membership": "premium"}
--user=Bob 注入标头 {"membership": "normal"}
--route=value 注入标头 {"route": "value"}
--affinity=true 注入标头 {"session_id": "value"}

准备本地环境

如需为这些示例设置本地环境,请运行以下命令:

  1. 更新 gcloud 二进制文件,确保您拥有最新版本:

    gcloud components update
    
  2. 下载示例代码库:

    sudo apt-get install git -y
    
  3. 克隆示例的正确代码库:

    export EXAMPLES_VERSION=v1.1.x
    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 \
      https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    

创建和配置 Cloud Router 路由器实例

在本部分中,您将在每个区域中创建 Cloud Router 实例,并为 Cloud NAT 配置这些实例。此示例创建的虚拟机没有外部 IP 地址,但它们需要访问互联网。使用 Cloud NAT 配置 Cloud Router 可提供所需的访问权限。

gcloud

  1. 创建 Cloud Router 实例:

    gcloud compute routers create nat-router-us-central1 \
        --network=default \
        --region=us-central1
    
  2. 为 Cloud NAT 配置路由器:

    gcloud compute routers nats create nat-config \
        --router-region=us-central1 \
        --router=nat-router-us-central1 \
        --nat-all-subnet-ip-ranges \
        --auto-allocate-nat-external-ips
    

创建 gRPC 健康检查和防火墙规则

在本部分中,您将创建 gRPC 健康检查和防火墙规则,以允许 gRPC 健康检查请求到达您的网络。稍后,gRPC 健康检查会与后端服务相关联,似的它能够检查这些后端服务的后端实例的健康状况。

gcloud

  1. 创建健康检查:

    gcloud compute health-checks create grpc grpcwallet-health-check \
        --use-serving-port
    
  2. 为健康检查创建防火墙规则:

    gcloud compute firewall-rules create grpcwallet-allow-health-checks \
        --network default --action allow --direction INGRESS \
        --source-ranges 35.191.0.0/16,130.211.0.0/22 \
        --target-tags allow-health-checks \
        --rules tcp:50051-50053
    

创建实例模板

在本部分中,您将创建一个实例模板,以部署在端口 50053 上公开的 account gRPC 服务。

gcloud

  • 创建实例模板:

    gcloud compute instance-templates create grpcwallet-account-template \
       --scopes=https://www.googleapis.com/auth/cloud-platform \
       --tags=allow-health-checks \
       --network-interface=no-address \
       --image-family=debian-10 \
       --image-project=debian-cloud \
       --metadata-from-file=startup-script=<(echo "#! /bin/bash
    set -ex
    cd /root
    export HOME=/root
    sudo apt-get update -y
    pushd \$(mktemp -d)
    sudo apt-get install -y wget git
    wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz
    sudo tar -C /usr/local -xvf go1.16.5.linux-amd64.tar.gz
    sudo cp /usr/local/go/bin/go /usr/bin/go
    popd
    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    cd traffic-director-grpc-examples/go/account_server/
    go build .
    sudo systemd-run ./account_server --port 50053 --hostname_suffix account")
    

创建代管式实例组

代管式实例组 (MIG) 根据需要使用自动扩缩功能创建新的虚拟机实例。在本部分中,您将使用在上一部分中创建的实例模板创建 MIG。

gcloud

  • 创建实例组:

    gcloud compute instance-groups managed create grpcwallet-account-mig-us-central1 \
       --zone=us-central1-a \
       --size=2 \
       --template=grpcwallet-account-template
    

配置已命名端口

在本部分中,您将为 gRPC 服务配置已命名端口。已命名端口是 gRPC 服务在其中侦听请求的端口。在此示例中,已命名端口是端口 50053

gcloud

  • 创建已命名端口:

    gcloud compute instance-groups set-named-ports grpcwallet-account-mig-us-central1 \
       --named-ports=grpcwallet-account-port:50053 \
       --zone=us-central1-a
    

创建后端服务

在本部分中,您将创建具有 INTERNAL_SELF_MANAGED 和协议 GRPC 的负载平衡方案的全局后端服务。然后,您需要将健康检查和实例组与后端服务相关联。在此示例中,您将使用在创建代管实例组中创建的 MIG。此 MIG 运行 account gRPC 服务。--port-name 标志中的端口是您在配置已命名端口中创建的已命名端口。

gcloud

  1. 创建后端服务并将健康检查与新的后端服务相关联。

    gcloud compute backend-services create grpcwallet-account-service \
        --global \
        --load-balancing-scheme=INTERNAL_SELF_MANAGED \
        --protocol=GRPC \
        --port-name=grpcwallet-account-port \
        --health-checks=grpcwallet-health-check
    
  2. 将代管实例组添加为后端:

    gcloud compute backend-services add-backend grpcwallet-account-service \
        --instance-group=grpcwallet-account-mig-us-central1 \
        --instance-group-zone=us-central1-a \
        --global
    

创建 gRPC Wallet 示例中使用的其余后端服务的步骤与上述步骤类似。您可以通过运行 Shell 脚本来创建其余服务。该脚本会部署以下后端服务:

  • stats
  • stats-premium
  • wallet-v1
  • wallet-v1-affinity
  • wallet-v2

运行创建额外后端服务的 Shell 脚本:

traffic-director-grpc-examples/scripts/create_service.sh go stats 50052 stats '--account_server="xds:///account.grpcwallet.io"'

traffic-director-grpc-examples/scripts/create_service.sh go stats 50052 stats-premium '--account_server="xds:///account.grpcwallet.io" --premium_only=true'

# This command creates wallet-v1 and wallet-v1-affinity backend services.
traffic-director-grpc-examples/scripts/create_service.sh java wallet 50051 wallet-v1 '--account_server="xds:///account.grpcwallet.io" --stats_server="xds:///stats.grpcwallet.io" --v1_behavior=true'

traffic-director-grpc-examples/scripts/create_service.sh java wallet 50051 wallet-v2 '--account_server="xds:///account.grpcwallet.io" --stats_server="xds:///stats.grpcwallet.io"'

创建路由规则

在本部分中,您将创建一个网址映射,用于演示各种流量管理功能。网址映射指定此示例中服务的虚拟主机名以及关联的路由规则。如需了解详情,请参阅路由规则映射

在网址映射中,hostRules 指定示例中服务的虚拟主机名。这些是客户端在通道 URI 中用于连接到特定服务的名称。例如,为了向 account 服务发送请求,客户端会在通道 URI 中使用 xds:///account.grpcwallet.io。在 hostRules 中,配置值为 account.grpcwallet.iohosts 条目。

hosts 条目关联的 pathMatcher 指定包含该虚拟主机的所有路由规则的 pathMatcher 的名称。pathMatcher 配置由匹配规则和相应的操作规则组成,如关于 gRPC Wallet 示例中所述。

gcloud

创建网址映射:

export PROJECT_ID=$(gcloud config list --format 'value(core.project)')
export BS_PREFIX=projects/$PROJECT_ID/global/backendServices/grpcwallet

gcloud compute url-maps import grpcwallet-url-map << EOF
name: grpcwallet-url-map
defaultService: $BS_PREFIX-account-service

hostRules:
- hosts:
  - account.grpcwallet.io
  pathMatcher: grpcwallet-account-path-matcher
- hosts:
  - stats.grpcwallet.io
  pathMatcher: grpcwallet-stats-path-matcher
- hosts:
  - wallet.grpcwallet.io
  pathMatcher: grpcwallet-wallet-path-matcher

pathMatchers:
- name: grpcwallet-account-path-matcher
  defaultService: $BS_PREFIX-account-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: account-fault
    priority: 0
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-account-service
        weight: 100
      faultInjectionPolicy:
        abort:
          httpStatus: 503
          percentage: 30

- name: grpcwallet-stats-path-matcher
  defaultService: $BS_PREFIX-stats-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: membership
        exactMatch: premium
    priority: 0
    service: $BS_PREFIX-stats-premium-service

- name: grpcwallet-wallet-path-matcher
  defaultService: $BS_PREFIX-wallet-v1-service
  routeRules:
  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: session_id
        presentMatch: true
    priority: 0
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-affinity-service
        weight: 100

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: timeout
    priority: 1
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
      maxStreamDuration:
        seconds: 5

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: route
        exactMatch: fault
    priority: 2
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
      faultInjectionPolicy:
        abort:
          httpStatus: 503
          percentage: 50

  - matchRules:
    - prefixMatch: /
      headerMatches:
      - headerName: membership
        exactMatch: premium
    priority: 3
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-service
        weight: 100
      retryPolicy:
        retryConditions:
          - unavailable
        numRetries: 3

  - matchRules:
    - fullPathMatch: /grpc.examples.wallet.Wallet/FetchBalance
    priority: 4
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v1-service
        weight: 70
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 30

  - matchRules:
    - prefixMatch: /grpc.examples.wallet.Wallet/
    priority: 5
    routeAction:
      weightedBackendServices:
      - backendService: $BS_PREFIX-wallet-v2-service
        weight: 100
EOF

创建目标代理和转发规则

在本部分中,您将创建目标 gRPC 代理和转发规则。

目标 gRPC 代理会引用您在上一步中创建的网址映射。--validate-for-proxyless 标志会启用配置检查,这样您就不会在无意中启用与无代理 gRPC 部署不兼容的功能。

gcloud

  • 创建目标 gRPC 代理:

    gcloud compute target-grpc-proxies create grpcwallet-proxy \
       --url-map=grpcwallet-url-map \
       --validate-for-proxyless
    

转发规则会引用您创建的目标 gRPC 代理。负载平衡方案设置为 INTERNAL_SELF_MANAGED,以指示此转发规则会由 Traffic Director 使用。此规则必须是全局转发规则。IP 地址设置为 0.0.0.0,因为无代理 gRPC 客户端通过向 Traffic Director 发送 LDS 请求(而不是执行 DNS 查找)来解析目标 URI 中的 hostname:port。如需了解详情,请参阅名称解析方案

如果目标 URI 中未指定端口,则默认值为 80。例如,目标 URI xds:///foo.myservice:8080 与使用端口 8080 配置的转发规则匹配。在此示例中,转发规则是使用端口 80 进行配置的。

gcloud

  • 创建转发规则:

    gcloud compute forwarding-rules create grpcwallet-forwarding-rule \
       --global \
       --load-balancing-scheme=INTERNAL_SELF_MANAGED \
       --address=0.0.0.0 \
       --address-region=us-central1 \
       --target-grpc-proxy=grpcwallet-proxy \
       --ports=80 \
       --network=default
    

验证配置

配置过程完成后,请通过检查 Google Cloud Console 中的 Traffic Director 页面来验证您配置的后端服务是否可用。请确认后端服务和关联的后端报告为运行状况良好。 这可能需要几分钟。

验证路由配置

在本部分中,您将验证路由配置是否正常工作。您可以使用 grpcurl 工具测试配置。

gcloud

  1. 创建客户端虚拟机,用于运行客户端以测试服务。 您可以选择性添加 --network-interface=no-address 标志。

    gcloud compute instances create grpc-wallet-client \
        --zone=us-central1-a \
        --scopes=https://www.googleapis.com/auth/cloud-platform \
        --image-family=debian-10 \
        --image-project=debian-cloud \
        --metadata-from-file=startup-script=<(echo '#! /bin/bash
    set -e
    export GRPC_XDS_BOOTSTRAP=/run/td-grpc-bootstrap.json
    # Expose bootstrap variable to SSH connections
    echo export GRPC_XDS_BOOTSTRAP=$GRPC_XDS_BOOTSTRAP | sudo tee /etc/profile.d/grpc-xds-bootstrap.sh
    # Create the bootstrap file
    curl -L https://storage.googleapis.com/traffic-director/td-grpc-bootstrap-0.11.0.tar.gz | tar -xz
    ./td-grpc-bootstrap-0.11.0/td-grpc-bootstrap | sudo tee $GRPC_XDS_BOOTSTRAP')
    
  2. 使用 SSH 访问虚拟机,然后运行以下命令来准备虚拟机:

    export EXAMPLES_VERSION=v1.1.x
    sudo apt-get update
    sudo apt-get install git -y
    
  3. 运行以下命令:

    git clone -b $EXAMPLES_VERSION --single-branch --depth=1 \
       https://github.com/GoogleCloudPlatform/traffic-director-grpc-examples.git
    curl -L https://github.com/fullstorydev/grpcurl/releases/download/v1.6.1/grpcurl_1.6.1_linux_x86_64.tar.gz | tar -xz
    chmod +x grpcurl
    

为了在没有边车代理的情况下访问服务,gRPC 客户端需要使用 xds 名称解析方案。此方案指示客户端所用的 gRPC 库使用 xDS 服务器来解析主机名。为此,您需要用到引导配置。

上一部分中的启动脚本会设置 GRPC_XDS_BOOTSTRAP 环境变量,并使用帮助程序脚本生成引导文件。所生成引导文件中 TRAFFICDIRECTOR_GCP_PROJECT_NUMBERTRAFFICDIRECTOR_NETWORK_NAME 和 zone 的值将从知道 Compute Engine 虚拟机实例这些详细信息的元数据服务器获取。您可以使用 -gcp-project-number-vpc-network-name 选项手动将这些值提供给帮助程序脚本。

使用 grpcurl 工具验证配置

在 SSH shell 中运行以下命令,验证 wallet-servicestats-serviceaccount-service 正在运行。

./grpcurl -plaintext xds:///account.grpcwallet.io list
./grpcurl -plaintext -d '{"token": "2bd806c9"}' xds:///account.grpcwallet.io grpc.examples.wallet.account.Account/GetUserInfo
./grpcurl -plaintext -H 'authorization:2bd806c9' -H 'membership:premium' xds:///stats.grpcwallet.io grpc.examples.wallet.stats.Stats/FetchPrice
./grpcurl -plaintext -H 'authorization:2bd806c9' -H 'membership:premium' -d '{"include_balance_per_address": true}' xds:///wallet.grpcwallet.io grpc.examples.wallet.Wallet/FetchBalance

您会看到以下结果:

grpc.examples.wallet.account.Account
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection

{
  "name": "Alice",
  "membership": "PREMIUM"
}

{
  "price": "10295"
}

{
  "balance": "5089953"
}

使用 grpc-wallet 客户端进行验证

使用以下特定于语言的说明来验证配置。以下命令会发送多个 RPC(一些 RPC 具有额外的元数据),以显示系统根据网址映射中的匹配规则将请求路由到后端服务。该命令还会输出每个响应的虚拟机实例的主机名,以显示请求所路由到的虚拟机实例。

Java

  1. 如需使用 gRPC Java 客户端验证服务,请运行以下命令:

    sudo apt-get install -y openjdk-11-jdk-headless
    
  2. 运行以下命令:

    cd ~/traffic-director-grpc-examples/java
    ./gradlew installDist
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance' from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests are
    # sent to 'wallet-v2'.
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium and Bob's go to regular.
    ./build/install/wallet/bin/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Bob
    ./build/install/wallet/bin/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Alice
    

Go

  1. 如需使用 gRPC Go 客户端验证服务,请安装 golang 或按照官方说明进行操作:

    sudo apt-get install -y wget
    wget https://dl.google.com/go/go1.16.5.linux-amd64.tar.gz
    sudo tar -C /usr/local -xvf go1.16.5.linux-amd64.tar.gz
    sudo ln -s /usr/local/go/bin/go /usr/bin/go
    
  2. 运行以下命令:

    cd ~/traffic-director-grpc-examples/go/wallet_client
    go build
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance' from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests
    # are sent to 'wallet-v2'.
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium and Bob's go to regular.
    ./wallet_client price --stats_server="xds:///stats.grpcwallet.io" --watch --user=Bob
    ./wallet_client price --stats_server="xds:///stats.grpcwallet.io" --watch --user=Alice
    

C++

  1. 如需使用 gRPC C++ 客户端验证服务,请运行以下命令:

    sudo apt-get install -y build-essential
    
  2. 运行以下命令:

    cd ~/traffic-director-grpc-examples/cpp
    ../tools/bazel build :client
    
    # This command calls 'FetchBalance' from 'wallet-service' in a loop,
    # to demonstrate that 'FetchBalance' gets responses from 'wallet-v1' (70%)
    # and 'wallet-v2' (30%).
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob
    
    # This command calls the streaming RPC 'WatchBalance'  from 'wallet-service'.
    # The RPC path matches the service prefix, so all requests are sent to 'wallet-v2'.
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
    # This command calls 'WatchPrice' from 'stats-service'. It sends the
    # user's membership (premium or not) in metadata. Premium requests are
    # all sent to 'stats-premium' and get faster responses. Alice's requests
    # always go to premium, and Bob's go to regular.
    ../bazel-bin/cpp/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Bob
    ../bazel-bin/cpp/client price --stats_server="xds:///stats.grpcwallet.io" --watch=true --user=Alice
    

配置更高级的选项

您可以按照下面各部分中的说明配置其他高级流量路由选项。

熔断

通过熔断功能,您可以设置失败阈值以防止客户端请求导致后端服务过载。在未完成请求的数量达到您设置的上限后,客户端会停止发送其他请求,从而为您的后端服务留出恢复时间。

熔断会通过向客户端返回错误而不是让后端服务过载来防止级联故障。这样一来,您便可处理一些流量,同时为管理过载情况提供时间,例如通过自动扩缩来增加容量以应对流量高峰。

stats-service 创建后端服务时,create_service.sh 脚本在其配置中包含以下行:

circuitBreakers:
  maxRequests: 1

此设置将客户端限制为一次向 stats-service 发出一个待处理的请求。由于只有一项 wallet-v2 服务,因此会同时运行两个并发客户端客户端 WatchBalance 操作,从而导致第二个实例发生故障。

Java

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob  2>/dev/null 1>/dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 输出如下所示:

    io.grpc.examples.wallet.Client run
    INFO: Will try to run balance
    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=INTERNAL, description=RPC to stats server failed: UNAVAILABLE: Cluster max concurrent requests limit exceeded, cause=null}
    
  3. 发出 kill 命令:

    kill %%
    

Go

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob 2> /dev/null 1> /dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
  2. 输出如下所示:

    server host: error: no hostname
    failed to fetch balance: rpc error: code = Internal desc = RPC to stats server failed:
    UNAVAILABLE: Cluster max concurrent requests limit exceeded
    
  3. 发出 kill 命令:

    kill %%
    

C++

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob 2>/dev/null 1>/dev/null &
    
    sleep 10 # Wait a few seconds to allow the watch to begin.
    
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 输出如下所示:

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io, stats_server:
    localhost:18882, user: Bob, watch: 1 ,unary_watch: 0, observability_project: , route:
    13: RPC to stats server failed: UNAVAILABLE: Cluster max concurrent requests limit exceeded
    
  3. 发出 kill 命令:

    kill %%
    

故障注入

在处理请求时故障注入会引入错误,以此模拟高延迟、服务过载、服务故障和网络分区等故障。在测试服务对模拟故障的弹性时,这项功能会非常有用。

当您之前创建 URL 映射时,您设置了故障注入政策,让发送到 wallet.grpcwallet.io 的 RPC(具有 route=fault 标头)失败 50%。

如需演示故障注入,请使用以下语言的代码。

Java

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=fault
    
  2. 输出如下所示:

    server host: grpcwallet-wallet-v2-mig-us-central1-zznc
    total balance: 10340491
    - address: 148de9c5, balance: 2549839
    - address: 2e7d2c03, balance: 7790652
    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=UNAVAILABLE, description=RPC terminated due to fault injection: HTTP status code 503, cause=null}
    

Go

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch --user=Bob --route=fault
    
  2. 输出如下所示:

     server host: grpcwallet-wallet-v1-mig-us-central1-bm1t_wallet-v1
     user: Bob, total grpc-coin balance: 10452589.
      - address: 2e7d2c03, balance: 7875108.
      - address: 148de9c5, balance: 2577481.
     failed to fetch balance: rpc error: code = Unavailable desc = RPC terminated due to fault injection
    

C++

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=fault
    
  2. 输出如下所示:

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io,
    stats_server: localhost:18882, user: Bob, watch: 0 ,unary_watch: 1, observability_project: ,
    route: fault server host: grpcwallet-wallet-v2-mig-us-central1-1lm6
    user: Bob total grpc-coin balance: 10211908
     - address: 148de9c5, balance: 2518132
     - address: 2e7d2c03, balance: 7693776
    14: Fault injected
    

流时长上限

流时长上限允许将超时上限应用于所有 RPC。这样可防止忘记设置截止时间或设置过度截止时间的客户端浪费服务器资源。

之前创建网址映射时,将发送到 wallet.grpcwallet.io 的 RPC(具有 route=timeout 标头)的最大流时长设置为 5 秒。

如需演示超时,我们首先停止 wallet-v2 服务。

gcloud

gcloud compute instance-groups managed resize \
    --size=0 grpcwallet-wallet-v2-mig-us-central1 \
    --zone=us-central1-a

以下命令会永久卡住,因为没有后端服务来处理它,并且应用未设置截止时间。

Java

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 此命令停止响应。按 ^C 可中断此命令。

Go

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob
    
  2. 此命令停止响应。按 ^C 可中断此命令。

C++

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    
  2. 此命令停止响应。按 ^C 可中断此命令。

但是,由于 maxStreamDuration 设置,使用 timeout 路由的以下命令将在 5 秒后失败。

Java

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob --route=timeout
    
  2. 输出如下所示:

    io.grpc.examples.wallet.Client run
    WARNING: RPC failed: Status{code=DEADLINE_EXCEEDED, description=deadline exceeded after 4.999594070s.         [wait_for_ready, buffered_nanos=5000553401, waiting_for_connection], cause=null}
    

Go

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/go/wallet_client
    ./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch --user=Bob --route=timeout
    
  2. 输出如下所示:

    failed to create stream: rpc error: code = DeadlineExceeded desc = context deadline exceeded.
    

C++

  1. 运行以下命令:

    cd ~/traffic-director-grpc-examples/cpp
    ../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob --route=timeout
    
  2. 输出如下所示:

    Client arguments: command: balance, wallet_server: xds:///wallet.grpcwallet.io, stats_server:
    localhost:18882, user: Bob, watch: 1 ,unary_watch: 0, observability_project: , route: timeout
    4: Deadline Exceeded
    

重启 wallet-v2 服务。

gcloud

gcloud compute instance-groups managed resize \
    --size=1 grpcwallet-wallet-v2-mig-us-central1 \
    --zone=us-central1-a

重试

重试功能可让您的 gRPC 应用根据重试政策重试出站请求,从而提高服务可用性。在重试政策中,您可以配置应重试失败请求的条件和重试次数上限(例如,当请求失败并显示特定响应代码时)。

之前创建网址映射时,您会为对 FetchBalance 方法的 RPC(具有 membership=premium 标头)设置重试政策。此政策会重试失败且状态代码为 unavailable 的 RPC,最多重试 3 次。您还将为对 account.grpcwallet.io 的 RPC(具有 route=account-fault 标头)设置故障注入政策,这可导致从 Wallet 服务到 Account 的 RPC 失败 30%。因此,来自测试客户端的标头为 membership=normal 的 RPC 失败率为 30%,而标头为 membership=premium 的 RPC 失败率低于 1%。

如需演示重试,请使用以下语言的代码。

Java

运行以下命令:

cd ~/traffic-director-grpc-examples/java
# 30% of the requests fail because Bob is a normal user.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

Go

运行以下命令:

cd ~/traffic-director-grpc-examples/go/wallet_client
# 30% of the requests fail because Bob is a normal user.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

C++

运行以下命令:

cd ~/traffic-director-grpc-examples/cpp
# 30% of the requests fail because Bob is a normal user.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Bob --route=account-fault
# Less than 1% of the requests fail because Alice is a premium user.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --route=account-fault

会话亲和性

只要实例运行状况良好且有可用容量,会话亲和性就会尽力尝试将具有特定特征(HTTP 标头)的请求发送到同一实例。这对于有状态应用服务器非常有用(当将特定客户端的请求发送到同一实例时(而不是例如轮询分发到不同实例),有状态应用服务器可从提高的性能和效率获益)。

创建后端服务 grpcwallet-wallet-v1-affinity-service 时,在 create_service.sh 脚本中,localityLbPolicy 设置为 ROUND_ROBIN。在此示例中,您将应用以下配置来将 localityLbPolicy 更改为 RING_HASH

sessionAffinity: HEADER_FIELD
localityLbPolicy: RING_HASH
consistentHash:
  httpHeaderName: "session_id"

gcloud

  1. 保存后端服务 grpcwallet-wallet-v1-affinity-service 的配置:

    gcloud compute backend-services export grpcwallet-wallet-v1-affinity-service \
     --destination=bs_config.yaml \
     --global
    
  2. 更新后端服务 grpcwallet-wallet-v1-affinity-service

    project_id="$(gcloud config list --format 'value(core.project)')"
    backend_config="
    backends:
    - balancingMode: UTILIZATION
      capacityScaler: 1.0
      group: projects/${project_id}/zones/us-central1-a/instanceGroups/grpcwallet-wallet-v1-mig-us-central1
    connectionDraining:
      drainingTimeoutSec: 0
    healthChecks:
    - projects/${project_id}/global/healthChecks/grpcwallet-health-check
    loadBalancingScheme: INTERNAL_SELF_MANAGED
    name: grpcwallet-wallet-v1-affinity-service
    portName: grpcwallet-wallet-port
    protocol: GRPC
    sessionAffinity: HEADER_FIELD
    localityLbPolicy: RING_HASH
    consistentHash:
      httpHeaderName: session_id"
    gcloud compute backend-services import grpcwallet-wallet-v1-affinity-service --global <<< "${backend_config}"
    

如需演示会话亲和性,请使用以下语言的代码。使用 --affinity=true 标志时,客户端插入标头 session-id,其中包含每个用户的唯一值。此值的哈希值用于向后端服务 grpcwallet-wallet-v1-affinity-service 的实例组中的特定实例发送请求。

Java

运行以下命令:

cd ~/traffic-director-grpc-examples/java
# Without affinity, requests are sent to both instances.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

Go

运行以下命令:

cd ~/traffic-director-grpc-examples/go/wallet_client
# Without affinity, requests are sent to both instances.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
./wallet_client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

C++

运行以下命令:

cd ~/traffic-director-grpc-examples/cpp
# Without affinity, requests are sent to both instances.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice
# With affinity, requests are sent to only one instance.
../bazel-bin/cpp/client balance --wallet_server="xds:///wallet.grpcwallet.io" --unary_watch=true --user=Alice --affinity=true

恢复后端服务 grpcwallet-wallet-v1-affinity-service 的配置。

gcloud

gcloud compute backend-services import grpcwallet-wallet-v1-affinity-service \
  --source=bs_config.yaml \
  --global

离群值检测

要提高服务可用性,请配置异常主机检测。通过此功能,您可以识别并临时从负载均衡池中移除运行状况不佳的主机。这些运行状况不佳的主机称为异常主机。

gRPC 根据成功率(主机成功处理请求的频率)评估主机。速率受 gRPC 错误、HTTP 错误、超时和其他问题等失败的影响。

通过 Traffic Director 配置异常主机检测时,您可以微调 gRPC 评估主机的方式以及处理异常主机的方式。例如,您可以指定以下条件:

  • 在 gRPC 分析可能的异常主机状态之前,主机必须收到的请求数。

  • 主机在被视为异常主机之前偏离平均成功率的程度。

  • 任何时候都可以从负载均衡池中移除的主机百分比上限。

  • 异常主机从负载均衡池中排除的时长。

如需详细了解可用的参数,请参阅 REST Resource: backendServices 参考文档。但是,gRPC 存在一些限制,如以下部分所述。

限制

gRPC 客户端不支持以下字段:

  • outlierDetection.consecutiveErrors

  • outlierDetection.enforcingConsecutiveErrors

  • outlierDetection.consecutiveGatewayFailure

  • outlierDetection.enforcingConsecutiveGatewayFailure

设置离群值检测

以下过程演示了如何为使用一个实例组作为其后端的服务配置异常主机检测。此过程会建立以下配置:

  • 异常主机检测分析每秒运行一次。您可以使用 interval 字段来配置此行为。
  • 异常主机从负载均衡池中移除,时长增量为 30 秒,如下所示:
    • 如果主机之前从未移除过,则系统只会移除 30 秒。
    • 如果之前已移除过主机,则每次移除后时间都会增加 30 秒。例如,主机第三次移除后,系统会将其移除 90 秒。您可以使用 baseEjectionTime 字段配置此行为。
  • 如果主机在所选时间间隔(在本例中为 1 秒)内成功率低于平均值一个标准差,则被视为运行状况不佳。您可以使用 successRateStdevFactor 字段配置最大标准差。

要以这种方式配置异常主机检测,请按以下步骤操作。

gcloud

  1. 使用 gcloud compute backend-services export 命令导出 grpcwallet-wallet-v2-service 后端服务配置文件:

    gcloud compute backend-services export grpcwallet-wallet-v2-service \
     --destination=bs_config.yaml \
     --global
    
  2. bs_config.yaml 文件中,更新 grpcwallet-wallet-v2-service 配置以包含异常主机检测字段:

    outlierDetection:
     interval:
       seconds: 1
       nanos: 0
     baseEjectionTime:
       seconds: 30
       nanos: 0
     successRateStdevFactor: 1000
    
  3. 使用 gcloud compute backend-services import 命令导入更新后的文件:

    gcloud compute backend-services import grpcwallet-wallet-v2-service \
     --source=bs_config.yaml \
     --global

局部负载均衡

如需以最高效的方式利用资源,请配置局部负载均衡。此功能通过在后端之间均匀分配客户端请求来帮助您节省资源。

配置局部负载均衡时,您可以使用下表中描述的选项。所有选项都要求配置 backendServices 资源。

选项 适用对象 相关配置字段
使用内置政策 所有 gRPC 客户端 localityLbPolicy
使用自定义政策 在仅包含 gRPC 客户端的网格中,使用 gRPC 1.47 版或更高版本的 Java 客户端 localityLbPolicies
定义首选政策列表 在仅包含 gRPC 客户端的网格中,使用 gRPC 1.47 版或更高版本的 Java 客户端 localityLbPolicies

您可以任意组合上述选项。但是,如果同时配置 localityLbPolicylocalityLbPolicies,则 gRPC 会先尝试使用 localityLbPolicies 配置。

如果您未配置局部负载均衡,则 Traffic Director 会使用 ROUND_ROBIN 政策。

如需了解 ROUND_ROBIN 和其他内置政策,请参阅 backendServices 页面上的 localityLbPolicy 说明。

使用内置政策

如果您希望所有客户端都使用单个内置政策,则可以通过配置 localityLbPolicy 字段来选择该政策。

配置此字段时,您可以从以下政策中进行选择:

  • LEAST_REQUEST(仅限 Java 客户端)
  • RING_HASH
  • ROUND_ROBIN

如果并非所有客户端都可以使用同一个政策,请参阅定义首选政策列表

gcloud

  1. 使用 gcloud compute backend-services export 命令导出 grpcwallet-wallet-v2-service 后端服务配置文件:

    gcloud compute backend-services export grpcwallet-wallet-v2-service \
      --destination=bs_config.yaml \
      --global
    
  2. 更新导出的 bs_config.yaml 文件,在其中添加以下代码行:

    localityLbPolicy:
     -- policy
        name: RING_HASH
    
  3. 使用 gcloud compute backend-services import 命令导入更新后的文件:

    gcloud compute backend-services import grpcwallet-wallet-v2-service --source=bs_config.yaml --global
    

使用自定义政策

在适用的情况下,您可以使用通过 gRPC 创建和部署的自定义负载均衡政策。此功能适用于使用 gRPC 1.47 版或更高版本的 Java 客户端。请仅在仅包含 gRPC 客户端的网格中使用此功能。

创建自定义政策时,以下文档可能会有所帮助:

  • 如需使自定义政策更加复杂精密,您可以使用 Open Request Cost Aggregation (ORCA) API。利用这些 API,您可以捕获有关查询费用和服务器利用率的指标。使用这些 API 需要 gRPC 1.48.1 或更高版本。如需了解详情,请参阅 gRPC ORCA 示例

  • 如需了解 xDS 如何将自定义负载均衡配置传送到 gRPC,请参阅 gRPC xDS 自定义负载均衡器配置

如需将 Traffic Director 配置为使用您的自定义政策,请使用 localityLbPolicies 字段标识该政策。

以下步骤演示了此过程。在此过程中,您将更新 grpcwallet-wallet-v2-service 后端服务的配置,以便连接到该服务的客户端使用名为 example.ExampleLoadBalancer 的自定义政策。

gcloud

  1. 使用 gcloud compute backend-services export 命令导出 grpcwallet-wallet-v2-service 后端服务配置文件:

    gcloud compute backend-services export grpcwallet-wallet-v2-service \
      --destination=bs_config.yaml \
      --global
    
  2. 更新导出的 bs_config.yaml 文件以引用 example.ExampleLoadBalancer 政策。添加以下代码行:

    localityLbPolicies:
      - customPolicy:
          name: example.ExampleLoadBalancer
          data: '{ "message": "Hello load-balancing world!" }'
    
  3. 使用 gcloud compute backend-services import 命令导入更新后的文件:

    gcloud compute backend-services import grpcwallet-wallet-v2-service --source=bs_config.yaml --global
    
  4. 可选:测试负载均衡配置:

    Java

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    

    如果您正确配置了自定义负载均衡政策,则会看到包含在配置中的消息 ("Hello load-balancing world!")。

定义首选政策列表

如果您有多个客户端不支持单个负载均衡政策,请创建客户端可使用的政策列表。使用此选项时,如果特定客户端无法使用您的第一个首选政策,则 gRPC 会回退到列表中的下一个政策。

创建首选政策列表时,有效选项包括自定义政策和 ROUND_ROBIN,对于 Java 客户端,还包括 LEAST_REQUEST。您最多可以列出 10 个政策。

此功能仅适用于使用 gRPC 1.47 版或更高版本的 Java 客户端。请仅在仅包含 gRPC 客户端的网格中使用此功能。

gcloud

  1. 使用 gcloud compute backend-services export 命令导出 grpcwallet-wallet-v2-service 后端服务配置文件:

    gcloud compute backend-services export grpcwallet-wallet-v2-service \
      --destination=bs_config.yaml \
      --global
    
  2. 更新导出的 bs_config.yaml 文件以添加 localityLbPolicies 字段。使用表示以下政策的条目填充它:

    • 无效的自定义政策 (example.InvalidLoadBalancer)
    • 有效的自定义政策 (example.ExampleLoadBalancer)
    • 支持的内置政策 (LEAST_REQUEST)
    localityLbPolicies:
      - customPolicy:
          name: example.InvalidLoadBalancer
          data: '{ "message": "This load-balancing policy doesn't work!" }'
      - customPolicy:
          name: example.ExampleLoadBalancer
          data: '{ "message": "Hello load-balancing world!" }'
      - policy:
          name: LEAST_REQUEST
    
  3. 使用 gcloud compute backend-services import 命令导入更新后的文件:

    gcloud compute backend-services import grpcwallet-wallet-v2-service --source=bs_config.yaml --global
    
  4. 可选:测试负载均衡配置:

    Java

    cd ~/traffic-director-grpc-examples/java
    ./build/install/wallet/bin/client balance --wallet_server="xds:///wallet.grpcwallet.io" --watch=true --user=Bob
    

    在响应中,gRPC 尝试寻找但找不到 example.InvalidLoadBalancer。之后,它回退为使用 example.ExampleLoadBalancer,并且您会看到在配置中包含的消息 ("Hello load-balancing world!")。客户端上的 gRPC 日志记录包含一条消息,表明未找到 example.InvalidLoadBalancer

清理资源

要清理资源,请从本地系统运行以下命令:

traffic-director-grpc-examples/scripts/cleanup.sh

禁止服务之间的流量

如果您想要阻止服务 A 和服务 B 之间的流量,并且您的部署在 GKE 上,请设置服务安全性,并使用授权政策来阻止服务之间的流量。如需查看完整说明,请参阅 Traffic Director 服务安全以及 Envoy无代理 gRPC 设置说明。

后续步骤