X.COM 架构白皮书 v15.0:系统设计与技术架构全景

IT 技术 111 阅读 更新于 2026-09-25 17:38

系统设计与技术架构全景 —— 从客户端到数据中心的完整拆解:前端、设计系统、网关、微服务、时间线排序、存储、流计算、边缘网络与安全;七大专题深挖:搜索、通知、DM 消息、媒体管线、ML 平台、实验与变现;五大扩展专题:内容治理、多区域容灾、计数系统、国际化与开放平台;并附 19 个可复现的实战教程(T1–T19),每篇按「原理 → 代码 → 要点 → 动手练习」组织。读多写少、缓存优先、优雅降级,是贯穿全文的三条主线。

文档信息

项目内容
文档版本v15.0
维护团队Core Infra
章节 / 教程27 章 + 19 教程
密级INTERNAL
上次评审2026-07-28

卷首指标(原页为数字滚动动画,此处静态化)

  • 日活跃用户(可货币化 DAU):2.52 亿
  • 峰值推文 / 秒:9400
  • 在线微服务:1200+
  • 边缘 POP 城市:40+
  • 近 12 个月可用性:99.96%

01 · 总体架构

SYSTEM MAP

五层模型 · 读写分离 · 多区域

X 是典型的读多写少社交系统,读写比约 100:1。整体采用五层模型:请求自客户端经边缘 POP 进入,由统一网关完成鉴权与路由后进入领域服务层;数据按访问模式拆分至事务库、KV、缓存与对象存储;ML 平台横向贯穿,为召回与排序提供特征和推理能力。

分层系统图

  • L1 · 客户端:iOS · Swift · Android · Kotlin · Web SPA · React · 第三方 API
  • L2 · 边缘层:Anycast POP · WAF · Bot 评分 · TLS 1.3 · HTTP/3 · 边缘缓存 · 速率限制
  • L3 · 接入层:GraphQL 网关 · WebSocket 通道 · REST v2 · OAuth2 · JWT
  • L4 · 领域服务:推文 tweetypedia · 时间线 mixer · 社交图谱 · 推荐 cr-mixer · 通知 · DM · 媒体 · 搜索
  • L5 · 数据层:MySQL 分片 · Manhattan KV · Pelikan 缓存 · Blobstore · Kafka · Elasticsearch
  • ◈ ML 平台横向贯穿 L3–L5:特征仓库 · GPU 训练集群 · 在线推理(<10ms / 样本)

五条设计原则

  • CACHE-FIRST · 缓存优先:读路径默认命中缓存;一切可重建的数据皆可丢弃。
  • EVENTUAL · 最终一致:观众视角容忍秒级延迟;作者视角保证 read-your-writes。
  • HYBRID FANOUT · 混合扇出:普通用户写扩散、超大 V 读扩散,兼顾延迟与成本。
  • GRACEFUL · 优雅降级:推荐失效回退关注流;依赖故障自动熔断与限流。
  • MOBILE-FIRST · 移动优先:面向弱网与高丢包网络设计,预取与请求合并为默认能力。

关键负载画像

接口 / 场景峰值吞吐P95 目标备注
时间线拉取420K QPS250ms缓存命中率 >95%
发布推文9.4K /s150ms触发扇出与索引双写
媒体上传2.3K /s400ms异步转码矩阵
搜索查询38K QPS300ms查询理解 + 检索分离
DM 消息96K /s200ms端到端加密路径
通知扇出1.2M 事件/s—Kafka 削峰

02 · Web 前端架构

WEB CLIENT

React · GraphQL · PWA

Web 端为React 单页应用:首屏由服务端预渲染 + 客户端水合,后续路由完全客户端渲染。数据层统一走GraphQL 持久化查询,只传查询哈希与变量,显著压缩弱网载荷;Service Worker 负责离线壳、静态资源缓存与 Web Push。状态管理采用轻量自研 store,配合乐观更新保证交互零等待。

TTI < 3s · 首屏 JS ≤ 180KB gz · i18n 40+ 语言 · A/B 实验 SDK · WebSocket 增量时间线

模块划分

模块技术职责
渲染框架React 18+ Concurrent流式渲染、可中断更新
数据层GraphQL / Relay持久化查询、字段订阅、缓存归一化
状态管理自研 store(不可变快照)乐观更新、失败回滚
构建Vite + SWC路由级代码分割、Tree-shaking
实时通道WebSocket时间线增量、通知、DM 打字态
离线 / 推送Service Worker + Web Push离线壳、后台同步、通知唤起

GRAPHQL · 首页时间线持久化查询

query HomeTimeline($cursor: String) {
  home { timeline(count: 20, cursor: $cursor) {
    entries { id sortIndex content { ... on Tweet {
      id text lang createdAt favoriteCount retweetCount
      author { id name verified } media { url type }
    } } }
    nextCursor
  } }
}
# 持久化查询 sha256=3f2a…c9 —— 服务端按哈希命中,仅传输变量

乐观更新:点赞 / 转推先写入本地 store 即时反馈,服务端失败则回滚并提示;弱网下写操作进入离线队列,恢复后按序重放(幂等键去重)。对应教程见T6 GraphQL 网关;长列表渲染优化见T14 虚拟长列表。


03 · 移动客户端

iOS · ANDROID

Swift · Kotlin · 模块化

双端均为模块化原生工程:feed / compose / media / dm 独立编译、独立发布。iOS 全面 Swift 并渐进迁移 SwiftUI;Android 以 Kotlin 为主,Compose 已覆盖约 60% 界面。共享层包括 Protobuf 协议模型、实验 SDK、图片管线(降采样 + 渐进式解码)与崩溃防护框架。发布采用每周版本火车 + 配置中心热修。

双端质量指标

指标iOSAndroid口径
冷启动 P751.6s2.1s中端机型实测
崩溃率0.03%0.05%会话级
下载包体积112MB78MB商店分发尺寸
帧率基准60fps60fps高端机型 120fps
启动内存320MB380MBP75

弱网策略

请求合并 Batching · 图片自适应降质 · 首屏 20 条预取 · 2G 纯文本降级 · 失败指数退避重试


04 · 设计系统

DESIGN SYSTEM

Design Token · 暗黑模式 · 无障碍

X 的界面语言以「内容即界面」为核心:极简黑白基调、单一品牌蓝点缀、信息密度高而秩序清晰。所有视觉决策沉淀为Design Token,由设计工具同步到三端(iOS / Android / Web),任何组件不允许硬编码色值与间距。

色彩 Token(亮色主题)

Token色值用途
accent#1d9bf0主行动按钮、链接、选中态(X Blue)
accentHover#1a8cd8悬停 / 按压
textPrimary#0f1419正文与标题
textSecondary#536471次要文字、时间戳
bgSecondary#f7f9f9次级背景、悬停行
divider#eff3f4分割线、边框
like#f91880点赞(品红)
retweet#00ba7c转推 / 成功(绿)
danger#f4212e删除、错误、录制中
warning#ffd400警示、订阅高亮

暗黑模式(双档)

主题背景主文本说明
Dim 暗灰#15202b#f7f9f9默认深色,低对比刺激
Lights out 纯黑#000000#e7e9eaOLED 省电,分割线 #2f3336

字体 / 间距 / 圆角

  • 品牌字体 Chirp(自研,覆盖多语言字重 12 档),Web 端按语言回退系统字体栈;正文基准15px / 1.5,字号阶梯 13 / 15 / 17 / 20 / 24 / 31。
  • 4pt 间距网格:所有留白取自 0 · 4 · 8 · 12 · 16 · 24 · 32 · 48,禁止奇数值。
  • 圆角语义:头像全圆、主按钮胶囊(9999px)、卡片 16px、输入框 8px。
  • 图标:24px 网格、2px 描边、选中态填充实心;全局统一一套线性图标库。

DESIGN TOKEN · JSON 同步源(节选)

{
  "color": {
    "accent": "#1d9bf0", "accentHover": "#1a8cd8",
    "textPrimary": "#0f1419", "textSecondary": "#536471",
    "bgPrimary": "#ffffff", "bgSecondary": "#f7f9f9",
    "divider": "#eff3f4", "like": "#f91880",
    "retweet": "#00ba7c", "danger": "#f4212e"
  },
  "space": [0, 4, 8, 12, 16, 24, 32, 48],
  "radius": { "full": "9999px", "card": "16px", "input": "8px" },
  "font": { "brand": "Chirp", "body": "15px/1.5", "scale": [13, 15, 17, 20, 24, 31] },
  "motion": { "duration": "200ms", "easing": "cubic-bezier(.2,.8,.4,1)" }
}

信息架构(IA)

页面移动端入口Web 端入口核心能力
首页 Home底部 Tab 1左侧导航 1关注流 / 推荐流切换,无限滚动
探索 Explore底部 Tab 2左侧导航 2趋势、话题、搜索一体
通知 Notifications底部 Tab 3左侧导航 3全部 / 提及 / 已验证 过滤
私信 Messages底部 Tab 4左侧导航 4DM 会话列表,E2E 加密入口
AI 助手 Grok底部 Tab 5左侧导航 5对话、图像理解、实时资讯
个人主页 Profile头像 / 侧滑左侧导航底部推文 / 回复 / 媒体 / 点赞 四页签

动效与无障碍

  • 动效:统一 200ms + ease-out 曲线;列表进入、点赞爆炸粒子、底部弹层三套标准动效;骨架屏代替转圈。
  • 对比度:正文对背景 ≥ 4.5:1(WCAG AA),大字号放宽至 3:1。
  • 触控目标:最小 44×44pt;图标按钮扩大命中热区。
  • 读屏:图片支持 ALT 文本(AI 自动建议);全部控件带语义标签;支持系统「减弱动态效果」。

