+86 189 8218 1436 周一至五 10:00-22:00

Snapchat 使用 Cloud Run 处理动态内容请求

TL;DR:用小白能懂的话拆解 Snapchat 使用 Cloud Run 处理动态内容请求:为何选它、请求怎么跑、如何上手与避坑。

它是什么

Snapchat 使用 Cloud Run 处理动态内容请求,可以理解成:把需要实时计算的内容接口,放进 GCP(谷歌云)的托管容器服务里。你不用自己买服务器、装系统、盯扩容;把接口代码打包成容器,Cloud Run 收到请求就运行并返回结果。动态接口每次可能不同,所以适合这种能按请求自动扩缩的后端。

「动态」不是说页面会动,而是结果会随账号、时间、地区或策略变化的接口调用,例如个性化列表、活动配置、元数据或一段 JSON 响应。A 用户和 B 用户看到的列表可能不同,上午和晚上的配置可能不同,不同地区也可能返回不同内容。判断登录态、查数据库、套规则、拼返回值,都是后端计算。

先不用部署,也不用 GCP 账号,复制下面命令看测试接口如何返回 JSON:

curl -s "https://httpbin.org/anything?user=demo&region=cn&time=now"

换掉 user、region、time,结果里的 args 也会变。真实项目里,生成 JSON 的代码就可以放在 Cloud Run 容器里;这只是演示,不是 Snapchat 真实接口。

静态图片、视频等资源更适合交给 CDN 或 Cloud Storage 这类对象存储。它们像货架上的成品,通常不随账号变化,放 CDN 更快、更省,也能减轻后端压力。Cloud Run 主要负责需要后端计算的那部分,别把大文件塞进接口。

  • 别把 Cloud Run 当图床:大图、视频放对象存储,再由 CDN 分发;接口转发会慢、贵、超时。
  • 别把个性化 JSON 缓存太久:否则可能串号或看到旧活动;按账号、地区变化的响应必要时用 no-store。
  • 别把状态只写在容器本地:实例会扩缩,下次可能换一台;数据放数据库,文件放对象存储。首次访问略慢常是冷启动。

一句话:静态资源负责「快和省」,动态接口交给 Cloud Run 负责「算和变」。

解决什么问题

把“Snapchat 使用 Cloud Run 处理动态内容请求”想成一家排队忽长忽短的小店:活动、热点、消息扩散都会让请求突然暴涨,过一会儿又安静。Cloud Run 会按请求自动扩容,高峰多开实例;空闲时缩容,甚至缩到零,少为空机器买单。

难点在于这些结果多是动态的:不同用户看到的推荐、未读、好友状态和权限不一样,通常要现场算,不能像图片、JS 这类静态文件长期缓存。可以缓存公共外壳或短时间公共数据,但用户级结果缓存太久,常会踩到“刷新还是旧状态”甚至串号的坑。

托管容器解决的是“少管机器”:团队不用自己维护底层 VM、系统补丁,也不必提前猜要买多少台。把代码、运行时和依赖封进容器,Cloud Run 负责接请求、拉起实例和扩缩容。小白先在 Cloud Shell 复制下面命令做体检,确认登录账号、当前项目、API 和已有服务;这不是部署,不会马上建出一堆资源。

gcloud auth login
gcloud config list
gcloud services enable run.googleapis.com
gcloud run services list --platform=managed --format='table(metadata.name,status.url,spec.template.spec.containerConcurrency)'

这几行分别用于登录、查看账号和项目、启用 API、列出服务网址与并发设置。常见坑:project 显示 unset 时后面会失败;API 生效可能要等一会儿;列表为空只是还没服务。缩容到零省钱,但第一次访问可能有冷启动,敏感接口可设最小实例;容器本地盘也别当用户数据永久保存。

  • 容器把语言版本、依赖库、启动命令一起打包,换机器也更一致;每次发布形成清楚的版本,回滚旧版本、隔离测试与线上都更直观。
  • Cloud Run 隐藏了节点、Pod、Ingress 等细节,零基础不必先学完整 Kubernetes,先记住“一个镜像 + 一个网址 + 按请求扩缩容”即可。

