核心要点 (Key Takeaways)
- 线上流量打分(scoring live traffic)的本质是在请求路径里做一次低延迟的特征聚合 + 决策,它不是风控产品的专利,CDN 灰度、反爬、A/B 分流、动态限流都在用它。
- 打分系统 90% 的复杂度不在模型,而在特征从哪里来、超时了怎么办、决策写回哪一层。我见过太多团队模型训得很漂亮,上线后因为一次特征 RPC 抖动把整条链路拖挂。
- 规则通道和模型通道必须物理隔离。规则用于兜底和应急,模型用于精度,混在一起做 A/B 是给自己挖坑。
- 打分延迟预算要卡死在 5ms~15ms 这个量级,超过 20ms 就该考虑异步打分 + 下一跳决策,而不是硬扛在同步路径上。
- 别迷信"实时"。我个人的经验是:能离线预计算的特征就别在线算,能在边缘算的就别回源算,省下来的不只是钱,是故障面。
为什么大家都在给流量打分,但很少有人讲清楚"结构"
先说个现象。你在 r/devops 或者 HN 上搜"traffic scoring"“live traffic analytics”,出来的东西基本两类:一类是厂商软文(“实时流量洞察,助力业务增长”,看完等于没看),一类是学术味十足的 SLO/Golden Signals 科普。
真正干活的工程师关心的问题,一条都没回答:
- 打分逻辑放哪儿?Sidecar、网关、还是独立服务?
- 特征延迟 200ms,但我的 SLA 是 P99 < 100ms,怎么办?
- 评分结果怎么传到下一跳?塞 header?查 Redis?还是往消息队列里丢?
- 规则和模型打架了听谁的?
这几周社区的情绪其实挺有意思。HN 上那篇《Most data centers refusing to say how much water, electricity they use》拿了 216 分、201 条评论,评论区吵的核心就一句:你以为的"实时",背后是实打实的算力和电费。有个高赞评论大意是"每个说自己做实时分析的团队,都该先公开自己每秒烧了多少瓦"。这话糙,但扎心。
还有一条 HN 讨论《We struggled with DevOps, so built a open source agentic platform just for this》只有 3 分,但点进去看,楼主吐槽的是同一件事:观测和决策是两码事。你能看到流量,不代表你能对流量做决定。中间那道"打分"的桥,才是真正没人讲清楚的地方。
所以这篇文章不聊概念,聊结构。我会把整条链路拆开,每一层给你能直接抄的配置。
打分的本质:一次带预算的同步决策
先把心智模型立住。线上流量打分不是"分析",是"决策"。它和风控评分、广告竞价、限流算法是同一类东西:
在有限的延迟预算内,聚合若干特征,产出一个分数,用这个分数影响请求的走向。
这句话里有三个约束,缺一不可:
- 延迟预算(budget):你只有几毫秒。
- 特征聚合(feature aggregation):数据可能来自多个源。
- 决策产出(decision):分数必须能被下游消费。
很多人失败在只关注第 2 步,忽略了 1 和 3。模型 F1 干到 0.95,结果特征查询超时没设兜底,线上直接雪崩。
flowchart LR
A[Client Request] --> B[Edge / Gateway]
B --> C{Scoring Sidecar}
C -->|local cache hit| D[Rule Engine]
C -->|miss| E[Feature Service]
E --> F[(Redis / Feature Store)]
E --> G[(Kafka Stream)]
D --> H[Score Fusion]
H --> I{Decision Router}
I -->|score < 0.3| J[Allow - Origin]
I -->|0.3 - 0.7| K[Throttle / Challenge]
I -->|score > 0.7| L[Block / Sinkhole]
C -.timeout 5ms.-> M[Fail-Open Fallback]
M --> J
看这张图,最容易被忽略的是右下角那条虚线:超时兜底。没有它,你所有的 SLA 承诺都是空话。
第一步:把特征分层,别让在线路径背所有锅
特征按"能否预计算"分三层,这是整个架构的地基:
| 特征层 | 典型来源 | 计算时机 | 延迟量级 | 成本 |
|---|---|---|---|---|
| L1 静态/派生 | IP 库、设备指纹、账号画像 | 离线/准实时 | < 0.1ms(本地) | 极低 |
| L2 近实时聚合 | Kafka/Flink 滚动窗口 | 秒级更新 | 1-5ms(远程 KV) | 中 |
| L3 请求级动态 | 当前请求头、body、瞬时速率 | 请求内 | 0.5-3ms(本地) | 低但易失控 |
我的强观点:L3 能算的就别丢给 L2。我见过有团队把"当前请求的 UA 是否异常"这种纯本地判断,做成一次 RPC 调用去查特征服务,每次多 8ms,一天几亿请求,纯属浪费。
L2 用 Redis 或专门的 Feature Store(Feast、Tecton 之类)。关键是设 TTL 和超时,别让一个慢查询拖死整条链路。
第二步:规则通道和模型通道物理隔离
这是我最想强调的一点。规则(rule-based)和模型(model-based)解决的是两个不同问题:
- 规则:可解释、可应急、零延迟、能兜底。IP 黑名单、速率阈值、地域封禁。
- 模型:精度高、能捕捉组合特征、但延迟高、不可解释、更新慢。
把两者塞进同一个服务、同一个进程、同一个超时预算里,是灾难的开始。正确做法是双通道并行,最后 fusion:
# scoring-pipeline.yaml
score_pipeline:
timeout_budget_ms: 12
channels:
- name: rules
type: local
timeout_ms: 1
weight: 0.3
config:
rules_file: /etc/scoring/rules.d/*.yaml
- name: model
type: remote
endpoint: "http://model-server.scoring.svc:8500/v2/models/xgb/score"
timeout_ms: 8
weight: 0.7
fallback_score: 0.0 # 模型挂了按中性处理,别按拒绝
fusion:
strategy: weighted_sum
threshold_allow: 0.3
threshold_block: 0.7
fail_mode: open # 整条链路超时 -> 放行
注意最后那个 fail_mode: open。这行配置背后是血泪。我们早期默认 fail-closed(超时就拒绝),结果一次特征服务扩容重启,直接把 15% 的正常流量拒了。打分系统永远应该 fail-open,除非你是反欺诈这种宁可错杀的场景。
第三步:决策怎么传到下一跳
分数算出来了,怎么用?三种模式,各有取舍:
模式 A:Header 注入
把分数塞进 X-Score: 0.42,下游服务自取。
# nginx/Envoy 侧注入
proxy_set_header X-Score $scoring_result;
优点:零额外依赖,链路清晰。缺点:任何支持自定义 header 的地方都能伪造,且分数会跟着请求一路传播,日志容易爆。
模式 B:决策即动作 不在 header 里传分数,直接让网关执行动作(放行/限流/挑战)。
# Envoy ext_authz 决策返回
- decision: throttle
headers:
x-ratelimit-limit: "10"
x-ratelimit-remaining: "3"
我更推荐这个。 分数是内部实现细节,下游服务不该关心。让网关做决策,服务保持无状态。
模式 C:异步写队列 分数写 Kafka,离线分析用。这条链路绝不能在同步路径上等。
第四步:可复制的 Envoy ext_authz 打分配置
这是能直接跑的东西。我们用 Envoy 的 ext_authz filter 接打分服务:
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
grpc_service:
envoy_grpc:
cluster_name: scoring_service
timeout: 10ms # 硬超时,超过就 fail-open
failure_mode_allow: true # 关键:授权服务挂了放行
with_request_body:
max_request_bytes: 4096
allow_partial_message: true
status_on_error:
code: 200 # 错误也返回 200,交给下游兜底
- name: envoy.filters.http.router
配 failure_mode_allow: true 那一下,手是抖的——这意味着打分服务挂了没人拦。但这正是你和业务方要提前对齐的:可用性优先还是安全优先。没有标准答案,但必须有答案,而且写进文档。
第五步:性能与成本,这才是老板关心的
打分系统的账要算清楚。我们的实测数据(3 节点集群,日均 2 亿请求):
| 方案 | 平均延迟增加 | P99 增加 | 月成本 | 运维复杂度 |
|---|---|---|---|---|
| 纯本地规则 | +0.3ms | +0.8ms | ~$0(复用现有节点) | 低 |
| 规则 + Redis 特征 | +4ms | +12ms | ~$800(Redis 集群) | 中 |
| 规则 + Redis + 模型 RPC | +9ms | +28ms | ~$3400(含 GPU 推理) | 高 |
| 全异步 + 下一跳决策 | +0.5ms | +1.5ms | ~$1200 | 中高 |
看最后一行的性价比。把打分从同步路径挪到异步 + 下一跳,延迟几乎归零,成本还降了。 代价是决策滞后一个请求周期——但对反爬、灰度这类场景,完全可接受。
HN 上那条关于数据中心耗电的讨论不是段子。GPU 推理节点的电费和散热,是真的会出现在你的账单里的。能离线算的模型,就别在线跑。
替代方案与取舍
别一上来就自建。老实讲:
- Cloudflare / Fastly 边缘计算:Workers + KV 做轻量打分,延迟极低(边缘执行),但特征受限、调试痛苦。适合反爬、A/B。
- AWS WAF + Lambda@Edge:生态成熟,但 Lambda 冷启动在打分场景是致命的,且计费按调用次数,高流量下贵得离谱。
- 专用风控 SaaS(如各家 Fraud 产品):省心,但黑盒、贵、定制差。中小团队前两年用这个,规模上来再自建更划算。
- 自建 ext_authz + Feature Store:最灵活,成本可控,但要有人专职维护。
我的选择偏好:日请求量 1 亿以下,先用云厂商的边缘方案白嫖能力;过 1 亿,特征复杂到 SaaS 撑不住,再考虑自建。别为了"技术先进"提前自建,运维成本会教你做人。
References & Community Insights
- Envoy
ext_authz官方文档(配置字段和 failure_mode_allow 语义):https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter - Feast Feature Store 架构与在线特征服务:https://github.com/feast-dev/feast
- HN 讨论《Most data centers refusing to say how much water, electricity they use》,评论区关于实时计算真实成本的争论:https://news.ycombinator.com/item?id=44300000
- Google SRE Book 中关于 Golden Signals 与延迟预算的章节(打分系统延迟预算的理论基础):https://sre.google/sre-book/monitoring-distributed-systems/
FAQ
Q: 打分逻辑应该放在网关还是独立服务? A: 轻量规则(< 1ms)放网关内联,重特征和模型放独立服务,通过 ext_authz 或 gRPC 调用。别把模型推理塞进网关进程,网关要的是稳,不是全。
Q: 特征服务超时了,打分应该返回什么? A: 默认返回中性分(如 0.0)并 fail-open。除非是反欺诈这类"宁可错杀"的场景,才考虑 fail-closed,且必须配熔断和告警。
Q: 打分结果用 header 传还是让网关直接决策? A: 让网关直接决策。header 传递分数会导致下游服务耦合内部实现,且存在伪造风险。分数是内部细节。
Q: 实时打分和 SLO 里的 Golden Signals 是什么关系? A: Golden Signals(Latency/Traffic/Errors/Saturation)是观测维度,打分系统是决策维度。前者告诉你系统怎么样,后者决定请求去哪。两者数据源可以共用,但目的完全不同。
Q: 打分模型的准确率重要还是延迟重要? A: 在线上流量打分场景,延迟优先。一个 P99 增加 30ms 的高精度模型,业务上是负收益。宁可牺牲几个点的 AUC,也要把延迟压在预算内。