组件清单(节选):Primary / Secondary / Negative 按钮、Tab 页签、Toast、底部弹层 BottomSheet、模态 Modal、骨架屏 Skeleton、无限滚动 Sentinel —— 每个组件在三端同名、同 Token、同交互规范。


05 · API 与统一网关

GATEWAY & GRAPHQL

GraphQL · 幂等 · 限流

所有流量在边缘完成 TLS 终止后进入统一 GraphQL 网关,聚合约 30 个领域服务。字段级鉴权:viewer context(登录态、屏蔽关系、敏感内容偏好)注入每个字段解析器;持久化查询白名单杜绝任意查询注入。写接口全部幂等——客户端生成 Idempotency-Key,服务端 24h 去重窗口。限流采用三级令牌桶:IP → 用户 → 设备,配额通过响应头透出(实现见教程T4)。

协议用途说明
GraphQL主站 / App 全量读路径持久化查询 + 字段级权限
REST v2开放平台、合规接口保持向后兼容与分页游标
WebSocket时间线增量 / 通知 / DM按频道订阅,断线自动补帧

幂等写入 MUTATION

mutation PostTweet($idk: ID!) {
  createTweet(input: {
    idempotencyKey: $idk,          # 客户端生成,服务端 24h 去重
    tweetText: "hello, world",
    replySettings: "everyone",
    media: { ids: ["195391…", "195392…"] }
  }) {
    tweet { id }
    ... on RateLimited { retryAfter }
  }
}

限流响应示例

HTTP/1.1 429 Too Many Requests
x-rate-limit-limit: 300
x-rate-limit-remaining: 0
x-rate-limit-reset: 1770480600
retry-after: 42
content-type: application/problem+json
{ "title": "RateLimited", "detail": "timeline.read" }

06 · 微服务拓扑

SERVICES

~1,200 服务 · 服务网格 · Snowflake

经 2023 年大规模整合后,线上约1,200 个服务:Go 主导无状态边缘与工具服务,JVM(Scala/Java)承载核心领域逻辑,Rust 用于高性能基础设施组件。服务间走自研 RPC + mTLS 服务网格,注册中心基于一致性哈希;无状态优先,一切会话态外置到 Pelikan 缓存层。

核心服务清单

服务职责关键特性
tweetypedia推文读写核心一致性裁决、双写索引与缓存
timeline-mixer时间线组装混排关注流与推荐流统一管线
fanout-service写扩散投递为每个粉丝写入收件箱
socialgraph关注 / 粉丝图谱边存储 + 实时计数
visibility可见性裁决屏蔽、限流、法律移除统一出口
cr-mixer推荐候选混合多路召回融合,已开源
reco-platform特征与模型推理GPU 推理,P99 < 10ms
notify-service通知聚合去重、合并、优先级降级(详见17)
dm-platform私信与群聊MLS 端到端加密(详见18)
media-service上传 / 转码图片即时裁剪、视频多码率(详见19)
search-service查询理解与检索热词缓存、30 天滚动索引(详见16)

Snowflake 全局 ID · 64 bit(实现见教程T2)

1

41

10

12

符号

毫秒时间戳 · 纪元 2010-11-04

机器 ID · 1024 节点

序列 · 4096/ms

单节点理论产能 4096 × 1000 ≈ 410 万 ID/秒;ID 单调递增且可反解生成时间,天然适合作为游标与分片键。部署流程:K8s + canary 1%→5%→25%→100%,指标门禁异常自动回滚。


07 · 时间线与排序引擎

TIMELINE & RANKING

重召回 · 神经排序 · 混合扇出

关注流采用混合扇出:普通用户发推后由 fanout-service 写扩散到每位粉丝的缓存收件箱;粉丝量超阈值的账号改为读扩散,拉取时实时合并。推荐流走「重召回 → 过滤 → 精排 → 混排」四级管线,端到端 P95 控制在 500ms 内。想亲手实现?见教程T1与T5。

一次首页请求的旅程(延迟预算)

  1. 8ms · 接入鉴权
  2. 22ms · 邮箱拉取
  3. 120ms · 6 路并行 · 多路召回
  4. 60ms · 可见性过滤
  5. 180ms · 48M 参数 · 神经精排
  6. 40ms · 启发式混排
  7. 60ms · 数据富化
  8. P95 ≤ 500ms · 响应返回

召回源(六路并行)

召回路信号说明
关注收件箱写扩散缓存基础盘,保证关注内容不缺席
SimClusters兴趣社区相似度约 2 万个稀疏社区向量
TwHIN异构图嵌入用户–推文–实体联合图
RealGraph互动亲密度回复 / 点赞 / 点击加权
Trends地域热点突发话题快速注入
冷启动兜底热门 + 编辑精选新用户与低信号场景

排序漏斗

  1. ~2,000 条 · 6 路并行 · 召回候选
  2. ~500 条 · 屏蔽 / 去重 / 法律 · 可见性过滤
  3. 48M 参数 · 多任务打分 · 神经精排
  4. 40 条 · 作者多样性启发式 · 首屏混排

排序模型为多任务结构,示例目标权重:点赞 1.0 / 回复 1.3 / 转推 1.2 / 关注 3.0 / 负反馈 −8.0。线上以「重度用户活跃天数」为北极星指标做 A/B 决策;混排层强制同作者连续不超过 2 条、按节奏穿插视频。


08 · 存储体系

DATA & STORAGE

MySQL · Manhattan · Pelikan

数据层按访问模式拆分:事务归 MySQL、低延迟 KV 归 Manhattan、热点归 Pelikan、二进制归 Blobstore。核心原则是「读路径必须命中缓存」,缓存整体命中率长期维持在 97% 以上。缓存层的工程细节(穿透 / 击穿 / 雪崩)见教程T3。

存储矩阵

系统类型承载数据关键特性
MySQL分库分表 RDBMS推文主数据、用户资料user_id 哈希分片,3 副本半同步
Manhattan自研分布式 KV收件箱、计数、会话SSD + RAM 分层,P99 < 5ms
Pelikan统一缓存层热点推文、图谱边、会话Twemcache 演进,命中率 > 97%
Blobstore对象存储图片 / 视频 / 语音纠删码 EC,跨区域复制
Kafka事件主干行为流、审计日志3 副本,按用户哈希分区
Elasticsearch检索索引推文全文、用户搜索30 天热索引 + 冷归档

一致性矩阵

操作一致性级别实现
发推后作者刷新强一致(read-your-writes)写后读主库 / 旁路标记
他人时间线可见最终一致 ≤ 3s异步扇出 + 缓存失效广播
点赞 / 转推计数最终一致(±1s)增量合并写,Kafka 聚合
DM 会话因果一致会话级序列号 + 确认回执

备份与容灾:逻辑备份 + binlog 增量;跨区域异步复制 RPO < 30s;每季度切换演练,RTO < 15min。


09 · 实时流式管道

STREAMING PIPELINE

Kafka · Flink · 事件驱动

事件驱动是 X 的神经系统:所有行为(发推、互动、曝光、关注)以事件形式进入Kafka 主干,由 Flink 集群完成实时计数、趋势窗口与风控特征计算,同时在 5 分钟内把「曝光 ↔ 互动」按 request_id join 成训练样本入湖,供排序模型持续迭代。动手搭一条消费管道见教程T7。

核心 Topic

Topic峰值吞吐分区策略下游消费方
tweet.create9.4K /s512 · user_id 哈希扇出、索引、风控
engagement.events900K /s1024计数、样本拼接
impression.stream3.5M /s2048样本拼接、指标
social.follow12K /s128图谱、推荐
abuse.signals260K /s512实时风控模型

可靠性设计

at-least-once + 幂等 sink · Flink checkpoint · Schema Registry 契约 · 反压与死信队列 · 消费滞后 SLO < 2s

互动事件样例

{
  "event": "tweet.engagement",
  "type": "favorite",
  "tweet_id": 1953921047311282176,
  "user_id": 88214526,
  "request_id": "r-7f3c…",        // 与曝光事件 join 成训练样本
  "ts_ms": 1770480600123,
  "surface": "home_timeline",
  "experiment": ["ranker_v42", "ui_density_b"]
}

10 · 基础设施与交付

INFRASTRUCTURE

多云 · Kubernetes · IaC

基础设施已从自有机房迁移为多云架构:AWS 为主、GCP 为辅,媒体负载保留部分自持节点。Kubernetes 联邦按节点池(计算 / 内存 / GPU)调度,GPU 集群同时服务推荐推理与 AI 训练。全量环境 Terraform 化,漂移巡检每日执行。

环境区域用途
prod-usus-east / us-west主生产,全量服务
prod-eueu-west数据驻留合规 + 就近服务
prod-apap-northeast亚太只读副本 + 边缘回源
stagingus-east预发,镜像生产拓扑
edge40+ POP缓存 / 安全 / 媒体就近分发

交付流水线

Mono-repo + 增量构建 → 单元测试与契约测试 → 镜像构建与 SBOM 生成 →canary 1%→5%→25%→100%,每级由指标门禁自动裁决,异常 5 分钟内自动回滚。容量规划由历史负载预测模型驱动,批处理与训练任务使用 Spot 实例压低成本。

成本治理:2023 年整合关闭大量冗余服务与机房,服务器规模显著收缩;此后持续做 right-sizing 与存储分层(热 / 温 / 冷)。下游故障时的自保机制见教程T15 熔断与降级。


11 · 边缘网络与媒体

EDGE & MEDIA

Anycast · HTTP/3 · AVIF