怎么上手

先打地基:在控制台建好 GCP 项目,复制项目 ID;Linux/macOS 用官方脚本装 Google Cloud CLI,Windows 用安装包。只从官方域名执行,装完重开终端。

curl https://sdk.cloud.google.com | bash

下面整段复制执行,输入项目 ID 和区域即可。启用 API 是让 Cloud Run、镜像仓库和构建服务互相放行;区域选离用户和数据库近的,别只填项目名。

read -p "输入你的 GCP 项目 ID:" PROJECT_ID
read -p "输入部署区域(直接回车用 asia-east1):" REGION
REGION="${REGION:-asia-east1}"
gcloud auth login
gcloud config set project "$PROJECT_ID"
gcloud config set run/region "$REGION"
gcloud services enable run.googleapis.com artifactregistry.googleapis.com cloudbuild.googleapis.com --project "$PROJECT_ID"
gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet
gcloud artifacts repositories create snapchat-dynamic --repository-format=docker --location="$REGION" --description="Cloud Run dynamic content" --project "$PROJECT_ID" --quiet || true

接着做一个最小动态服务:每次请求都返回当前时间、路径和区域,这是 Snapchat 使用 Cloud Run 处理动态内容请求的最小版本。用容器是为了让本地和线上环境一致。

cat > main.js <<'EOF'
const http = require('http');
const port = process.env.PORT || 8080;
http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
  res.end(JSON.stringify({
    service: 'snapchat-dynamic',
    time: new Date().toISOString(),
    path: req.url,
    region: process.env.REGION || 'unknown',
    tip: '这是每次请求生成的动态 JSON'
  }));
}).listen(port, '0.0.0.0', () => console.log('listening on', port));
EOF
cat > Dockerfile <<'EOF'
FROM node:20-slim
WORKDIR /app
COPY main.js .
EXPOSE 8080
CMD ["node", "main.js"]
EOF

再构建镜像并部署。公开演示用允许匿名;内部接口改成禁止匿名并给调用方 Cloud Run Invoker。端口要与 PORT 一致,环境变量只放普通配置。

PROJECT_ID="$(gcloud config get-value project)"
REGION="$(gcloud config get-value run/region)"
IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/snapchat-dynamic/app:v1"
gcloud builds submit --tag "$IMAGE" --project "$PROJECT_ID" .
gcloud run deploy snapchat-dynamic 
  --image "$IMAGE" 
  --region "$REGION" 
  --project "$PROJECT_ID" 
  --platform managed 
  --allow-unauthenticated 
  --port 8080 
  --set-env-vars "REGION=${REGION},STAGE=demo" 
  --quiet
gcloud run services describe snapchat-dynamic --region "$REGION" --project "$PROJECT_ID" --format='value(status.url)'

需要数据时再连接 Cloud SQL、Firestore、Memorystore 或外部接口:关系数据、文档、缓存、第三方接口各有分工;密钥放 Secret Manager。下面先启用相关 API、设置接口地址并读日志。

PROJECT_ID="$(gcloud config get-value project)"
REGION="$(gcloud config get-value run/region)"
gcloud services enable sqladmin.googleapis.com firestore.googleapis.com redis.googleapis.com vpcaccess.googleapis.com secretmanager.googleapis.com logging.googleapis.com monitoring.googleapis.com --project "$PROJECT_ID"
gcloud run services update snapchat-dynamic 
  --region "$REGION" 
  --project "$PROJECT_ID" 
  --update-env-vars "API_BASE=https://example.com,DATA_MODE=demo" 
  --quiet
gcloud logging read 'resource.type="cloud_run_revision" AND resource.labels.service_name="snapchat-dynamic"' --limit 20 --project "$PROJECT_ID"

上线后用 Cloud Logging 看日志,用 Cloud Monitoring 看延迟与错误率,重点盯 p95 和 5xx。

最后绑定已验证的自定义域名,或接入负载均衡的 serverless NEG。映射后按输出加 DNS 记录,再按访问量调整并发、最小实例、最大实例和超时时间。

