本教程介绍如何使用 WebSocket 创建具有多个聊天室的实时聊天服务,并通过持久性连接进行双向通信。通过 WebSocket,客户端和服务器可以互相推送消息,而无需轮询服务器获取更新。
虽然您可以将 Cloud Run 配置为使用会话亲和性,但这会提供尽力而为亲和性,这意味着任何新请求仍然可能会路由到其他实例。因此,聊天服务中的用户消息需要在所有实例之间同步,而不仅仅是在连接到一个实例的客户端之间同步。
设计概览
此示例聊天服务使用 Memorystore for Redis 实例在所有实例中存储和同步用户消息。Redis 使用发布/订阅机制(不要与产品 Cloud Pub/Sub 混淆)将数据推送到连接到任何实例的订阅客户端,从而无需进行 HTTP 轮询来获取更新。
但是,即使有推送更新,任何启动的实例也只会收到推送到容器的新消息。如需加载之前的消息,需要通过永久性存储解决方案存储和检索消息历史记录。此示例使用 Redis 常规的对象存储功能来缓存和检索消息历史记录。

Redis 实例受具有访问权限控制的专用 IP 保护以免受互联网攻击,并且限制为 Redis 实例所在虚拟专用网中运行的服务;因此,Cloud Run 服务需要无服务器 VPC 访问通道连接器才能连接到 Redis。详细了解无服务器 VPC 访问通道。
限制
本教程未介绍最终用户身份验证或会话缓存。如需详细了解最终用户身份验证,请参阅有关最终用户身份验证的 Cloud Run 教程。
本教程未实现数据库(例如 Firestore)以无限期地存储和检索聊天记录。
此示例服务还需要其他元素才能用于生产环境。建议使用标准层级 Redis 实例,通过复制和自动故障切换提供高可用性。
目标
编写、构建和部署使用 WebSocket 的 Cloud Run 服务。
连接到 Memorystore for Redis 实例,以跨实例发布和订阅新消息。
使用无服务器 VPC 访问通道连接器连接 Cloud Run 服务和 Memorystore。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
准备工作
- 登录您的 Google Cloud 帐号。如果您是 Google Cloud 新手,请创建一个帐号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
-
在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目。
-
确保您的 Google Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能。
-
在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目。
-
确保您的 Google Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能。
-
启用 Cloud Run, Memorystore for Redis, Serverless VPC Access, Artifact Registry, and Cloud Build API。
- 安装并初始化 gcloud CLI。
所需的角色
如需获得完成本教程所需的权限,请让您的管理员为您授予项目的以下 IAM 角色:
-
Artifact Registry Reader (
roles/artifactregistry.reader
) -
Cloud Build Editor (
roles/cloudbuild.builds.editor
) -
Cloud Memorystore Redis Admin (
roles/redis.admin
) -
Cloud Run Admin (
roles/run.admin
) -
Create Service Accounts (
roles/iam.serviceAccountCreator
) -
Project IAM Admin (
roles/resourcemanager.projectIamAdmin
) -
Serverless VPC Access Service Agent (
roles/vpcaccess.serviceAgent
) -
Service Account Admin (
roles/iam.serviceAccountAdmin
) -
Service Usage Consumer (
roles/serviceusage.serviceUsageConsumer
)
如需详细了解如何授予角色,请参阅管理访问权限。
设置 gcloud
默认值
如需为您的 Cloud Run 服务配置 gcloud 默认值,请执行以下操作:
设置默认项目:
gcloud config set project PROJECT_ID
将 PROJECT_ID 替换为您在本教程中创建的项目的名称。
为您选择的区域配置 gcloud:
gcloud config set run/region REGION
将 REGION 替换为您选择的受支持的 Cloud Run 区域。
Cloud Run 位置
Cloud Run 是区域级的,这意味着运行 Cloud Run 服务的基础架构位于特定区域,并且由 Google 代管,以便在该区域内的所有可用区以冗余方式提供。
选择用于运行 Cloud Run 服务的区域时,主要考虑该区域能否满足您的延迟时间、可用性或耐用性要求。通常,您可以选择距离用户最近的区域,但除此之外,您还应该考虑 Cloud Run 服务使用的其他 Google Cloud 产品的位置。跨多个位置使用 Google Cloud 产品可能会影响服务的延迟时间和费用。
Cloud Run 可在以下区域使用:
基于层级 1 价格
asia-east1
(台湾)asia-northeast1
(东京)asia-northeast2
(大阪)europe-north1
(芬兰)二氧化碳排放量低
europe-southwest1
(马德里)二氧化碳排放量低
europe-west1
(比利时)二氧化碳排放量低
europe-west4
(荷兰)europe-west8
(米兰)europe-west9
(巴黎)二氧化碳排放量低
me-west1
(特拉维夫)us-central1
(爱荷华)二氧化碳排放量低
us-east1
(南卡罗来纳)us-east4
(北弗吉尼亚)us-east5
(哥伦布)us-south1
(达拉斯)us-west1
(俄勒冈)二氧化碳排放量低
基于层级 2 价格
asia-east2
(香港)asia-northeast3
(韩国首尔)asia-southeast1
(新加坡)asia-southeast2
(雅加达)asia-south1
(印度孟买)asia-south2
(印度德里)australia-southeast1
(悉尼)australia-southeast2
(墨尔本)europe-central2
(波兰,华沙)europe-west12
(都灵)europe-west2
(英国伦敦)europe-west3
(德国法兰克福)europe-west6
(瑞士苏黎世)二氧化碳排放量低
me-central1
(多哈)northamerica-northeast1
(蒙特利尔)二氧化碳排放量低
northamerica-northeast2
(多伦多)二氧化碳排放量低
southamerica-east1
(巴西圣保罗)二氧化碳排放量低
southamerica-west1
(智利圣地亚哥)us-west2
(洛杉矶)us-west3
(盐湖城)us-west4
(拉斯维加斯)
如果您已创建 Cloud Run 服务,则可以在 Google Cloud 控制台中的 Cloud Run 信息中心内查看区域。
检索代码示例
要检索可用的代码示例,请执行以下操作:
将示例代码库克隆到本地计算机:
Node.js
git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
或者,您也可以下载该示例的 zip 文件并将其解压缩。
切换到包含 Cloud Run 示例代码的目录:
Node.js
cd nodejs-docs-samples/run/websockets/
了解代码
Socket.io 是一个可实现浏览器与服务器间实时双向通信的库。虽然 Socket.io 不是 WebSocket 实现,但它封装了功能以提供一个较简单的 API,为多个通信协议提供额外功能,例如更高的可靠性、自动重新连接以及广播到所有或部分客户端。
客户端集成
客户端为每个连接实例化一个新的套接字实例。由于此示例是服务器端呈现的,因此无需定义服务器网址。套接字实例可以发出和监听事件。
服务器端集成
在服务器端,Socket.io 服务器完成初始化,并连接到 HTTP 服务器。与客户端类似,Socket.io 服务器与客户端建立连接后,会为每个连接创建一个套接字实例,用于发出和监听消息。Socket.io 还提供了一个简单的界面,用于创建“聊天室”,即套接字可以加入和退出的任意通道。
Socket.io 还提供了一个 Redis 适配器,可以向所有客户端广播事件,无论是哪个服务器服务套接字。Socket.io 仅使用 Redis 的发布/订阅机制,不存储任何数据。
Socket.io 的 Redis 适配器可以重复使用用于存储聊天室的消息记录的 Redis 客户端。每个容器都会创建一个与 Redis 实例的连接,而 Cloud Run 可以创建大量实例。这远低于 Redis 可以支持的 65,000 个连接。如果您需要支持这么大的流量,还需要评估无服务器 VPC 访问通道连接器的吞吐量。
重新连接
Cloud Run 的最长超时时间为 60 分钟。因此,您需要添加重新连接逻辑,以应对可能的超时情况。在某些情况下,Socket.io 会在断开连接或连接错误事件后自动尝试重新连接。无法保证客户端将重新连接到同一个实例。
如果存在活动连接,实例将一直存在,直到所有请求关闭或超时为止。即使您使用 Cloud Run 会话亲和性,也可以将新请求负载均衡到活跃容器,从而允许容器缩容。如果您担心大量容器在流量高峰后继续存在,可以降低超时值上限,从而更频繁地清理未使用的套接字。
发布服务
创建一个 Memorystore for Redis 实例:
gcloud redis instances create INSTANCE_ID --size=1 --region=REGION
将 INSTANCE_ID 替换为实例名称(即
my-redis-instance
),并将 REGION_ID 替换为所有资源和服务(例如us-central1
)的区域。系统会自动为实例分配一个在默认服务网络范围内的 IP 地址范围。本教程使用 1GB 的内存用于本地缓存 Redis 实例中的消息。详细了解如何为您的使用场景确定 Memorystore 实例的初始大小。
设置无服务器 VPC 访问通道连接器:
要连接到 Redis 实例,您的 Cloud Run 服务需要访问 Redis 实例的已获授权的 VPC 网络。
每个 VPC 连接器都需要有自己的
/28
子网以放置连接器实例。此 IP 范围不得与 VPC 网络中预留的任何现有 IP 地址重叠。例如,10.8.0.0
(/28
) 适用于大多数新项目,您也可以另外指定一个未使用的自定义 IP 范围,例如10.9.0.0
(/28
)。您可以在 Google Cloud 控制台中查看当前预留的 IP 地址范围。gcloud compute networks vpc-access connectors create CONNECTOR_NAME \ --region REGION \ --range "10.8.0.0/28"
将 CONNECTOR_NAME 替换为您的连接器的名称。
此命令会在默认 VPC 网络中创建一个连接器,该连接器与 Redis 实例相同,机器大小为
e2-micro
。增加连接器的机器大小可以提高连接器的吞吐量,但也会增加费用。连接器还必须与 Redis 实例位于同一区域。详细了解如何配置无服务器 VPC 访问通道。使用 Redis 实例的授权网络的 IP 地址定义环境变量:
export REDISHOST=$(gcloud redis instances describe INSTANCE_ID --region REGION --format "value(host)")
创建一个服务帐号作为服务身份。默认情况下,该帐号不具备除项目成员资格之外的任何特权。
gcloud iam service-accounts create chat-identity gcloud projects add-iam-policy-binding PROJECT_ID \ --member=serviceAccount:chat-identity@PROJECT_ID.iam.gserviceaccount.com \ --role=roles/serviceusage.serviceUsageConsumer
构建容器映像并部署到 Cloud Run:
gcloud run deploy chat-app --source . \ --vpc-connector CONNECTOR_NAME \ --allow-unauthenticated \ --timeout 3600 \ --service-account chat-identity \ --update-env-vars REDISHOST=$REDISHOST
在系统提示时通过响应
y
来响应任何提示,以安装所需 API。 您只需为项目执行一次此操作。如果您尚未按照设置页面中的说明为其他提示设置默认值,请通过提供平台和区域来响应这些提示。详细了解如何从源代码部署。
测试
如需试用完整服务,请执行以下操作:
在浏览器中导航至上述部署步骤提供的网址。
添加你的姓名和聊天室以登录。
向聊天室发送消息!
如果您选择继续开发这些服务,请注意,它们已限制了 Identity and Access Management (IAM) 对 Google Cloud 其余服务的访问权限,并需要额外的 IAM 角色才能访问众多其他服务。
费用讨论
以下是一个聊天服务的费用明细示例,该聊天服务托管在爱荷华 (us-central1) 并且具有 5 GB Redis 实例和无服务器 VPC 访问通道连接器。
产品 | 每月费用 |
---|---|
Redis | 费用 = 预配容量 (5 GB) * 区域层级价格 (us-central1) 标准层级:5 GB * $0.054 GB/小时 * 730 小时/月 = $197 基本层级:5 GB * $0.027 GB/小时 * 730 小时/月 = $99 |
无服务器 VPC 访问通道 | 费用 = 机器大小价格 * 实例数(最小实例数默认为 2) f1-micro:$3.88 * 2 个实例 = $7.76 e2- micro:$6.11 * 2 个实例 = $12.22 e2-standard-4:$97.83 * 2 个实例 = $195.66 |
Cloud Run | 费用 = Cloud Run 服务资源使用时间 * 价格 * 实例数 内存:0.5 GiB * $0.00000250 / GiB-s * 60 秒/分钟 * 60 分钟/小时 * 8 小时 * 30.5 天 * 4 个实例 = $4.39† CPU:1 个 vCPU * $0.00002400 / vCPU-s * 60 秒/分钟 * 60 分钟/小时 * 8 小时 * 30.5 天 * 4 个实例 = $84.33† 请求:$0.40 / 百万 ~= $0 网络:$0.085/GB/月 ~= $0 |
总计 | $197 + $12.22 + $89 ~= 每月 $298 |
† 请参阅下方的讨论,了解场景上下文。
本教程使用独立的基本层级 Redis 实例。服务层级可升级到标准层级,以获得自动启用的跨可用区复制和自动故障切换功能,从而实现高可用性。区域和容量也会影响 Redis 价格。例如,爱荷华 (us-central1) 的一个 5 GB 标准层级实例费用为每小时每 GB $0.054。每小时费用为 5 * $0.054,约为每小时 $0.27 或每月 $197。确定 Memorystore 实例的初始大小,并详细了解 Redis 价格。
无服务器 VPC 访问通道连接器按实例大小和数量以及网络出站流量计费。增加大小和数量可以提高吞吐量或缩短消息的延迟时间。有 3 种大小的机器,f1-micro、e2-micro 和 e2-standard-4。最小实例数为 2,因此最少费用是机器大小的两倍。
Cloud Run 按内存、CPU、请求数量和网络的资源用量计费,舍入至最接近的 100 毫秒。本教程使用的默认 Cloud Run 设置为 512 MiB 和 1 个 vCPU,费用分别为 0.5 GiB * $0.00000250 / GiB 秒和 1 个 vCPU * $0.00002400 / vCPU 秒,即每个实例每小时总计 $0.091。并发对总费用有很大影响,但限制和建议因语言而异。增加最大并发可以减少所需的实例数量。在使用最多 1,000 个实例和 250 个并发请求的情况下,您最多可以服务 25 万个客户端。例如,一个服务为 1000 名员工提供服务,并发请求数为 250,则至少需要 4 个实例。4 个实例在 1 个月(每天 8 小时)内的费用为 $0.091 * 4 个实例 * 8 小时 * 30.5 天 = $89。请求数量和网络将产生额外费用,但这部分费用可能很少。
对于托管在爱荷华 (us-central1) 的这一聊天服务,5 GB 标准 Redis 实例费用为 $197,默认的无服务器 VPC 访问通道连接器费用为 $12.22,Cloud Run 服务的预估费用为 89 美元,每月总费用为 298 美元。使用 Google Cloud 价格计算器查看预估费用。
清除数据
如果您为本教程创建了一个新项目,请删除项目。 如果您使用的是现有项目,希望保留此项目且不保留本教程中添加的任何更改,请删除为教程创建的资源。
删除项目
为了避免产生费用,最简单的方法是删除您为本教程创建的项目。
如需删除项目,请执行以下操作:
- 在 Google Cloud 控制台中,转到管理资源页面:
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
删除教程资源
删除您在本教程中部署的 Cloud Run 服务:
gcloud run services delete SERVICE-NAME
其中,SERVICE-NAME 是您选择的服务名称。
您还可以从 Google Cloud 控制台中删除 Cloud Run 服务。
移除您在教程设置过程中添加的 gcloud 默认区域配置:
gcloud config unset run/region
移除项目配置:
gcloud config unset project
删除在本教程中创建的其他 Google Cloud 资源:
- 从 Artifact Registry 中删除服务容器映像(名称为
gcr.io/PROJECT_ID/chat-app
) - 删除服务帐号
chat-identity@PROJECT_ID.iam.gserviceaccount.com
- 删除 Memorystore for Redis 实例
- 删除无服务器 VPC 访问通道连接器
- 从 Artifact Registry 中删除服务容器映像(名称为
后续步骤
详细了解 Socket.io 的工作原理和更高级的用法。
深入了解如何配置无服务器 VPC 访问通道。
查看 Memorystore和在 Cloud Run 上使用 WebSocket 的最佳做法。
探索无服务器 VPC 访问通道诊断工具,排查任何无服务器网络问题。