全球40+ Anycast POP承担 TLS 终止、WAF、Bot 评分与缓存。媒体是不可变内容,天然适合边缘缓存——命中率长期 >96%;动态 API 请求走优化专线回源。图片 URL 参数化,边缘按需即时裁剪转码;视频上传后进入转码矩阵(HLS/DASH,6 档码率),分片预热到各 POP;直播走 LL-HLS,端到端约 3s。媒体在源站侧的完整加工流程见19 媒体处理管线。

URL 即转码指令

原图  https://pbs.twimg.com/media/GBxK2…?format=jpg&name=4096x4096    3.8 MB
边缘  https://pbs.twimg.com/media/GBxK2…?format=avif&name=900x900     38 KB
视频  https://video.twimg.com/ext_tw_video/1953…/pu/vid/avc1/1280x720/seg-3.m4s  (HLS 分片)

媒体缓存命中 > 96% · HTTP/3 · QUIC · 签名 URL 防盗链 · Origin Shield 二级回源 · 边缘 JS 挑战 · Bot 评分


12 · 可观测性

OBSERVABILITY

Metrics · Tracing · SLO

三支柱体系:Metrics(自研时序库,约 1,200 万时序,10s 粒度)、Tracing(全链路 1% 采样,错误与慢请求 100% 全采)、Logs(结构化 JSON,热存 7 天、冷归档 90 天)。告警按 P1/P2/P3 分级:P1 立即呼叫值班,P2 出工单,P3 仅观察。落地代码见教程T8。

SLO 与错误预算(30 天窗口)

服务SLO错误预算超限动作
统一网关99.95%21.6 min冻结非必要发布
时间线99.90%43 min降级为关注流兜底
媒体分发99.95%21.6 min切换备用回源链路
DM99.90%43 min只读模式预案

P1 MTTA < 5min · P1 MTTR < 30min · 无责复盘周会 · Runbook 随告警附送 · RUM 10% 采样


13 · 安全与合规

SECURITY & COMPLIANCE

2FA · E2E · 风控

安全设计覆盖身份、传输、内容治理与数据合规四层。DM 采用MLS 协议端到端加密,密钥定期轮换,服务端零知识(协议细节见18);内容治理坚持「言论自由,触达受限」——高风险内容不删除但降权分发。

威胁与对策矩阵

威胁对策
账号盗用2FA(TOTP / WebAuthn 通行密钥)+ 异地登录挑战
接口滥用 / 爬虫设备指纹 + 行为评分 + 梯度限流(429 响应头透出配额)
垃圾与操纵实时风控模型 + 可见性分级降级
DM 窃听MLS 端到端加密 · 服务端零知识
数据泄露PII 字段级加密 + KMS 密钥托管 + 最小权限
合规要求GDPR / CCPA 数据导出与删除,SLA 30 天;审计日志不可篡改
供应链攻击SBOM + SLSA L3 构建溯源 + 依赖准入

Bug Bounty:关键漏洞设高额赏金,7×24 应急响应;白帽提交平均首次响应 < 24h。加密实践见教程T12。


14 · 性能预算

PERFORMANCE BUDGET

P75/P95 · CI 门禁 · RUM

所有体验指标以分位数而非均值治理,预算写入 CI 门禁:Lighthouse CI + bundle diff 超 5% 即阻断合并;线上 RUM 按地域 / 网络制式分位看板持续跟踪,性能 OKR 与团队发布节奏挂钩。

预算总表

指标预算口径
FCP≤ 1.5sP75 · RUM
LCP≤ 2.0sP75 · RUM
TTI≤ 3.0s中端机(Moto G 档位)
时间线 APIP95 ≤ 250ms网关出口
首屏端到端P99 ≤ 800ms冷缓存最坏路径
首屏 JS≤ 180KB gz路由首包
图片 TTFBP75 ≤ 80ms边缘命中
崩溃率≤ 0.05%双端会话级

15 · 架构演进路线

ROADMAP

2006 → 2026

  • 2006:Ruby on Rails 单体上线,五个月完成 MVP。
  • 2008 – 2012:去 Rails 化:JVM + SOA,告别「Fail Whale」时代。
  • 2010:Snowflake 全局 ID 与大规模缓存体系上线。
  • 2013:自研 KV 存储 Manhattan 成为低延迟数据主干。
  • 2017:Web 全面 React + GraphQL 重写,首屏提速 3 倍。
  • 2019:推荐算法透明化,cr-mixer 等核心组件开源。
  • 2023:成本重构:服务与服务器大规模整合,迁移多云,关闭冗余机房。
  • 2024:长文 / 长视频 / 创作者分成上线,媒体管线重构。
  • 2025:支付牌照落地、X Money 试点,AI 深度融入时间线与搜索。
  • 2026:多云双活与「Everything App」:通讯 · 支付 · 内容 · AI 一体化。

架构的终点不是某个技术栈,而是让数亿人同时说话时,系统依然稳定的那种韧性。


16 · 搜索系统深挖

SEARCH

查询理解 · 滚动索引 · LTR

搜索意图分为三类:用户、话题、实时新闻。写路径消费tweet.create事件,富化实体信息(用户、媒体、话题标签)后构建 30 天滚动热索引,更早内容转入冷归档;读路径经过查询理解、混合检索、多因子排序三级,热词与结果页均有二级缓存(命中率约 78%)。键入联想(typeahead)走独立集群,P95 < 80ms。

搜索请求四级管线

阶段组件延迟预算
查询理解分词 / 拼写纠错 / 意图分类 / 改写15ms
混合检索ES 热索引(BM25)+ 向量 ANN120ms
排序LTR 模型:相关性 + 互动 + 时效 + 个性化80ms
渲染高亮、媒体富化、作者批量回填40ms
合计—P95 ≤ 300ms

查询改写示例

输入:   怎么学习分布式
改写:   +分布式 +系统 +入门 -广告 -营销号
意图:   topic
扩展:   同义词[分布式 → 分库分表, 多机]   语言: zh

时效性在新闻意图下是一等公民:1 小时内的内容时效因子权重 ×3;用户意图则直接命中资料卡 + 近期推文。动手实现见教程T10 手写倒排索引。


17 · 通知系统设计

NOTIFICATIONS

聚合窗口 · 优先级 · 多通道

通知系统完全事件驱动:任何互动(点赞 / 关注 / 提及)成为事件,经「风控过滤 → 优先级打分 → 聚合窗口 → 渲染 → 通道路由」管线投递。核心设计是聚合与频控——「10 人赞了你的帖子」只产生一条通知;提及与回复永不聚合(高价值),点赞按 10 分钟窗口合并;全局频率上限与免打扰时段防止通知风暴。

  1. Kafka · 事件接入
  2. spam 拦截 · 风控过滤
  3. P0–P3 · 优先级打分
  4. 10min 合并 · 聚合窗口
  5. 文案 + 头像堆叠 · 渲染
  6. 推送/应用内/邮件 · 通道路由
  7. 失败降级重投 · 回执闭环

通知类型策略表

类型触发聚合策略优先级通道
提及 / 回复有人 @ 或回复我不聚合P0推送 + 应用内
引用引用我的帖子不聚合P0推送 + 应用内
转推转推我的帖子10 分钟窗口合并P1推送 + 应用内
点赞赞了我的帖子10 分钟窗口合并P2应用内角标
新关注有人关注我每小时聚合P2推送
系统推荐运营 / 算法每日摘要P3仅邮件

回执形成闭环:推送失败降级为应用内角标;角标 24h 未读可再降级为邮件摘要;用户可按类型配置开关——通知设置页本质是一台路由规则编辑器。聚合的实现简化版见教程T11。


18 · DM 消息与端到端加密

DM & MLS

MLS · 密钥树 · 零知识

会话式模型:会话列表 + 消息时间线两级结构。在线投递走 WebSocket 实时通道,离线转 APNs/FCM 推送;已读回执与「正在输入…」走短暂通道(不落库)。加密采用MLS(Messaging Layer Security)协议:每个会话维护一棵棘轮密钥树,成员变更时只更新受影响路径的密钥,同时满足前向安全与后向安全;服务器只存密文,零知识。

能力矩阵

能力状态说明
文本 / 表情✔E2E 加密
图片 / GIF✔媒体密钥单独加密
群聊✔MLS 密钥树,成员变更路径更新
多端同步✔每设备独立密钥对
阅后即焚✔客户端定时销毁
服务端内容审查✘无法解密,仅客户端举报通道

加密消息信封

{
  "conv_id": "c-9f2e…",
  "seq": 48213,                 // 会话级单调序号,用于排序与去重
  "sender_device": "d-77ab…",
  "key_epoch": 12,              // 密钥树代次,接收方据此取解密钥匙
  "ciphertext": "base64…",      // AES-GCM 加密的正文
  "mac": "…"                    // 完整性校验
}

E2E 的代价:服务端搜索、审查与多端漫游都变得更昂贵。X 的选择是「内容加密 + 元数据(会话关系、时间戳)可见」——投递优化与合规调查依赖元数据。简化实践见教程T12。


19 · 媒体处理管线

MEDIA PIPELINE

转码矩阵 · 预热 · 直播

一张图 / 一段视频从上传到被看见要经过五道工序:分片断点上传 → 校验与扫描 → 缩略图提取 → 转码矩阵 → 预热分发。视频在转码矩阵中产出 6 档码率的 HLS/DASH,客户端按带宽自动选档(ABR);GIF 统一转成 MP4 循环视频节省约 60% 带宽;图片在源站只保留 4096 原图与少数标准尺寸,其余规格由边缘按指令即时处理(见11 边缘与媒体)。

  1. 签名直传 S3 · 分片断点上传
  2. 格式/病毒/违规哈希 · 校验与扫描
  3. 主色 + 去 EXIF · 缩略图提取
  4. 6 档码率 + AVIF · 转码矩阵
  5. 按粉丝地域推 POP · 预热分发

视频码率阶梯