PROJECT_ID="$(gcloud config get-value project)"
REGION="$(gcloud config get-value run/region)"
read -p "输入已验证的自定义域名(如 api.example.com):" DOMAIN
gcloud run domain-mappings create --service snapchat-dynamic --domain "$DOMAIN" --region "$REGION" --project "$PROJECT_ID" --quiet
gcloud run domain-mappings list --region "$REGION" --project "$PROJECT_ID" --format='yaml'
gcloud run services update snapchat-dynamic 
  --region "$REGION" 
  --project "$PROJECT_ID" 
  --concurrency 80 
  --min-instances 1 
  --max-instances 20 
  --timeout 30s 
  --quiet
  • 常见坑:项目 ID 不是项目名;API 生效要等一会儿;容器只听 127.0.0.1 会 502,必须听 0.0.0.0 和 PORT。
  • 常见坑:公开服务别放管理接口;本地文件重启会丢;外部接口不设超时会占满实例。
  • 常见坑:最小实例越大越贵,最大实例太小会排队,并发太高会压垮数据库,超时别盲目拉长;域名要验证所有权,DNS 不是秒生效。

与替代方案对比

把「Snapchat 使用 Cloud Run 处理动态内容请求」说人话,就是用户一点开内容,后端要按请求即时算或取结果,不是直接发一张固定图片。先在本地看一次这种请求,在 macOS、Linux 或 Cloud Shell 粘贴:

python3 -c 'from http.server import *
import datetime, threading, urllib.request
class H(BaseHTTPRequestHandler):
 def do_GET(s):
  b=("动态内容:"+datetime.datetime.now().isoformat()+" "+s.path).encode()
  s.send_response(200); s.end_headers(); s.wfile.write(b)
 def log_message(s,*a): pass
h=HTTPServer(("127.0.0.1",8080),H); threading.Thread(target=h.serve_forever,daemon=True).start()
r=urllib.request.urlopen("http://127.0.0.1:8080/feed"); print(r.status, r.read().decode()); h.shutdown()'

为什么这么做:它会临时开一个小 HTTP 服务,再请求 /feed,返回时间和路径都会变。看懂它,对比方案时只看一件事:谁替你接住请求、扩缩容、打补丁、管流量。小白坑:8080 被占用会报错;真正上 Cloud Run 别只监听 127.0.0.1 或写死端口,要按 PORT 启动。

  • 对比 VM:自己搭 VM 控制力最强,但系统、补丁、扩容、负载均衡都要自己扛;只想快速托管 HTTP 服务,Cloud Run 更省事。
  • 对比 GKE:GKE 适合复杂微服务、精细调度,代价是 Pod、Service、YAML 学习成本更高;轻量接口更适合 Cloud Run 按请求扩缩,没人访问就缩回去。
  • 对比 Cloud Functions:小函数、事件触发很方便;依赖多、要统一容器镜像、要管并发和常驻接口时,Cloud Run 更顺手。
  • 对比 App Engine 与 CDN:App Engine 托管应用省心但运行时更受限;CDN 和对象存储擅长图片、JS、视频等静态资源,不能替代动态计算。

最后提醒:别把 CDN 缓存当默认答案,动态接口缓存错了可能把 A 用户内容给 B 用户;也别以为托管就等于不用管,Cloud Run 仍要设并发、超时、最大实例和鉴权。

最佳实践

先记住一句话:Cloud Run 的实例说没就没。空闲或扩缩容时它会重启,写在内存、本地磁盘里的登录态、草稿和上传图片,重启后全丢。所以服务尽量做成无状态:会话放缓存,业务数据放数据库,文件放对象存储。为什么?实例可以随便换,数据才不会跟着一起消失。