分辨率编码码率场景
1080pH.264 / HEVC6.0 MbpsWi-Fi 高清
720pH.2643.0 Mbps默认档
480pH.2641.2 Mbps4G
288pH.2640.5 Mbps弱网兜底
音轨AAC64 kbps独立分片

LL-HLS 直播延迟 ~3s · WebRTC 连麦 · AVIF 体积 −35% · 预热命中率 92%


20 · 机器学习平台与 AI

ML PLATFORM & GROK

特征仓库 · 训练 · 推理

平台分三层:特征仓库(在线 / 离线共用一套定义,杜绝训练-推理不一致)、训练(样本拼接复用09 流式管道,GPU 集群日增量 + 周全量训练)、在线推理(模型注册表 → canary → 全量,P99 < 10ms)。Grok 大模型运行在独立推理集群上,承担帖子撰写建议、搜索答案生成、图像理解与审核辅助。

模型矩阵

模型 / 场景输入延迟 SLA更新频率
时间线精排用户 × 推文特征(48M 参数)P99 10ms每日
粗排双塔embedding 点积P99 3ms每日
风控评分实时事件流P99 5ms每小时
图像理解视觉编码器P99 400ms每周
Grok 对话LLM 流式生成TTFT < 1.5s每周

铁律:在线服务与离线训练必须命中同一份特征快照(point-in-time correctness),否则离线指标漂亮、上线即翻车——「训练-推理偏差」是推荐系统最常见的深坑。


21 · 实验平台

EXPERIMENTATION

正交分层 · holdout · CUPED

一切排序与 UI 变更必须经过 A/B。分桶用user_id + 实验 id的稳定哈希,保证同一用户全程看到同一版本;实验按正交层组织——排序层与 UI 层互不干扰;保留 5% 长期 holdout 组衡量长期效应。指标计算依赖曝光 × 互动的样本拼接(见09),并用 CUPED 方差缩减压缩实验周期。

稳定分桶(简化版,完整实现见 T13)

const bucket  = (userId, expId) => fnv1a(`${userId}:${expId}`) % 100;
const variant = b => b < 50 ? 'control' : b < 95 ? 'treatment' : null;  // 5% holdout

决策流程

阶段动作产出
假设明确改动与预期影响实验文档
功效计算估算最小可检测效应与样本量预计时长
放量1% → 10% → 50% 阶梯护栏指标监控
决策显著性 + 北极星 + 护栏三查全量 / 回滚

护栏指标(崩溃率、举报率、取关率)单侧报警——只要显著变差立即停实验,不看收益;北极星是「重度用户活跃天数」而非短期点击。分桶与指标计算见教程T13。


22 · 变现与支付

MONETIZATION & X MONEY

订阅 · 分成 · 双式记账

四条业务线:Premium 订阅(分层会员)、创作者广告分成(按回复区产生的可验证曝光结算,需过滤机器人)、广告系统(实时竞价 + 受众定向 + 频次控制)、X Money(P2P 转账与汇款,多地区牌照)。技术重点:计费幂等、账本与对账、反作弊、地区税务合规。

业务收益模型技术要点
Premium月费 / 年费订阅订阅生命周期、Apple/Google/Stripe 多渠道、区域定价
创作者分成广告收益分成可验证曝光计数、反作弊过滤、T+30 结算
广告CPM / CPC 竞价竞价 P99 50ms、定向隐私合规、频次上限
X Money转账 / 汇兑手续费KYC/AML、双式记账、日终对账

支付是强一致领域——与时间线的最终一致世界观截然不同:资金要求同步双式记账、事件 at-least-once + 对账任务兜底。同一个平台并存两种一致性策略,这是「架构服从业务」最直观的体现(对照08 一致性矩阵)。


T1 · 教程:迷你时间线:扇出与收件箱

FANOUT & INBOX

写扩散 · 读扩散 · 游标分页

难度 ★★☆ · 耗时 3–4h · 栈 Node.js + Redis

原理:写扩散(fan-out on write)在发推时把推文 ID 推入每位粉丝的收件箱列表,读路径只需取自己的收件箱,复杂度 O(1);粉丝量巨大的账号改为读扩散,拉取时实时合并,避免一次写入百万级列表。分页使用 Snowflake ID 作游标,天然按时间倒序且无偏移漂移。

NODE.JS · 发布 = 写扩散

const BIG_V = 10_000;                          // 大 V 阈值(粉丝数)

async function postTweet(userId, text) {
  const tweet = { id: snowflake(), text, author: userId, ts: Date.now() };
  await kv.set(`tweet:${tweet.id}`, tweet);            // ① 主数据落盘
  await redis.lpush(`user:${userId}:tweets`, tweet.id); // ② 个人主页列表

  const followers = await graph.followers(userId);      // ③ 粉丝列表
  if (followers.length > BIG_V) {
    await redis.sadd('bigv:active', userId);            // ④ 大 V:读扩散登记
  } else {
    await Promise.all(followers.map(f =>
      redis.lpush(`inbox:${f}`, tweet.id)
        .then(() => redis.ltrim(`inbox:${f}`, 0, 799))  // ⑤ 邮箱封顶 800 条
    ));
  }
  bus.publish('tweet.create', tweet);                   // ⑥ 事件总线通知下游
  return tweet;
}

NODE.JS · 读取 = 组装时间线

async function homeTimeline(userId, cursor = 0, count = 20) {
  let ids = await redis.lrange(`inbox:${userId}`, cursor, cursor + count - 1);
  const bigvs = await redis.smembers('bigv:active');    // 大 V 读扩散补偿
  ids = ids.concat(await recentTweetsOf(bigvs, count));
  ids = [...new Set(ids)].sort((a, b) => b - a);        // Snowflake 即时间序
  const page = ids.slice(0, count);
  return { entries: await hydrate(page), nextCursor: cursor + page.length };
}

async function hydrate(ids) {                            // 批量回填,杜绝 N+1
  const tweets = await kv.mget(ids.map(i => `tweet:${i}`));
  const authors = await kv.mget([...new Set(tweets.map(t => t.author))]
    .map(u => `user:${u}`));
  return tweets.map(t => ({ ...t, author: authors[t.author] }));
}
  • 邮箱封顶:ltrim 保证单用户收件箱有界,控制内存与扇出成本。
  • 游标 ≠ 页码:以 ID 定位,插入新推文不会造成翻页重复。
  • hydrate 批量化:一次 mget 拉齐作者与计数,避免逐条查询。

动手练习:① 把同步扇出改为消费 Kafka 的异步扇出(见T7),观察发推延迟变化;② 在 sort 之后插入一个简单 ranker(点赞数加权),体会排序对停留的影响;③ 压测 1 万粉丝用户发推,统计扇出耗时分布。


T2 · 教程:Snowflake 分布式 ID

DISTRIBUTED ID

64bit · 位运算 · 时钟回拨

难度 ★☆☆ · 耗时 1–2h · 栈 Node.js(BigInt)

原理:64 位 = 1 符号 + 41 位毫秒时间戳(可用 69 年)+ 10 位机器号(1024 节点)+ 12 位序列号(每毫秒 4096 个)。无需数据库自增即可全局唯一、趋势递增,且可从 ID 反解生成时间。

NODE.JS · 生成与反解

const EPOCH = 1288834974657n;              // 2010-11-04,自定义纪元
let seq = 0n, lastTs = -1n;

function snowflake(machineId) {            // machineId ∈ [0, 1023]
  let ts = BigInt(Date.now());
  if (ts === lastTs) {
    seq = (seq + 1n) & 4095n;              // 同毫秒内序列号自增取模
    if (seq === 0n) {                      // 序列耗尽 → 自旋等下一毫秒
      while (ts <= lastTs) ts = BigInt(Date.now());
    }
  } else {
    seq = 0n;
  }
  if (ts < lastTs) throw new Error('clock moved backwards'); // 时钟回拨保护
  lastTs = ts;
  return ((ts - EPOCH) << 22n) | (BigInt(machineId) << 12n) | seq;
}

const parseTime = id => new Date(Number(id >> 22n) + Number(EPOCH));
console.log(parseTime(1953921047311282176n));   // 从任意推文 ID 反解时间
  • 容量:单节点 4096 × 1000 ≈ 410 万 ID/秒,远超单机写入能力。
  • 时钟回拨:小回拨可等待追平,大回拨必须拒绝服务或切换机器号,绝不可静默生成。
  • 机器号分配:生产中由配置中心 / K8s StatefulSet 序号下发,避免冲突。

动手练习:① 把 10 位机器号拆成 5 位机房 + 5 位 worker;② 写基准测试统计每秒生成量;③ 对比 UUID v7 的长度、索引友好度与隐私泄露风险。


T3 · 教程:多级缓存与三大故障

CACHING PATTERNS

Cache-Aside · 击穿 · 雪崩

难度 ★★☆ · 耗时 2–3h · 栈 Node.js + Redis

原理:X 的读路径 97% 命中缓存。标准模式是 Cache-Aside:读未命中时回源并回填;写时「先更新库,再删缓存」。三个经典故障——穿透(查不存在的 key)、击穿(热点 key 过期瞬间)、雪崩(大批 key 同时过期)——分别用空值哨兵、singleflight 合并请求、TTL 抖动化解。

NODE.JS · CACHE-ASIDE + 防护三件套

const NULL = Symbol('NULL_SENTINEL');
const inflight = new Map();

async function getTweet(id) {
  const key = `tweet:${id}`;
  let v = cache.get(key);
  if (v === undefined) {                                 // 未命中
    v = await singleflight(key, async () => {           // ② 击穿:合并并发回源
      return (await db.getTweet(id)) ?? NULL;           // ① 穿透:空值哨兵
    });
    cache.set(key, v, 300_000 + Math.random() * 60_000); // ③ 雪崩:TTL 抖动
  }
  return v === NULL ? null : v;
}

function singleflight(key, fn) {                         // 同 key 只放一个请求回源
  if (!inflight.has(key)) {
    inflight.set(key, fn().finally(() => inflight.delete(key)));
  }
  return inflight.get(key);
}

async function updateTweet(id, patch) {
  await db.updateTweet(id, patch);     // 先写库
  cache.del(`tweet:${id}`);            // 再删缓存(可加延迟双删防脏读)
}
故障现象对策
穿透大量查询不存在的 ID,直达数据库空值哨兵 + 布隆过滤器
击穿热点 key 过期瞬间并发打爆 DBsingleflight / 互斥重建 / 逻辑过期
雪崩大批 key 同时失效TTL 加随机抖动 + 多级缓存兜底

动手练习:① 给 cache 加 LRU 淘汰并统计命中率曲线;② 模拟热点 key 过期,对比有无 singleflight 时 DB 的 QPS 峰值;③ 比较「删缓存」与「写缓存」两种更新策略的脏读窗口。


T4 · 教程:令牌桶限流

RATE LIMITING

Token Bucket · 分级 Key · 429

难度 ★☆☆ · 耗时 1–2h · 栈 Node.js(+Redis 进阶)

原理:令牌以固定速率注入桶中,请求到来时取走令牌,桶空即拒。相比漏桶,令牌桶允许一定程度的突发,更贴近真实用户体验。网关按 IP → 用户 → 设备多级 Key 叠加限流,并把配额写进响应头。

NODE.JS · 令牌桶与网关接入

class TokenBucket {
  constructor(rate, capacity) {          // rate: 每秒补充数
    this.rate = rate; this.cap = capacity;
    this.tokens = capacity; this.ts = Date.now();
  }
  take(n = 1) {
    const now = Date.now();
    this.tokens = Math.min(this.cap, this.tokens + (now - this.ts) / 1000 * this.rate);
    this.ts = now;
    if (this.tokens >= n) { this.tokens -= n; return true; }
    return false;
  }
}

const buckets = new Map();
const bucket = (key, rate, cap) =>
  buckets.get(key) ?? buckets.set(key, new TokenBucket(rate, cap)).get(key);

app.use('/timeline', (req, res, next) => {
  const b = bucket(`u:${req.userId}`, 300, 300);     // 用户级
  const ok = b.take() && bucket(`ip:${req.ip}`, 100, 200).take(); // 叠加 IP 级
  res.set('x-rate-limit-remaining', Math.floor(b.tokens));
  if (!ok) { res.set('retry-after', 42); return res.status(429).end(); }
  next();
});
  • 分布式:多实例场景把计数放进 Redis,用 Lua 脚本保证「读-改-写」原子性。
  • 分级:IP 防爬虫、用户保公平、设备防多开,三层独立又叠加。
  • 客户端义务:收到 429 必须按 retry-after 指数退避,禁止硬重试。

动手练习:① 用 Redis + Lua 重写 take(),压测分布式一致性;② 实现滑动窗口日志算法并对比两者对突发流量的表现;③ 给客户端加退避重试,画出 QPS 恢复曲线。


T5 · 教程:推荐召回与排序

RECALL & RANKING

多路召回 · 多目标打分 · 重排

难度 ★★★ · 耗时 4–6h · 栈 Node.js / Python

原理:推荐 = 漏斗。先用多路召回从亿级内容池捞出约 1,000 条候选,再用模型对候选打分排序,最后用启发式规则重排保证多样性。本节用一个「兴趣向量 + 余弦相似度」的简化模型复现这条管线。

NODE.JS · 召回 → 打分 → 重排

// ① 用户兴趣向量 = 历史互动推文的主题向量加权平均(简化版 SimClusters)
function userVector(uid) {
  const hist = engagementHistory(uid);                // 点赞/转推记录
  return normalize(sum(hist.map(t => weight(t) * t.topicVec)));
}
const cos = (a, b) => dot(a, b) / (norm(a) * norm(b));

// ② 多路召回(并行执行,互相兜底)
async function recall(uid) {
  const [follow, interest, hot] = await Promise.all([
    inbox(uid, 400),                                  // 关注路
    topKByScore(candidatePool(), t => cos(userVector(uid), t.topicVec), 400), // 兴趣路
    trending(regionOf(uid), 200),                     // 热门路
  ]);
  return dedupe([...follow, ...interest, ...hot]);    // ~1000 候选
}

// ③ 打分:多目标加权(简化版 48M 参数模型)
const W = { like: 1.0, reply: 1.3, retweet: 1.2, follow: 3.0, negative: -8.0 };
const score = (u, t) => Object.entries(W)
  .reduce((s, [k, w]) => s + w * predict(u, t, k), 0);

// ④ 重排启发式:同作者最多连续 2 条,每 4 条穿插一个视频
function rerank(list) {
  const out = []; let streak = {};
  for (const t of list.sort((a, b) => score(u, b) - score(u, a))) {
    if ((streak[t.author] ?? 0) >= 2) continue;
    streak[t.author] = (streak[t.author] ?? 0) + 1;
    out.push(t);
  }
  return interleaveMedia(out);
}
  • 负反馈权重最高:一次「不感兴趣」要抵消多次点赞,避免标题党绑架时间线。
  • 粗排-精排分离:候选多时用轻量模型粗筛,控制 GPU 成本。
  • 离线评估:用历史互动回放,算 precision@40 / NDCG,再上 A/B(见21 实验平台)。

动手练习:① 把余弦相似度换成真实 ANN 索引(HNSW);② 加入「作者多样性」约束前后对比点击分布;③ 设计一个 1% 流量的 A/B 实验方案,明确北极星指标与护栏指标。


T6 · 教程:GraphQL 网关与 DataLoader

GRAPHQL GATEWAY

Schema · N+1 · 持久化查询

难度 ★★☆ · 耗时 3–4h · 栈 Node.js + graphql

原理:GraphQL 让客户端按需取字段,一次请求聚合多个领域。网关层必须解决两件事:N+1 查询(用 DataLoader 把同一帧内的请求合并成批量查询)与查询滥用(持久化查询白名单,只接受注册过的查询哈希)。

SCHEMA · 最小时间线模型

type Tweet  { id: ID!  text: String!  author: User!  stats: Stats! }
type User   { id: ID!  name: String!  verified: Boolean! }
type Stats  { like: Int!  retweet: Int!  reply: Int! }
type Timeline { entries: [Tweet!]!  nextCursor: String }
type Query  { homeTimeline(count: Int = 20, cursor: String): Timeline! }

NODE.JS · DATALOADER 消除 N+1

import DataLoader from 'dataloader';

// 每请求一个 loader:同一事件循环内的 load() 自动合并成一次批量查询
const userLoader = new DataLoader(async ids => {
  const users = await db.users.findByIds(ids);        // 单条 IN 查询
  return ids.map(id => users.find(u => u.id === id)); // 顺序必须对齐
});

const resolvers = {
  Tweet: {
    author: t => userLoader.load(t.authorId),         // 20 条推文 → 1 次 DB 查询
    stats:  t => countLoader.load(t.id),
  },
  Query: {
    homeTimeline: async (_, { count, cursor }, ctx) => {
      const ids = await timelineSvc.get(ctx.viewer.id, cursor, count);
      return { entries: await hydrate(ids), nextCursor: ids.at(-1) ?? null };
    },
  },
};

// 持久化查询:只放行白名单哈希,拒绝任意 query 字符串
app.post('/graphql', (req, res) => {
  const doc = registry.get(req.body.sha256);
  if (!doc) return res.status(400).json({ error: 'unknown persisted query' });
  return execute({ schema, document: doc, variableValues: req.body.variables });
});
  • 游标分页:nextCursor 用最后一条推文 ID,天然抗并发插入。
  • 字段级权限:viewer context 注入 resolver,屏蔽关系在字段解析时裁决。
  • 深度限制:对查询做最大深度 / 复杂度分析,防止恶意嵌套。

动手练习:① 给 homeTimeline 加一个 subscription,新推文实时推送;② 实现一个权限中间件,未登录用户隐藏 stats.reply;③ 对比 DataLoader 开 / 关时的 SQL 条数与总耗时。


T7 · 教程:Kafka 事件管道

EVENT PIPELINE

分区 · 幂等消费 · 死信

难度 ★★☆ · 耗时 2–3h · 栈 Node.js + KafkaJS

原理:生产者按用户 ID 做分区键,保证同一用户的事件有序;消费者采用 at-least-once + 幂等 sink:处理成功后才提交位点,重放时靠去重表跳过已处理事件;反复失败的消息进死信队列人工介入。

NODE.JS · 生产与消费

// 生产者:key = 用户 ID → 同用户事件落入同一分区,保持顺序
await producer.send({
  topic: 'tweet.create',
  messages: [{ key: String(tweet.authorId), value: JSON.stringify(tweet) }],
});

// 消费者:at-least-once + 幂等 sink
for await (const batch of consumer.subscribe('tweet.create')) {
  for (const msg of batch) {
    const evt = JSON.parse(msg.value);
    if (await dedupe.seen(evt.id)) continue;          // ① 去重(Redis SETNX 24h)
    try {
      await Promise.all([                             // ② 多个下游并行消费
        fanout(evt),                                   //    扇出服务
        searchIndex(evt),                              //    搜索索引
        riskScan(evt),                                 //    风控扫描
      ]);
      await dedupe.mark(evt.id);
    } catch (e) {
      if (msg.retries >= 3) await dlq.send(evt);      // ③ 三次失败进死信
      else throw e;                                    //    否则重试
    }
  }
  await consumer.commit(batch);                        // ④ 全部成功才提交位点
}
语义含义适用场景
at-most-once可能丢,不重复采样指标、日志
at-least-once不丢,可能重复主流场景 + 幂等 sink(X 默认)
exactly-once不丢不重计费、事务型流处理(成本高)