export PROJECT_ID=$(gcloud config get-value project)
export REGION=asia-east1
gcloud storage buckets create gs://${PROJECT_ID}-snap-uploads --location ${REGION} --uniform-bucket-level-access
gcloud iam service-accounts create snap-dynamic-sa --display-name "snap dynamic runner"
printf '替换成真实密钥' | gcloud secrets create snap-token --data-file=-
gcloud secrets add-iam-policy-binding snap-token --member "serviceAccount:snap-dynamic-sa@${PROJECT_ID}.iam.gserviceaccount.com" --role roles/secretmanager.secretAccessor

小白坑:项目 ID 由第一行自动取,别手敲;桶和密钥建过一次就跳过,重复建会报错;千万别把 /tmp 当网盘用。

冷启动要提前管。处理动态内容请求最怕首个请求卡住。镜像越轻、启动时连的依赖越少就越快:用精简基础镜像,别在启动里拉大模型;留 1 个最小实例兜底,并把并发、最大实例、超时一次定好,上线前按真实并发压测。

gcloud run deploy snap-dynamic 
  --image gcr.io/${PROJECT_ID}/snap-dynamic:latest 
  --region ${REGION} 
  --service-account snap-dynamic-sa@${PROJECT_ID}.iam.gserviceaccount.com 
  --set-secrets SNAP_TOKEN=snap-token:latest 
  --min-instances 1 --max-instances 50 
  --concurrency 80 --timeout 30s 
  --no-allow-unauthenticated

为什么:--max-instances 不设,突发流量会一直扩,账单跟着炸;超时太长,卡住的请求会白占实例时间。坑:最小实例是常驻计费,压测完再决定留不留;镜像名要换成自己已推送的版本。

安全按最小权限来。上面的专用服务账号只拿刚够用的角色,密钥从 Secret Manager 注入,不写死在代码或明文环境变量里;--no-allow-unauthenticated 让内部调用必须带 IAM 身份。公网入口先接 HTTPS 负载均衡,再挂防护策略按 IP 限流。

gcloud compute security-policies create snap-waf --description "动态入口限流"
gcloud compute security-policies rules create 100 --security-policy snap-waf --expression "true" --action throttle --rate-limit-threshold-count 100 --rate-limit-threshold-interval-sec 60 --conform-action allow --exceed-action deny-429 --enforce-on-key IP

坑:别图省事用默认计算服务账号,它权限太大;也别在 run.app 域名上直接配防护规则,不生效。

发布别一把梭。新版本先切 10% 流量灰度,旧版本留着;日志输出成 JSON,配追踪、告警和 5xx 错误率看板,才算有基本可观测性。

gcloud run revisions list --service snap-dynamic --region ${REGION}
gcloud logging read 'resource.type="cloud_run_revision" AND severity>=ERROR' --limit 20 --format json
gcloud run services update-traffic snap-dynamic --region ${REGION} --to-revisions snap-dynamic-00002=10,snap-dynamic-00001=90

出问题时,只单独执行这条回滚:

gcloud run services update-traffic snap-dynamic --region ${REGION} --to-revisions snap-dynamic-00001=100

坑:revision 名以上面 list 的结果为准,别照抄示例编号;回滚命令别和灰度一起复制跑,否则等于没发新版。

成本盯三笔账:请求数、实例时间、出站流量。图片视频等静态资源交给 CDN,动态接口只做必要计算,别顺手把大文件也从容器里吐出去——实例时间和出站流量会一起涨。

常见问题(FAQ)

Cloud Run 能承受 Snapchat 这种大流量吗?

可以按需扩容,但要设最大实例、配额、数据库容量和压测,不是无限增长。

动态内容请求和静态资源有什么不同?

动态结果随用户、时间或地区变化,需要后端计算;静态资源如图片可长期缓存。

小白上手需要懂 Kubernetes 吗?

不需要,先会写 HTTP 服务、打包容器和使用 gcloud 部署即可。

Cloud Run 会不会有冷启动?

会,空闲缩容后首次请求可能变慢;可用最小实例和轻量镜像缓解。

怎么控制费用?

关注请求数、实例时间和出站流量,给最大实例、并发和缓存策略设边界。

不想自己折腾 GCP?我们提供云平台代维护、上云迁移与账单代付服务,一对一技术支持。

查看代维服务 →

发表评论