动手练习:① 用 Redis Streams 搭一个迷你版,理解 offset / 消费组概念;② 制造下游故障,观察死信队列与滞后(lag)变化;③ 给消费加背压:处理不过来时主动暂停拉取。


T8 · 教程:可观测性:RED 指标与链路

RED + TRACING

Rate · Errors · Duration

难度 ★☆☆ · 耗时 2h · 栈 Node.js + Prometheus

原理:每个服务先回答三个问题——每秒多少请求(Rate)、多少失败(Errors)、耗时分布(Duration)。用一个中间件就能采集 RED 三件套;再用贯穿全链路的 trace_id 把跨服务调用串成一条链路。

NODE.JS · RED 中间件 + TRACE_ID

app.use((req, res, next) => {
  const t0 = process.hrtime.bigint();
  req.traceId = req.headers['x-trace-id'] ?? crypto.randomUUID(); // 入口生成/继承
  res.on('finish', () => {
    const ms = Number(process.hrtime.bigint() - t0) / 1e6;
    const labels = { route: req.path, status: res.statusCode };
    metrics.counter('api_requests_total').inc(labels);        // Rate + Errors
    metrics.histogram('api_latency_ms').observe(labels, ms);  // Duration 分布
  });
  next();
});

// 调用下游时透传 trace_id,即可拼出全链路
await fetch('http://timeline-svc/get', {
  headers: { 'x-trace-id': req.traceId },
});

// 告警示例(PromQL):5xx 比例超 SLO 即触发
// sum(rate(api_requests_total{status=~"5.."}[5m]))
//   / sum(rate(api_requests_total[5m])) > 0.0005
  • 分位数优先:histogram 而不是 avg,P99 才代表真实痛感。
  • 采样策略:正常流量 1% 采样 trace,错误与慢请求 100% 全采。
  • 错误预算:30 天窗口内 (1 − SLO) × 总分钟数,烧完即冻结发布。

动手练习:① 在 Grafana 画一张 P50/P95/P99 三曲线面板;② 给慢请求(>800ms)自动打日志并附 trace_id;③ 用 k6 制造流量尖峰,验证告警在 1 分钟内触发。


T9 · 教程:学习路线图与书单

LEARNING ROADMAP

入门 · 进阶 · 专家

把本文档当成一张地图:先跑通最小系统,再逐层替换成真实组件。三个阶段,每阶段都有明确产出物,避免「只看不练」。

阶段周期目标行动产出物
入门1–2 月跑通最小闭环HTTP / SQL / 缓存基础;完成 T2、T4可发推、可刷新的 CRUD 小站
进阶3–6 月逼近真实架构完成 T1、T3、T6、T7;引入 Kafka 与 GraphQL带扇出、缓存、限流的迷你 Twitter + 压测报告
专家6–12 月理解规模化取舍完成 T5、T8 与全部进阶 / 扩展教程(T10–T19);读论文;做分片与多区域复制系统设计博客 / 开源贡献 / 故障演练记录

延伸阅读

  • 《Designing Data-Intensive Applications》— 分布式系统取舍的最佳入门框架。
  • Snowflake 官方博客 — 分布式 ID 的原始设计动机。
  • cr-mixer 开源仓库 — 真实的多路召回工程实现。
  • Zipkin 论文 — 分布式链路追踪的起源(诞生于 Twitter)。
  • Manhattan, our real-time, multi-tenant distributed database — 自研 KV 的工程细节。
  • RFC 9420(MLS)— 群聊端到端加密协议规范。

建议节奏:每周至少 2 个晚上写代码 + 1 篇复盘笔记;每完成一个教程,回头对照本文档 01–27 章,标注「我现在理解了哪条设计决策、为什么」。


T10 · 进阶:手写倒排索引与搜索

INVERTED INDEX

分词 · posting list · BM25

难度 ★★☆ · 耗时 3h · 栈 Node.js

原理:搜索的核心是倒排索引——把词元映射到包含它的文档 posting list。建索引时对每条推文分词入表,查询时取各词 posting list 求交集,再用 BM25 打分、叠加时效与互动权重。生产环境由 ES 完成这一切,但手写一遍才能理解分词器、posting list、打分三要素(对应16 搜索系统)。

NODE.JS · 建索引与查询

const index = new Map();                       // 词元 → posting list
const docs  = new Map();                       // id → 文档

function addDoc(doc) {
  docs.set(doc.id, doc);
  for (const [pos, w] of tokenize(doc.text).entries()) {
    if (!index.has(w)) index.set(w, []);
    index.get(w).push({ id: doc.id, pos });    // 记录位置,支持短语查询
  }
}

function search(q, k = 20) {
  const lists = tokenize(q).map(w => index.get(w) ?? []);
  const ids = intersect(lists.map(l => new Set(l.map(x => x.id)))); // 从最短表开始交
  return [...ids]
    .map(id => ({ id, score: bm25(docs.get(id), q) * freshBoost(docs.get(id)) }))
    .sort((a, b) => b.score - a.score)
    .slice(0, k);
}
  • 中文分词:无空格语言需分词器(ngram 简单、词典法准确),直接影响召回。
  • 交集优化:从最短 posting list 起步,逐个过滤,避免大表全扫。
  • 时效衰减:freshBoost 用指数衰减,新闻意图下加大权重。

动手练习:① 支持 @用户 与 #话题 前缀语法;② 加「你是不是想找」纠错(编辑距离 ≤ 1);③ 对比 ngram 与分词法的命中率与索引体积。


T11 · 进阶:通知聚合与优先级

NOTIFICATION FANOUT

聚合窗口 · 去重 · 延迟投递

难度 ★☆☆ · 耗时 2h · 栈 Node.js + Redis

原理:高频低价值通知(点赞、关注)必须聚合,否则用户会关掉通知。通用做法是聚合窗口:窗口期内同一目标的事件累计在 Redis 集合里,窗口到期统一 flush,渲染成「某某和其他 9 人喜欢了你的帖子」;高价值通知(提及 / 回复)跳过窗口直接投递(对应17 通知系统)。

NODE.JS · 窗口聚合

const WINDOW = 10 * 60_000;                    // 10 分钟聚合窗口

async function onEvent(evt) {
  if (evt.type === 'mention' || evt.type === 'reply') return deliver(evt); // P0 直发
  const key = `agg:${evt.targetId}:${evt.type}`;
  const isNew = await redis.sadd(key, evt.actorId);   // 参与者集合,天然去重
  await redis.pexpire(key, WINDOW);
  if (isNew === 1) scheduler.push({ key, at: Date.now() + WINDOW }); // 首事件定时
}

async function flush({ key }) {
  const actors = await redis.smembers(key);
  if (!actors.length) return;
  await redis.del(key);
  deliver({                                     // 渲染聚合文案
    text: actors.length === 1
      ? `${name(actors[0])} 赞了你的帖子`
      : `${name(actors[0])} 和其他 ${actors.length - 1} 人赞了你的帖子`,
    icons: avatars(actors.slice(0, 3)),         // 头像堆叠最多 3 个
  });
}
  • 去重:用集合存 actorId,同一人重复点赞只计一次。
  • 幂等 flush:定时任务可能重放,flush 前先原子取走集合。
  • 最后一道闸:免打扰时段与按类型开关在投递前再做一次过滤。

动手练习:① 加优先级队列,让 mention 抢占聚合任务;② 模拟一次 1 万点赞风暴,验证通知条数从 1 万压缩到 1;③ 实现「静默时段」并测试跨时区用户。


T12 · 进阶:端到端加密简化实现

E2E ENCRYPTION

ECDH · 棘轮 · 前向安全

难度 ★★★ · 耗时 4h · 栈 Node.js + tweetnacl

原理:E2E 的核心是两件事——密钥协商(ECDH/X3DH 让两端算出共享密钥,服务器不参与)与密钥棘轮(每轮通信都推进密钥,即使某把密钥泄露也不影响之后的消息,即前向安全)。MLS 在群聊中用密钥树把成员变更的轮换成本从 O(N) 降到 O(log N)(对应18 DM 消息)。

NODE.JS · 会话骨架(简化)

// ① 握手:ECDH 派生会话根密钥(生产需 X3DH + 预密钥包)
const root = HKDF(ECDH(alice.priv, bob.pub), salt);

// ② 棘轮:每次收发都推进根密钥 → 前向安全
function ratchet(state, theirNewPub) {
  const dh = ECDH(state.ephPriv, theirNewPub);
  state.root = HKDF(state.root, dh);
  state.ephPriv = keygen();                      // 自己的临时密钥也换新
  return {
    sendKey: HKDF(state.root, 'send'),           // 收发密钥分离
    recvKey: HKDF(state.root, 'recv'),
  };
}

// ③ 消息:AES-GCM 认证加密 = 保密 + 完整性
const enc = (key, msg) => AES_GCM_seal(key, nonce(), msg);
  • 前向安全:任何一把密钥泄露,历史消息仍安全——这是 E2E 与 TLS 的本质区别。
  • 服务器零知识:只转发密文与元数据,无法解密内容。
  • 中间人防护:双方比对安全码(密钥指纹)确认身份。

动手练习:① 用 tweetnacl 的 box/open 跑通真实加解密;② 模拟中间人替换公钥,理解签名的作用;③ 阅读 RFC 9420 中群聊密钥树章节,画出成员退出时的路径更新。


T13 · 进阶:A/B 分桶与指标计算

EXPERIMENTATION

稳定哈希 · 正交层 · holdout

难度 ★☆☆ · 耗时 1–2h · 栈 Node.js

原理:分桶只有一个要求——稳定:同一用户每次请求、每台设备都必须落在同一组,所以不能用随机数,而要把 user_id 与实验 id 一起哈希取桶;多实验用正交层组织,层 id 参与哈希,保证实验组合均匀分布;再预留 holdout 组做长期观察(对应21 实验平台)。

NODE.JS · 稳定分桶

function fnv1a(str) {                              // 快速非加密哈希
  let h = 0x811c9dc5;
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = Math.imul(h, 0x01000193) >>> 0;
  }
  return h;
}

function variant(userId, exp) {                    // exp: { layer, id, control: 50, treat: 45 }
  const b = fnv1a(`${exp.layer}:${exp.id}:${userId}`) % 100;
  if (b < exp.control) return 'control';
  if (b < exp.control + exp.treat) return 'treatment';
  return null;                                     // holdout,不参与实验
}

// 指标:互动率提升 + 置信区间(简化)
const lift = (t, c) => (t.mean - c.mean) / c.mean;
  • 不要按请求分桶:否则用户体验闪烁,指标也失去意义。
  • AA 空跑校验:实验未开启时两组指标应无显著差异,否则分桶有偏。
  • 护栏单侧报警:崩溃率、举报率只要显著变差立即停,不与收益对冲。

动手练习:① 用 10 万模拟用户验证分桶均匀性(卡方检验);② 加层正交校验,统计层间组合分布;③ 用 bootstrap 给点击率差值算 95% 置信区间。


T14 · 进阶:虚拟长列表:时间线渲染

VIRTUAL LIST

Windowing · 缓冲 · 回收

难度 ★★☆ · 耗时 2–3h · 栈 React

原理:时间线可能有上千条,但一屏只看得见 5–10 条。全量渲染会让 DOM 爆炸、滚动掉帧。虚拟列表只渲染「可视区 ± 缓冲」的窗口:容器用总高度撑开保证滚动条真实,条目绝对定位按序摆放,滚动时重算窗口起止下标——DOM 节点数从 N 降到 ~20。

REACT · 定高虚拟列表

function VirtualTimeline({ items, itemH = 140, viewH = 800 }) {
  const [scrollTop, setScrollTop] = useState(0);
  const BUFFER = 5;                                             // 上下各缓冲 5 条
  const start = Math.max(0, Math.floor(scrollTop / itemH) - BUFFER);
  const end   = Math.min(items.length, start + Math.ceil(viewH / itemH) + BUFFER * 2);

  return (
    <div className="viewport" style={{ height: viewH, overflow: 'auto' }}
         onScroll={e => setScrollTop(e.currentTarget.scrollTop)}>
      <div style={{ height: items.length * itemH, position: 'relative' }}>
        {items.slice(start, end).map((it, i) => (
          <TweetCard key={it.id} data={it}
            style={{ position: 'absolute', top: (start + i) * itemH, width: '100%' }} />
        ))}
      </div>
    </div>
  );
}
  • 动态高度:先用估计值占位,渲染后用 ResizeObserver 实测回填修正。
  • 缓冲防白屏:快速滚动时预留窗口外的若干条,牺牲少量节点换流畅。
  • 资源回收:滑出窗口的图片释放解码内存;key 稳定避免整段重建。

动手练习:① 对比虚拟化前后的 DOM 节点数与滚动帧率;② 实现动态高度版(测量回写 + 位置缓存);③ 结合 IntersectionObserver 做提前 3 屏的下一页预取。


T15 · 进阶:熔断器与服务降级

CIRCUIT BREAKER

三态机 · 快速失败 · 降级阶梯

难度 ★★☆ · 耗时 2h · 栈 Node.js

原理:下游故障不该拖垮上游。熔断器三态:CLOSED正常放行;连续失败达到阈值切换OPEN,后续请求快速失败并走降级;冷却期后进入HALF_OPEN试探放行一次,成功则关闭、失败重开。时间线场景下推荐服务熔断即降级为关注流——用户几乎无感(对应设计原则「优雅降级」)。

NODE.JS · 三态熔断器

class CircuitBreaker {
  constructor(fn, { threshold = 5, cooldown = 10_000 } = {}) {
    this.fn = fn; this.threshold = threshold; this.cooldown = cooldown;
    this.fails = 0; this.state = 'CLOSED'; this.openAt = 0;
  }
  async call(fallback) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.openAt > this.cooldown) this.state = 'HALF_OPEN';
      else return fallback?.();                    // 开路期间:快速失败 + 降级
    }
    try {
      const r = await this.fn();
      if (this.state === 'HALF_OPEN') { this.fails = 0; this.state = 'CLOSED'; }
      return r;
    } catch (e) {
      if (this.state === 'HALF_OPEN' || ++this.fails >= this.threshold) {
        this.state = 'OPEN'; this.openAt = Date.now();
      }
      return fallback?.();
    }
  }
}

// 时间线场景:推荐熔断 → 降级关注流
const recTimeline = new CircuitBreaker(() => svc.recommended(uid));
const timeline = await recTimeline.call(() => svc.followingOnly(uid));
  • 降级阶梯预先设计:推荐 → 关注 → 热门缓存 → 骨架页,逐级兜底。
  • 舱壁隔离:每个下游独立连接池,防止一个慢服务耗尽全部资源。
  • 分布式状态:多实例各自熔断可接受短暂不一致,必要时广播状态。

动手练习:① 注入故障,画出三态切换时间线;② 为时间线设计完整四级降级预案并演练;③ 结合T1验证「关注流兜底」在推荐全挂时真的可用。


23 · 反滥用与内容治理

TRUST & SAFETY

可见性分级 · 风险评分 · 申诉

治理理念是「言论自由,而非触达自由」:高风险内容不一刀切删除,而是按可见性分级限制触达。防线共四层:边缘 Bot 评分在入口拦截机器流量;abuse.signals实时流(见09)喂给风险评分模型;visibility 服务统一执行分级裁决(与13威胁矩阵呼应);人工复核 + 申诉通道完成最后纠偏。媒体完整性依赖哈希指纹库(CSAM / 恐袭内容),上传即比对拦截已知违规。

可见性分级表

层级动作典型触发用户感知
T0 正常—低风险无
T1 降权减少推荐分发风险评分灰区无感知,可申诉查状态
T2 回复警示回复需点开、默认折叠中度攻击性回复前弹提示
T3 内容警示敏感媒体覆盖层媒体分类器点开才可见
T4 功能限制限发帖 / 限关注高置信违规站内信告知理由
T5 移除与封禁内容删除、账号停用法律要求 / 严重违规正式通知

所有分级都可申诉,申诉队列 SLO 48h;规则条款公开、执行记录可审计;透明度报告按季度披露政府数据请求与内容处置量。模型误伤的兜底永远是人工通道——治理系统的最后一道防线是人。


24 · 多区域容灾与双活

MULTI-REGION DR

RPO/RTO · 切换演练 · 混沌工程

演进路径:单数据中心 → 三地备份 → 多云双活。写路径仍以主区域为主——资金、ID 生成等强一致域永远保持单写者原则;读路径与可重建数据全球分布;欧盟数据驻留合规要求独立存储集群。RPO/RTO 按领域分级承诺,每季度真实切换演练,混沌工程常态化(见教程T19)。

分领域 RPO / RTO 承诺

领域RPORTO恢复策略
核心写入(发推)≈0(半同步)< 15min区域级主从切换
时间线读缓存可重建< 5min降级关注流(见T15)
媒体 Blob跨区异步复制< 30s多云回源自动切换
搜索索引可重建< 2hKafka 重放重建
DM 密文3 副本< 15min密钥服务优先恢复

演练日历

  1. 随机杀 pod / 缓存分区 · 每月
  2. 区域 DNS 切换演练 · 每季
  3. 整云撤离演习 · 每年
  4. 假设文档 + 结论复盘 · 每次演练

双活 ≠ 双写:只有读域与可重建数据做多区域 active,强一致域保持单写者 + 同步复制。把「所有东西都双活」是多云架构最常见的误解,也是事故的主要来源。


25 · 计数系统深挖

COUNTING AT SCALE

合并写 · 近似算法 · 展示分层

点赞、转推、浏览量是写入频率最高的场景(合计 >900K/s),直接更新数据库会立即形成热点。方案是三件事:事件流合并写——1 秒窗口微批求和后批量增量,源头是09的 engagement.events;展示分层——全局计数接受 ±1s 最终一致,个人「我是否点过赞」走强一致 read-your-writes(对照08一致性矩阵);近似算法——浏览量去重统计用 HyperLogLog。展示层统一压缩大数字(1.2万 / 1.2M)。

四类计数的路径与承诺

计数写入路径一致性展示策略
点赞 / 转推数Kafka 合并写(1s 微批)±1s 最终一致压缩显示 1.2万
浏览量HyperLogLog 近似去重误差 ±5%超过阈值才显示
粉丝数缓存 + 异步校正自己强一致、他人最终主页实时、列表缓存
我是否点赞本地状态 + DB 双写强一致乐观渲染即时反馈

工程实现见教程T16 分布式计数器。计数校正:夜间对账任务以事件流重放为准重算全量计数,纠正合并写丢失或重放重复造成的漂移——「最终一致」的「最终」靠对账保证,而不是靠祈祷。


26 · 国际化与本地化

I18N & L10N

RTL · ICU · 加权字数

支持40+ 语言,含右起书写文字(阿拉伯语、希伯来语)。四大挑战:布局镜像——设计系统(见04)整体支持 RTL,图标选择性翻转;复数与格式——ICU MessageFormat + CLDR 日期数字格式;字数加权——280 上限按权重计:拉丁字符 1、CJK 计 2;翻译——帖子由 LLM 机器翻译 + 社区校对。本地化流水线:字符串提取 → 翻译平台 → 伪本地化测试 → 发布。

加权字数与 ICU 复数

// 加权文本长度:CJK 计 2(X 280 上限的真实规则)
const weight = c => /[\u2E80-\u9FFF\uFF66-\uFFEF]/.test(c) ? 2 : 1;
const weightedLen = s => [...s].reduce((n, c) => n + weight(c), 0);

// ICU MessageFormat 处理复数(每种语言的复数规则都不同)
"{count, plural, =0 {还没有赞} other {# 个赞}}"
  • 伪本地化:文本膨胀 40% + 加重音字符,在 CI 阶段提前暴露截断问题。
  • 时区:存储永远 UTC,展示按观众 locale 转换——绝不在库里存本地时间。
  • 字体:Chirp 按文字系统回退系统字体;阿拉伯语需整形引擎(shaping)。

RTL 不只是镜像翻转:数字方向、时间线排序、动画方向、滑动返回手势都要按语言翻转——设计 Token 层提供 dir 全局开关,业务代码禁止硬编码 left/right。


27 · 开放平台与开发者 API

PLATFORM API

分级订阅 · OAuth · Webhook

对外接口以REST v2为主,按分级订阅:Free / Basic / Pro / Enterprise,各级有不同的读写配额与端点范围;OAuth 2.0 PKCE 用于第三方登录与应用授权管理;Webhook(Account Activity API)把事件推给开发者服务器,HMAC 签名防伪造;合规数据接口面向学术机构。外部接口复用与内部相同的网关(05)限流(T4)与幂等机制——对外 API 本质是内部网关能力的出口。

API 分级

层级读取配额写入配额典型场景
Free抽样端点500 帖 / 月学习试用
Basic1 万读 / 月3 千帖 / 月小型机器人
Pro100 万读 / 月30 万帖 / 月商业产品
Enterprise定制 · Firehose定制研究 / 舆情监测

废弃策略:接口退役提前 6 个月公告 + sunset HTTP 头 + changelog 双周更新;限流头完整透出配额让开发者自适应(429 示例见05)。Webhook 重试采用指数退避,签名校验失败的推送直接丢弃并告警。


T16 · 扩展:分布式计数器

DISTRIBUTED COUNTERS

合并写 · HLL · 读写分离

难度 ★★☆ · 耗时 2h · 栈 Node.js + Redis

原理:高频计数直接写库会形成热点。两个关键手段:①合并写——1 秒窗口内的事件先求和,一次 INCRBY 顶上千次单写;②读写分层——全局计数最终一致即可,个人状态(我是否点赞)必须强一致。浏览量这类「去重统计」用 HyperLogLog 近似,误差不到 1%(对应25 计数系统)。

NODE.JS · 合并写 + 近似去重

// ① 合并写:1s 窗口微批,千次事件变成一次 INCRBY
const buf = new Map();
function onLike(tweetId) { buf.set(tweetId, (buf.get(tweetId) ?? 0) + 1); }
setInterval(async () => {
  for (const [id, n] of buf) await redis.incrby(`cnt:${id}`, n);
  buf.clear();
}, 1000);

// ② 读:全局计数与个人状态分离
const likeCount = await redis.get(`cnt:${id}`);            // 最终一致 ±1s
const iLiked    = await redis.sismember(`liked:${uid}`, id); // 强一致

// ③ 浏览量去重:HyperLogLog 近似基数(标准误差 ≈ 0.81%)
await redis.pfadd(`views:${id}`, uid);
const views = await redis.pfcount(`views:${id}`);
  • 合并写的代价:进程崩溃丢失最多 1s 窗口——计数可容忍,资金绝不可容忍。
  • 展示压缩:Intl.NumberFormat(locale, {notation:'compact'})输出 1.2万 / 1.2M。
  • 对账兜底:夜间任务重放事件流重算,纠正漂移。

动手练习:① 模拟 1 万 QPS 点赞,对比合并写与单写的 Redis 压力;② 对比 HLL、位图、精确集合三者的内存占用与误差;③ 写一个「计数校正」对账 job 并故意制造丢失验证它能修复。


T17 · 扩展:趋势热点:Count-Min Sketch

TRENDING / HEAVY HITTERS

滑动窗口 · Top-K · 反操纵

难度 ★★★ · 耗时 3h · 栈 Node.js

原理:趋势要在 3.5M/s 的事件流里找出滑动窗口内最热的词。全量排序内存会爆炸,工业界用 Heavy Hitters 近似算法:Count-Min Sketch用多行哈希表估计频率(只会高估不会低估),候选再用精确计数复核;配合增速与质量分产出趋势榜,并按地域 / 语言分片独立计算(对应09流式管道)。

NODE.JS · CMS + 滑动窗口

class CountMinSketch {
  constructor(w = 2048, d = 4) {
    this.t = Array.from({ length: d }, () => new Int32Array(w));
    this.w = w;
  }
  hash(s, i) { return fnv1a(i + s) % this.w; }
  add(key, n = 1) { this.t.forEach((row, i) => row[this.hash(key, i)] += n); }
  est(key) {                                             // 取多行最小值压低高估
    return Math.min(...this.t.map((row, i) => row[this.hash(key, i)]));
  }
}

// 双桶滑动窗口:两个 30min 桶轮换,过期桶清零后复用
// 每 5min 对候选重打分:score = z(频率)*0.6 + z(增速)*0.3 − 垃圾风险
  • 高估特性:CMS 只保证「不低于真值」,上榜前必须精确复核,防止误报。
  • 反操纵:剔除机器人账号贡献、打压单一来源突增(刷榜检测)。
  • 人工闸门:敏感词进入趋势前经过审核队列,避免热点被恶意绑架。

动手练习:① 用 Zipf 分布数据验证估计误差随宽度的变化;② 实现双桶轮换的 1 小时滑动窗口;③ 加地域分片,再做跨片聚合出全球榜。


T18 · 扩展:特性开关与 Kill Switch

FEATURE FLAGS

发布解耦 · 灰度 · 生命周期

难度 ★☆☆ · 耗时 2h · 栈 Node.js

原理:特性开关把「部署」和「发布」解耦——代码可以合并上线但默认关闭,再按百分比 / 用户标签 / 地域灰度打开;事故时 Kill Switch 秒级全关,无需回滚构建。开关有生命周期:全量后 2 周内必须清理,否则开关债务会让组合状态爆炸(配合10 交付流水线的 canary 机制)。

NODE.JS · 开关求值与 Kill Switch

const flags = { darkTimeline: { pct: 20, tags: ['beta'], kill: false } };

function isEnabled(flag, user) {
  const f = flags[flag];
  if (!f || f.kill) return false;                       // Kill Switch:一键全关
  if (user.tags.some(t => f.tags.includes(t))) return true; // 白名单优先
  return bucket(user.id, flag) < f.pct;                 // 百分比用稳定分桶(见 T13)
}

// 用法:代码分支收敛在开关处,而非发布分支
render(isEnabled('darkTimeline', user) ? <NewTimeline/> : <ClassicTimeline/>);
  • 开关 × 实验:开关 + 指标采集即成 A/B(见T13),一套分桶两处复用。
  • 客户端开关:配置中心推送热更新,冷启动拉取 + 本地缓存兜底。
  • 组合管理:多开关并存的测试矩阵要显式维护,别让它隐式爆炸。

动手练习:① 加配置中心推送实现秒级热更新;② 写生命周期 lint:全量超 14 天未清理自动告警;③ 设计一条「开关 → 实验 → 全量 → 清理」的完整流水线。


T19 · 扩展:混沌工程演练

CHAOS ENGINEERING

故障注入 · 爆炸半径 · 假设验证

难度 ★★★ · 耗时 4h · 栈 Node.js + K8s

原理:可靠性不是设计出来的,是演练出来的。混沌工程 = 在生产环境主动注入受控故障,验证系统假设:杀死单 pod、切断缓存、切换区域 DNS。核心循环是「假设 → 最小爆炸半径 → 观察 → 复盘」;先在 staging 跑,再到生产低峰窗口,全程可一键中止(配合24 多区域容灾的演练日历)。

NODE.JS · 简易故障注入框架

const chaos = {
  enabled: env === 'chaos',
  async maybe(name, fault, normal) {
    if (this.enabled && injectRate(name) > Math.random()) {
      log(`💥 inject: ${name}`);
      return fault();                     // 模拟延迟 / 异常 / 断连
    }
    return normal();                      // 正常路径
  },
};

// 演练假设:缓存全挂时 SLO 不破(用 T8 的指标做裁判)
const v = await chaos.maybe('cache-down',
  () => { throw new Error('cache unreachable'); },
  () => redis.get(key));
expect(p99('api_latency_ms')).toBeLessThan(800);   // 假设成立?
  • 爆炸半径:从 1% 灰度时段开始,任何异常一键中止,绝不在高峰期首演。
  • 假设先行:每次演练必须先写下「我们认为会发生什么」,结束后对账。
  • Runbook 闭环:演练暴露的每个问题都产出 Runbook,挂进告警附件(见T8)。

动手练习:① 设计三个假设:单实例死亡 / Redis 断连 / 下游延迟 +500ms,逐一注入验证;② 画出注入前后的 P99 对比图;③ 把演练流程写成季度 gameday 剧本,包含角色分工与中止条件。


X.COM ARCHITECTURE SPEC · v15.0 · 27 章 + 19 教程 · 2026-08-09 本文为架构研究示例文档,非 X Corp 官方资料;数据为公开信息与行业惯例的整理演示 ↑ 返回顶部 · 打印 / 导出 PDF

← 返回IT 技术 yicool 百科 · X.COM 架构白皮书 v15.0:系统设计与技术架构全景

评论 0