📐 一、整体系统架构
1.1 小红书整体架构概览
系统定位
小红书是一款以"生活方式分享社区"为核心的社交电商平台,日均活跃用户超过1亿,内容涵盖美妆、时尚、美食、旅行、健身等生活领域。其技术架构需要同时满足以下核心需求:
- 高并发:支持亿级用户同时在线,峰值QPS达到百万级别
- 实时性:信息流秒级推送、评论即时可见
- 海量存储:数亿条笔记内容、百亿级用户互动数据
- 智能推荐:千人千面的个性化内容分发
- 电商闭环:从种草到购买的完整商业链路
整体架构分层
┌─────────────────────────────────────────────────────────────────┐ │ 用户接入层 (Client Layer) │ │ iOS App | Android App | Web端 | 小程序 | 开放API │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 网关层 (Gateway Layer) │ │ CDN加速 | WAF防火墙 | API网关(鉴权/限流/路由) | 负载均衡 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 业务服务层 (Business Service Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │用户服务 │ │笔记服务 │ │社交服务 │ │电商服务 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │推荐服务 │ │搜索服务 │ │消息服务 │ │直播服务 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 基础服务层 (Infrastructure Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │配置中心 │ │注册中心 │ │链路追踪 │ │监控系统 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │消息队列 │ │分布式缓存 │ │任务调度 │ │日志平台 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 数据存储层 (Data Layer) │ │ MySQL集群 | Redis集群 | Elasticsearch | HBase | MongoDB │ │ HDFS | Kafka | TiDB | ClickHouse │ └─────────────────────────────────────────────────────────────────┘
核心技术栈
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 客户端 | React Native / Flutter / Swift / Kotlin | 跨平台+原生混合开发 |
| 网关 | Nginx + Kong + Envoy | 高性能API网关 |
| 服务框架 | Spring Cloud / Go-Micro / gRPC | 微服务通信框架 |
| 消息队列 | Kafka / RocketMQ / RabbitMQ | 异步解耦、削峰填谷 |
| 缓存 | Redis Cluster + 本地缓存(Caffeine) | 多级缓存架构 |
| 数据库 | MySQL + TiDB + MongoDB + HBase | 混合存储方案 |
| 搜索 | Elasticsearch + 自研NLP引擎 | 全文检索与语义搜索 |
| 大数据 | Spark + Flink + Hadoop | 实时与离线计算 |
| 容器化 | Kubernetes + Docker + Helm | 云原生部署 |
1.2 微服务架构设计
微服务拆分原则
小红书采用领域驱动设计(DDD)进行服务拆分,遵循以下原则:
- 单一职责:每个微服务只负责一个明确的业务领域
- 高内聚低耦合:服务内部功能紧密相关,服务间依赖最小化
- 业务边界清晰:按照限界上下文(Bounded Context)划分
- 独立部署:每个服务可独立开发、测试、部署和扩展
- 数据自治:每个服务拥有独立的数据库
核心微服务清单
| 服务名 | 职责 | 核心技术 | QPS预估 |
|---|---|---|---|
| user-service | 用户注册、登录、资料管理、等级体系 | Go + MySQL + Redis | 50,000+ |
| note-service | 笔记发布、编辑、管理、审核 | Java + MongoDB + Kafka | 30,000+ |
| feed-service | 信息流拉取、聚合排序、分页 | Go + Redis + ES | 200,000+ |
| social-service | 关注、粉丝、好友、互动 | Go + Redis + HBase | 80,000+ |
| interaction-service | 点赞、收藏、评论、分享 | Java + Redis + Kafka | 150,000+ |
| search-service | 搜索索引、查询、排序、推荐 | Java + ES + 自研NLP | 100,000+ |
| recommend-service | 个性化推荐、召回、排序 | Python/Go + TensorFlow + Redis | 150,000+ |
| media-service | 图片/视频上传、处理、CDN分发 | Go + FFmpeg + OSS | 50,000+ |
| commerce-service | 商品管理、订单、支付、物流 | Java + MySQL + MQ | 20,000+ |
| push-service | 消息推送、通知、IM聊天 | Go + WebSocket + Redis | 100,000+ |
服务间通信
采用同步+异步混合的通信模式:
- 同步调用:gRPC (内部服务间) + RESTful API (对外)
- 异步通信:Kafka消息队列 (事件驱动架构)
- 服务发现:Consul + Nacos
- 负载均衡:客户端LB (gRPC) + 服务端LB (Nginx)
- 熔断降级:Sentinel / Hystrix / Go Circuit Breaker
服务调用链路示例: ┌──────┐ gRPC ┌──────────┐ Kafka ┌──────────┐ │用户端│ ──────────▶ │API Gateway│ ────────▶ │note-svc │ └──────┘ └──────────┘ └──────────┘ │ │ gRPC │ Kafka│Event ▼ ▼ ┌──────────┐ ┌──────────┐ │feed-svc │◀─────────│recommend │ └──────────┘ └──────────┘ │ gRPC │ ▼ ┌──────────┐ │Redis缓存 │ │ES索引 │ └──────────┘
1.3 网关层设计详解
API网关核心功能
小红书采用自研网关(基于OpenResty/Kong二次开发),承担以下核心职责:
1. 统一鉴权
// 请求鉴权流程
1. 客户端携带 JWT Token 请求到达网关
2. 网关解析 Token,验证签名和有效期
3. 查询用户权限(角色/接口白名单)
4. 通过则转发请求,否则返回401
5. 将用户ID注入请求Header传递给下游
Token结构:
{
"sub": "user_12345",
"role": "normal_user",
"exp": 1699999999,
"device_id": "iPhone14,2",
"permissions": ["note.create", "note.view"]
}
2. 流量控制与限流
// 限流策略(基于令牌桶算法)
限流维度:
- 用户级: 每个用户每分钟最多请求100次
- IP级: 每个IP每秒最多50次请求
- 接口级: 每个接口设置独立的QPS上限
- 全局限流: 保护后端系统整体不超过承载能力
// 限流降级策略
正常流量 → 全功能
80%容量 → 关闭部分非核心功能
90%容量 → 仅保留核心浏览功能
100%容量 → 静态页面降级
3. 灰度发布与A/B测试
- 基于用户ID哈希进行流量分割
- 支持按地域、设备、用户标签分流
- 实时切换流量比例,无需重启
4. 请求聚合
// 减少客户端请求次数,网关聚合多个服务响应
GET /api/homepage 一次请求返回:
{
"user_info": "user-service", // 用户信息
"feed_list": "feed-service", // 信息流
"recommend": "recommend-service", // 推荐内容
"notifications": "push-service" // 通知数量
}
// 网关内部并发调用4个服务,合并后返回
🎨 二、前端架构设计
2.1 移动端架构设计
混合架构方案
小红书采用"原生+跨平台+H5"的混合架构:
- 核心模块(原生):首页Feed流、相机拍摄、视频播放等性能敏感模块
- 业务模块(React Native/Flutter):个人中心、设置页、活动页面等
- 运营模块(H5/Webview):活动专题页、营销落地页等需要热更新的页面
图片加载优化策略
// 瀑布流图片加载流程
1. 图片懒加载:只加载可视区域±1屏的图片
2. 渐进式加载:先加载缩略图,再替换高清图
3. WebP自适应:支持WebP的终端优先使用WebP格式
4. 占位计算:根据宽高比预计算布局,避免重排
5. 预加载策略:用户浏览时预加载下一页图片
6. CDN多域名:图片分布在不同CDN节点,并行下载
// 图片URL格式示例
https://ci.xiaohongshu.com/note_id?imageView2/2/w/540/format/webp
https://sns-webpic.xhscdn.com/20240101/hash.jpg@540w_720h.webp
视频播放架构
视频播放流程: ┌──────┐ ┌──────────┐ ┌─────────┐ │用户 │ 点击 │视频详情页 │ HLS │CDN边缘 │ │上滑 │──────▶│播放组件 │◀─────│节点 │ └──────┘ └──────────┘ └────┬────┘ │ │ 预加载│ 回源│ ▼ ▼ ┌──────────┐ ┌─────────┐ │本地缓存 │ │源站存储 │ │(LRU策略) │ │(OSS) │ └──────────┘ └─────────┘ 关键优化: - 滑动预加载:上滑时预加载下一个视频 - 自适应码率:根据网络状况切换清晰度 - 首帧优化:使用视频首帧作为封面图 - 弱网降级:网络差时自动降低分辨率
2.2 瀑布流布局实现
瀑布流核心算法
小红书采用多列不等高的瀑布流布局,核心思路:
// 瀑布流布局算法(最短列优先)
class WaterfallLayout {
constructor(columns = 2) {
this.columns = columns;
this.columnHeights = new Array(columns).fill(0);
}
addItem(item) {
// 1. 找到当前最短的列
const shortestColumn = this.columnHeights.indexOf(
Math.min(...this.columnHeights)
);
// 2. 计算该卡片的高度(基于宽高比)
const cardHeight = this.calculateHeight(item);
// 3. 放置卡片到最短列
item.position = {
column: shortestColumn,
top: this.columnHeights[shortestColumn]
};
// 4. 更新列高度
this.columnHeights[shortestColumn] += cardHeight + GAP;
return item;
}
calculateHeight(item) {
// 根据图片宽高比和固定列宽计算实际高度
const columnWidth = (screenWidth - padding) / this.columns;
const aspectRatio = item.width / item.height;
return columnWidth / aspectRatio;
}
}
// 列数适配策略
- 手机竖屏: 2列
- 手机横屏: 3列
- 平板: 3-4列
- 电脑: 4-5列(可拖拽调整)
虚拟滚动优化
// 虚拟列表只渲染可视区域的卡片
class VirtualWaterfall {
render() {
const scrollTop = container.scrollTop;
const viewHeight = container.clientHeight;
// 计算可视区域范围
const startIndex = this.findFirstVisibleIndex(scrollTop);
const endIndex = this.findLastVisibleIndex(scrollTop + viewHeight);
// 只渲染 startIndex 到 endIndex 之间的卡片
// 配合 buffer 区域(上下各多渲染2-3个)避免白屏
this.renderItems(startIndex - BUFFER, endIndex + BUFFER);
}
// 通过二分查找快速定位可见区域
findFirstVisibleIndex(scrollTop) {
let low = 0, high = this.totalItems - 1;
while (low < high) {
const mid = Math.floor((low + high) / 2);
if (this.items[mid].bottom < scrollTop) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
}
2.3 性能优化策略
启动优化
| 优化项 | 措施 | 效果 |
|---|---|---|
| 冷启动 | 预编译、懒加载、异步初始化 | 启动时间从3s降至1.2s |
| 首页渲染 | SSR预渲染 + 骨架屏 + 本地缓存 | 首屏渲染时间<500ms |
| 页面切换 | 预加载下一页 + 共享元素转场 | 切换感知延迟<100ms |
| 内存管理 | 图片内存池 + LRU缓存 + 及时回收 | 内存占用降低40% |
网络优化
- HTTP/2多路复用:减少连接建立开销
- 请求合并:短时间内多个请求合并为一个批次
- 弱网优化:自动切换协议(QUIC/HTTP3)
- 智能DNS:基于地理位置和网络状况选择最优节点
- 请求优先级:Feed流数据>用户信息>非关键资源
⚙️ 三、后端架构设计
3.1 信息流Feed系统设计
Feed流核心挑战
信息流系统是社交平台的核心,小红书面临以下技术挑战:
- 亿级用户 × 千万级内容 = 海量数据存储与查询
- 实时性要求高:新内容需要秒级进入用户Feed流
- 个性化需求:每个用户的Feed流内容都不同
- 热点处理:爆款内容可能瞬间产生千万级互动
推拉结合的Feed架构
┌─────────────────────────────────────────────────────┐ │ Feed架构选择 │ ├───────────────┬─────────────────┬───────────────────┤ │ 拉模式(Pull) │ 推模式(Push) │ 推拉结合(推荐) │ ├───────────────┼─────────────────┼───────────────────┤ │用户主动拉取 │写入时推送给粉丝 │混合策略 │ │适合大V(粉丝多) │适合普通用户 │根据粉丝数选择 │ │存储成本低 │存储成本高 │平衡存储与实时性 │ │实时性差 │实时性好 │实时性好 │ └───────────────┴─────────────────┴───────────────────┘ // 推拉结合策略 IF 发布者粉丝数 < 1000: 使用Push模式,直接写入每个粉丝的Timeline ELIF 发布者粉丝数 < 100万: Push到活跃粉丝 + Pull补充非活跃粉丝 ELSE (大V/官方号): 使用Pull模式,用户拉取时实时聚合
Feed数据存储结构
// Timeline存储 - 使用Redis ZSet (有序集合)
// Key: timeline:{user_id}
// Score: 发布时间戳(毫秒)
// Member: 笔记ID
ZADD timeline:user_123 1699900000000 note_abc
ZADD timeline:user_123 1699900001000 note_def
// 获取用户最新20条Feed
ZREVRANGE timeline:user_123 0 19
// 分页加载(游标分页)
ZREVRANGEBYSCORE timeline:user_123 last_score -INF LIMIT 0 20
// 内容详情存储 - Redis Hash
HSET note:note_abc title "今日穿搭分享"
HSET note:note_abc author_id "user_456"
HSET note:note_abc image_list "[url1,url2,url3]"
HSET note:note_abc like_count 1520
// 过期清理策略
- 超过30天的非热门内容移至HBase冷存储
- Timeline中只保留最近7天的活跃内容
- 使用TTL自动过期清理
3.2 高并发点赞系统设计
设计挑战
点赞系统看似简单,在高并发场景下存在诸多挑战:
- 单条爆款笔记可能产生千万级点赞
- 计数准确性与实时性的平衡
- 用户点赞状态的快速判断
- 防止重复点赞和刷赞
技术方案
// 点赞系统架构设计
class LikeService {
// 用户点赞操作
async likeNote(userId, noteId) {
// 1. 幂等性检查 - Redis Set判断是否已点赞
const likeKey = `note_likes:${noteId}`;
const isLiked = await redis.sismember(likeKey, userId);
if (isLiked) return { success: true, msg: 'already liked' };
// 2. Redis原子操作 - 点赞+计数+1
const pipeline = redis.pipeline();
pipeline.sadd(likeKey, userId); // 记录点赞用户
pipeline.hincrby('note:stats', `${noteId}:likes`, 1); // 计数+1
pipeline.sadd(`user_likes:${userId}`, noteId); // 用户的点赞列表
await pipeline.exec();
// 3. 异步写入MySQL(批量合并写入)
await kafka.send('like_events', {
userId, noteId, action: 'like',
timestamp: Date.now()
});
// 4. 推送通知(异步)
await notifyAuthor(userId, noteId, 'like');
}
// 计数显示策略
async getLikeCount(noteId) {
// 优先从Redis读取实时计数
let count = await redis.hget('note:stats', `${noteId}:likes`);
// 如果Redis没有数据,从MySQL兜底
if (!count) {
count = await mysql.getLikeCount(noteId);
await redis.hset('note:stats', `${noteId}:likes`, count);
}
// 大数字友好显示
return formatCount(count); // 1520 → "1.5k"
}
}
// 批量写入MySQL策略
// 消费Kafka消息,每10秒或累积1000条时批量写入
// 使用INSERT ON DUPLICATE KEY UPDATE实现增量更新
数据存储分层
| 存储层 | 存储内容 | 读写比例 |
|---|---|---|
| Redis | 实时点赞状态、计数、最近点赞用户 | 读90% / 写10% |
| MySQL | 完整点赞关系记录 | 写多读少(批量写入) |
| HBase | 历史点赞数据归档 | 写多读少 |
3.3 评论系统设计
多级评论数据模型
小红书采用两级评论结构:一级评论(直接评论笔记)+ 二级评论(回复一级评论)
// 评论表结构设计
CREATE TABLE comments (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
note_id BIGINT NOT NULL, -- 笔记ID
user_id BIGINT NOT NULL, -- 评论者ID
parent_id BIGINT DEFAULT 0, -- 父评论ID(0表示一级评论)
reply_to_user_id BIGINT, -- 回复目标用户ID
content TEXT NOT NULL, -- 评论内容
like_count INT DEFAULT 0, -- 点赞数
child_count INT DEFAULT 0, -- 子评论数
status TINYINT DEFAULT 1, -- 状态: 0-删除 1-正常 2-审核中
ip_location VARCHAR(50), -- IP属地
created_at TIMESTAMP DEFAULT NOW(),
INDEX idx_note_parent (note_id, parent_id, created_at),
INDEX idx_user (user_id)
);
// 热门评论排序算法
hot_score = like_count * log(time_decay + 1)
其中:
- like_count: 点赞数(权重最大)
- time_decay: 时间衰减因子(越新越靠前)
- 作者回复加分 +5
- 优质用户加分 +2
评论加载策略
- 首页加载:默认展示3条热门评论 + 2条最新评论
- 展开子评论:点击"展开更多"时加载该评论下的回复
- 评论排序:支持"最热"和"最新"两种排序方式切换
- 增量加载:新评论通过WebSocket实时推送到页面
3.4 消息推送系统设计
推送通道
| 通道 | 使用场景 | 技术实现 |
|---|---|---|
| App内通知 | 点赞、评论、新粉丝、系统通知 | WebSocket长连接 + Redis Pub/Sub |
| App Push | 重要通知唤醒用户 | APNs(iOS) / FCM+厂商通道(Android) |
| 短信 | 验证码、重要安全通知 | 短信服务商API |
| 站内信 | 活动通知、运营消息 | 消息中心 + 未读数Badge |
推送架构
推送流程: ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │业务事件 │───▶│消息服务 │───▶│路由分发 │───▶│推送通道 │ │点赞/评论 │ │聚合去重 │ │用户偏好 │ │WS/Push │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ // 消息去重与聚合策略 - 同一用户短时间内多次点赞同一笔记 → 合并为一条通知 - 10秒窗口内的多个新粉丝 → 聚合为"新增N个粉丝" - 用户设置的免打扰时间段 → 延迟推送 - 消息疲劳度控制 → 单日推送上限30条
3.5 分布式缓存架构
多级缓存设计
// 三级缓存架构
请求 → 本地缓存(L1) → Redis(L2) → 数据库(L3)
L1 本地缓存 (Caffeine/Guava):
- 容量: 每节点10000个Key
- 过期时间: 30秒
- 适用: 热点数据(热门笔记、大V用户信息)
- 优势: 微秒级响应,无网络开销
L2 分布式缓存 (Redis Cluster):
- 容量: TB级别
- 过期时间: 根据数据类型不同(1min~24h)
- 适用: 用户会话、Feed流、计数器、分布式锁
- 集群: 主从+哨兵 或 Redis Cluster模式
L3 数据库 (MySQL/HBase):
- 容量: 无限
- 适用: 持久化数据、冷数据
- 回源策略: 缓存未命中时查询并回填缓存
缓存一致性策略
// 采用 Cache Aside Pattern + 延迟双删
async function updateUserProfile(userId, newData) {
// 1. 先更新数据库
await db.update('users', userId, newData);
// 2. 删除缓存(第一次删除)
await redis.del(`user:${userId}`);
// 3. 延迟双删(防止并发读导致脏数据)
setTimeout(async () => {
await redis.del(`user:${userId}`);
}, 500); // 500ms后再次删除
}
// 读取时自动回填
async function getUserProfile(userId) {
// 1. 尝试从缓存获取
let data = await redis.get(`user:${userId}`);
if (data) return JSON.parse(data);
// 2. 缓存未命中,查数据库
data = await db.query('users', { id: userId });
// 3. 回填缓存
await redis.setex(`user:${userId}`, 3600, JSON.stringify(data));
return data;
}
// 防缓存击穿: 使用互斥锁
async function getUserWithLock(userId) {
const lockKey = `lock:user:${userId}`;
const acquired = await redis.set(lockKey, 1, 'EX', 10, 'NX');
if (acquired) {
try {
// 获取到锁,查数据库并回填缓存
const data = await db.query('users', { id: userId });
await redis.setex(`user:${userId}`, 3600, JSON.stringify(data));
return data;
} finally {
await redis.del(lockKey);
}
} else {
// 未获取到锁,等待后重试
await sleep(100);
return redis.get(`user:${userId}`);
}
}
🗄️ 四、数据库设计
4.1 用户表设计
核心用户表
-- 用户主表
CREATE TABLE users (
id BIGINT PRIMARY KEY, -- 雪花算法生成
phone VARCHAR(20) UNIQUE, -- 手机号(加密存储)
email VARCHAR(100), -- 邮箱
password_hash VARCHAR(255), -- 密码哈希(bcrypt)
nickname VARCHAR(50) NOT NULL, -- 昵称
avatar_url VARCHAR(500), -- 头像URL
bio VARCHAR(500), -- 个人简介
gender TINYINT DEFAULT 0, -- 性别: 0未知 1男 2女
birthday DATE, -- 生日
location VARCHAR(100), -- 地区
level TINYINT DEFAULT 1, -- 用户等级 1-10
verification_type TINYINT DEFAULT 0, -- 认证类型
status TINYINT DEFAULT 1, -- 账号状态
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW() ON UPDATE NOW(),
INDEX idx_phone (phone),
INDEX idx_created (created_at)
);
-- 用户统计表(异步更新)
CREATE TABLE user_stats (
user_id BIGINT PRIMARY KEY,
note_count INT DEFAULT 0, -- 发布笔记数
follower_count INT DEFAULT 0, -- 粉丝数
following_count INT DEFAULT 0, -- 关注数
like_count INT DEFAULT 0, -- 获赞总数
collect_count INT DEFAULT 0, -- 被收藏次数
updated_at TIMESTAMP DEFAULT NOW() ON UPDATE NOW()
);
-- 用户成长值表
CREATE TABLE user_growth (
user_id BIGINT PRIMARY KEY,
exp_points INT DEFAULT 0, -- 经验值
creator_score INT DEFAULT 0, -- 创作者分数
badges JSON, -- 获得的徽章
last_active_at TIMESTAMP -- 最后活跃时间
);
分库分表策略
- 分片键:user_id (雪花ID)
- 分库数量:64个库
- 分表数量:每库256张表
- 路由算法:user_id % 64 → 库号,user_id / 64 % 256 → 表号
- 中间件:ShardingSphere / MyCat
4.2 笔记内容表设计
笔记主表
-- 笔记表
CREATE TABLE notes (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
title VARCHAR(200), -- 标题
content TEXT, -- 正文内容
type TINYINT DEFAULT 1, -- 类型: 1图文 2视频 3直播
cover_url VARCHAR(500), -- 封面图
images JSON, -- 图片列表[{url, width, height}]
video_url VARCHAR(500), -- 视频URL
video_duration INT, -- 视频时长(秒)
topics JSON, -- 话题标签["#美食","#探店"]
location VARCHAR(200), -- 发布地点
poi_id BIGINT, -- POI地点ID
status TINYINT DEFAULT 0, -- 状态: 0审核中 1已发布 2已删除 3违规
audit_status TINYINT DEFAULT 0, -- 审核状态
visibility TINYINT DEFAULT 1, -- 可见性: 1公开 2仅粉丝 3私密
is_top TINYINT DEFAULT 0, -- 是否置顶
publish_at TIMESTAMP, -- 发布时间
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW() ON UPDATE NOW(),
INDEX idx_user_status (user_id, status, created_at),
INDEX idx_publish (publish_at, status),
FULLTEXT INDEX ft_content (title, content) -- 全文索引
);
-- 笔记统计表
CREATE TABLE note_stats (
note_id BIGINT PRIMARY KEY,
view_count INT DEFAULT 0, -- 浏览量
like_count INT DEFAULT 0, -- 点赞数
collect_count INT DEFAULT 0, -- 收藏数
comment_count INT DEFAULT 0, -- 评论数
share_count INT DEFAULT 0, -- 分享数
score DECIMAL(10,2) DEFAULT 0, -- 综合评分
updated_at TIMESTAMP DEFAULT NOW() ON UPDATE NOW()
);
-- 笔记标签关系表
CREATE TABLE note_tags (
id BIGINT PRIMARY KEY,
note_id BIGINT NOT NULL,
tag_id BIGINT NOT NULL,
tag_name VARCHAR(50) NOT NULL,
INDEX idx_note (note_id),
INDEX idx_tag (tag_id)
);
4.3 社交关系表设计
关注关系存储
-- 关注关系表
CREATE TABLE follows (
id BIGINT PRIMARY KEY,
follower_id BIGINT NOT NULL, -- 关注者ID
following_id BIGINT NOT NULL, -- 被关注者ID
status TINYINT DEFAULT 1, -- 状态: 0已取消 1关注中
group_name VARCHAR(50) DEFAULT 'default', -- 关注分组
is_mute TINYINT DEFAULT 0, -- 是否静音
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE KEY uk_relation (follower_id, following_id),
INDEX idx_following (following_id, status, created_at)
);
-- 由于社交关系数据量巨大,实际使用以下方案:
-- 1. 热数据: Redis Set存储活跃用户的关注/粉丝列表
-- followers:{user_id} -> Set[follower_ids]
-- following:{user_id} -> Set[following_ids]
-- 2. 温数据: MySQL分表存储
-- 3. 冷数据: HBase归档存储
-- 共同关注查询
-- 查询用户A和用户B的共同关注
SELECT following_id FROM follows
WHERE follower_id = A AND status = 1
INTERSECT
SELECT following_id FROM follows
WHERE follower_id = B AND status = 1;
-- 使用Redis实现:
SINTER following:A following:B
🤖 五、推荐系统设计
5.1 推荐系统整体架构
推荐系统流水线
推荐系统处理流程 (漏斗模型): 全量内容池 (数亿条笔记) │ ▼ ① 内容过滤 候选集 (~1000万条) ← 去除违规/低质/过期内容 │ ▼ ② 召回层 (Recall) 粗排集 (~10000条) ← 多路召回合并 │ ▼ ③ 粗排层 (Pre-ranking) 精排集 (~1000条) ← 轻量级模型快速打分 │ ▼ ④ 精排层 (Ranking) 最终排序 (~500条) ← 深度模型精确打分 │ ▼ ⑤ 重排层 (Re-ranking) 展示列表 (~20条) ← 多样性/新鲜度/商业化调整 │ ▼ 用户Feed流展示
核心指标
| 指标 | 说明 | 目标值 |
|---|---|---|
| CTR (点击率) | 用户点击内容的概率 | >15% |
| 互动率 | 点赞/收藏/评论/分享的比例 | >5% |
| 观看时长 | 用户在内容上的停留时间 | >30秒(图文) / >60秒(视频) |
| 推荐延迟 | 从请求到返回推荐结果的耗时 | <200ms |
| 多样性 | 推荐内容的话题/类目覆盖度 | >8个类目 |
5.2 多路召回策略
召回层详细设计
召回层负责从海量内容中快速筛选出用户可能感兴趣的候选集,采用多路召回合并的策略:
| 召回路 | 原理 | 召回数量 |
|---|---|---|
| 协同过滤召回 (CF) | 基于相似用户的喜好推荐(User-CF / Item-CF) | ~2000条 |
| 内容标签召回 | 根据用户兴趣标签匹配相关内容 | ~1000条 |
| 热门召回 | 全站/分类下的热门内容兜底 | ~500条 |
| 向量召回 (DeepWalk) | 基于图神经网络的内容向量化匹配 | ~2000条 |
| 实时兴趣召回 | 根据用户最近行为实时调整 | ~500条 |
| 关注人召回 | 用户关注的人发布的新内容 | ~1000条 |
| 地理位置召回 | 附近的热门内容(同城推荐) | ~500条 |
向量召回实现
// 基于双塔模型的向量召回
// User塔: 用户特征 → 用户向量 (128维)
// Item塔: 内容特征 → 内容向量 (128维)
class TwoTowerRecall:
def __init__(self):
self.user_tower = DNN([256, 128]) # 用户特征编码
self.item_tower = DNN([256, 128]) # 内容特征编码
self.index = FaissIndex(dimension=128) # 向量检索引擎
def get_user_embedding(self, user_features):
"""生成用户向量"""
return self.user_tower.forward(user_features)
def recall(self, user_vector, top_k=2000):
"""在Faiss索引中检索最相似的内容"""
return self.index.search(user_vector, top_k)
// Faiss索引构建 (离线)
// 将所有内容向量写入Faiss,支持毫秒级检索
// 使用 IVF(倒排文件) + PQ(乘积量化) 加速
5.3 精排模型设计
特征工程
| 特征类别 | 具体特征 | 维度 |
|---|---|---|
| 用户特征 | 年龄、性别、城市、兴趣标签、活跃度、消费能力 | ~100维 |
| 用户行为序列 | 最近50条点击/搜索/浏览序列 | 序列特征 |
| 内容特征 | 类目、标签、文本embedding、图片质量分、时长 | ~200维 |
| 上下文特征 | 时间、设备、网络类型、地理位置 | ~20维 |
| 统计特征 | 内容CTR、互动率、完播率、曝光数 | ~50维 |
| 交叉特征 | 用户-类目偏好、用户-标签亲和度 | ~100维 |
模型架构
// 精排模型: Deep & Cross Network + Attention
// 多目标学习: 同时预测点击/互动/停留时长
class MultiTaskRankingModel:
def __init__(self):
# 特征编码层
self.dense_features = DenseEncoder() # 连续特征
self.sparse_features = EmbeddingLayer() # 离散特征
# 交叉网络 (Cross Network) - 学习特征交叉
self.cross_net = CrossNetwork(num_layers=3)
# 深度网络 (Deep Network) - 学习高阶特征
self.deep_net = DNN([512, 256, 128])
# 注意力层 - 用户行为序列建模
self.attention = TargetAttention()
# 多目标输出头
self.ctr_head = DNN([64, 1]) # 点击率预测
self.interact_head = DNN([64, 1]) # 互动率预测
self.duration_head = DNN([64, 1]) # 停留时长预测
def forward(self, features):
# 特征编码
x_dense = self.dense_features(features['dense'])
x_sparse = self.sparse_features(features['sparse'])
x = concat([x_dense, x_sparse])
# 交叉+深度并行
cross_out = self.cross_net(x)
deep_out = self.deep_net(x)
# 注意力机制处理行为序列
seq_features = features['behavior_seq']
attention_out = self.attention(seq_features, target_item)
# 合并
merged = concat([cross_out, deep_out, attention_out])
# 多任务输出
ctr_prob = sigmoid(self.ctr_head(merged))
interact_prob = sigmoid(self.interact_head(merged))
duration_pred = self.duration_head(merged)
return ctr_prob, interact_prob, duration_pred
// 融合打分公式
final_score = α * ctr_prob + β * interact_prob + γ * normalize(duration_pred)
其中 α, β, γ 为可调节权重,用于平衡不同目标
5.4 用户画像构建
用户画像层次
- 基础属性:年龄、性别、城市、设备类型
- 行为偏好:浏览/点赞/收藏/搜索的类目分布
- 兴趣标签:通过行为挖掘的长期兴趣+短期兴趣
- 消费能力:基于浏览和购买行为推断
- 社交属性:社交活跃度、圈子特征
兴趣标签体系
// 三级兴趣标签体系
{
"长期兴趣": {
"美妆护肤": {"权重": 0.85, "更新时间": "2024-01-15"},
"穿搭时尚": {"权重": 0.72, "更新时间": "2024-01-14"},
"健身运动": {"权重": 0.45, "更新时间": "2024-01-10"}
},
"短期兴趣": {
"三亚旅行": {"权重": 0.90, "衰减时间": "3天"},
"日式料理": {"权重": 0.65, "衰减时间": "7天"}
},
"实时兴趣": {
"今日浏览类目": ["家居", "母婴"],
"最近搜索词": ["宝宝辅食", "婴儿床"]
}
}
// 兴趣权重更新公式 (时间衰减)
weight(t) = weight(0) * e^(-λ * t)
其中:
- weight(0): 初始权重
- λ: 衰减系数(短期兴趣衰减快,长期兴趣衰减慢)
- t: 距离上次行为的时间
📝 六、开发教程
6.1 项目初始化教程
环境准备
# 1. 安装基础环境
# Node.js >= 18.x
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Go >= 1.21
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# Java 17 (OpenJDK)
sudo apt install openjdk-17-jdk
# Docker & Docker Compose
curl -fsSL https://get.docker.com | sh
sudo apt install docker-compose
# 2. 克隆项目
git clone https://github.com/xhs-lab/xiaohongshu-clone.git
cd xiaohongshu-clone
# 3. 安装依赖
# Go微服务
cd services/ && go mod download
# 前端项目
cd web/ && npm install
# 4. 启动依赖服务 (Docker Compose)
docker-compose up -d redis mysql kafka elasticsearch
项目目录结构
xiaohongshu-clone/
├── api/ # API定义 (Protobuf/OpenAPI)
├── services/ # 微服务代码
│ ├── user-service/ # 用户服务 (Go)
│ ├── note-service/ # 笔记服务 (Java)
│ ├── feed-service/ # 信息流服务 (Go)
│ ├── recommend-service/ # 推荐服务 (Python)
│ └── search-service/ # 搜索服务 (Java)
├── web/ # 前端项目
│ ├── app/ # React Native App
│ └── admin/ # 管理后台 (Vue3)
├── deploy/ # 部署配置
│ ├── k8s/ # Kubernetes配置
│ └── docker/ # Docker配置
├── docs/ # 文档
└── scripts/ # 脚本工具
6.2 笔记发布接口开发教程
API设计 (RESTful)
// 发布笔记接口
POST /api/v1/notes
Content-Type: multipart/form-data
Authorization: Bearer {token}
Request Body:
{
"title": "今日穿搭分享",
"content": "今天尝试了法式穿搭...",
"images": [file1, file2, file3], // 最多9张图
"topics": ["#穿搭", "#日常"],
"location": "上海静安",
"visibility": 1 // 1公开 2仅粉丝 3私密
}
Response (201 Created):
{
"code": 0,
"data": {
"note_id": "6501234567890",
"status": "published",
"cover_url": "https://cdn.xhs.com/...",
"created_at": "2024-01-15T10:30:00Z"
}
}
后端实现 (Go)
// note-service/handler/note_handler.go
func (h *NoteHandler) CreateNote(c *gin.Context) {
// 1. 参数解析
userID := c.GetInt64("user_id") // 从JWT中获取
title := c.PostForm("title")
content := c.PostForm("content")
topics := c.PostFormArray("topics")
location := c.PostForm("location")
// 2. 参数校验
if title == "" || len(title) > 200 {
c.JSON(400, gin.H{"code": 40001, "msg": "标题不合法"})
return
}
// 3. 图片上传到OSS
images := make([]ImageInfo, 0)
form, _ := c.MultipartForm()
files := form.File["images"]
for _, file := range files {
url, err := h.ossClient.Upload(file)
if err != nil {
c.JSON(500, gin.H{"code": 50001, "msg": "图片上传失败"})
return
}
images = append(images, ImageInfo{
URL: url,
Width: getImgWidth(file),
Height: getImgHeight(file),
})
}
// 4. 内容安全审核
auditResult := h.contentAudit(title + content, images)
if !auditResult.Passed {
c.JSON(400, gin.H{"code": 40002, "msg": "内容包含违规信息"})
return
}
// 5. 生成笔记ID (雪花算法)
noteID := snowflake.Generate()
// 6. 写入数据库
note := &Note{
ID: noteID,
UserID: userID,
Title: title,
Content: content,
Images: images,
Topics: topics,
Location: location,
Status: StatusPublished,
CreatedAt: time.Now(),
}
if err := h.noteRepo.Create(note); err != nil {
c.JSON(500, gin.H{"code": 50002, "msg": "创建笔记失败"})
return
}
// 7. 发送Kafka事件(异步处理后续逻辑)
h.kafkaProducer.Send("note_created", map[string]interface{}{
"note_id": noteID,
"user_id": userID,
"topics": topics,
"created_at": time.Now().Unix(),
})
// 8. 返回结果
c.JSON(201, gin.H{
"code": 0,
"data": map[string]interface{}{
"note_id": noteID,
"status": "published",
"cover_url": images[0].URL,
"created_at": note.CreatedAt,
},
})
}
// Kafka消费者处理后续逻辑
// - 更新用户笔记数统计
// - 推送给粉丝的Feed流
// - 构建搜索索引
// - 触发推荐模型更新用户画像
6.3 信息流拉取接口教程
Feed流分页拉取
// 获取首页信息流
GET /api/v1/feed?cursor=1699900000000&limit=20&source=discover
Authorization: Bearer {token}
// 响应
{
"code": 0,
"data": {
"notes": [
{
"id": "6501234567890",
"title": "今日穿搭",
"cover": {"url": "...", "width": 540, "height": 720},
"author": {"id": "user_456", "name": "时尚达人", "avatar": "..."},
"stats": {"likes": 1520, "comments": 89},
"type": "image"
}
],
"has_more": true,
"next_cursor": 1699800000000
}
}
// 后端实现
func (h *FeedHandler) GetFeed(c *gin.Context) {
userID := c.GetInt64("user_id")
cursor := c.GetInt64("cursor")
limit := c.GetInt("limit")
source := c.GetString("source")
var notes []*NoteBrief
if source == "following" {
// 关注Feed: 按时间倒序拉取关注人的内容
followingIDs := h.socialService.GetFollowingList(userID)
notes = h.feedService.GetFollowingFeed(followingIDs, cursor, limit)
} else {
// 发现Feed: 个性化推荐
notes = h.recommendService.GetRecommendFeed(userID, cursor, limit)
}
// 构建响应
hasMore := len(notes) == limit
var nextCursor int64
if hasMore && len(notes) > 0 {
nextCursor = notes[len(notes)-1].SortScore
}
c.JSON(200, gin.H{
"code": 0,
"data": gin.H{
"notes": notes,
"has_more": hasMore,
"next_cursor": nextCursor,
},
})
}
6.4 实时搜索实现教程
Elasticsearch索引设计
// 笔记搜索索引Mapping
PUT /notes_index
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"xhs_analyzer": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": ["lowercase", "xhs_synonym", "xhs_stop"]
}
}
}
},
"mappings": {
"properties": {
"note_id": {"type": "long"},
"title": {
"type": "text",
"analyzer": "xhs_analyzer",
"boost": 3
},
"content": {
"type": "text",
"analyzer": "xhs_analyzer"
},
"tags": {"type": "keyword"},
"user_id": {"type": "long"},
"category": {"type": "keyword"},
"like_count": {"type": "integer"},
"score": {"type": "float"},
"publish_time": {"type": "date"},
"images": {
"type": "nested",
"properties": {
"url": {"type": "keyword"},
"width": {"type": "integer"},
"height": {"type": "integer"}
}
}
}
}
}
// 搜索查询构建
func (s *SearchService) Search(query SearchRequest) (*SearchResult, error) {
// 构建ES查询
boolQuery := elastic.NewBoolQuery()
// 关键词匹配
if query.Keyword != "" {
multiMatch := elastic.NewMultiMatchQuery(query.Keyword, "title^3", "content", "tags^2")
boolQuery.Must(multiMatch)
}
// 类目过滤
if query.Category != "" {
boolQuery.Filter(elastic.NewTermQuery("category", query.Category))
}
// 时间范围
if query.Since > 0 {
boolQuery.Filter(elastic.NewRangeQuery("publish_time").Gte(query.Since))
}
// 排序策略
var sorters []elastic.Sorter
switch query.SortBy {
case "hot":
sorters = append(sorters, elastic.NewFieldSort("score").Desc())
case "newest":
sorters = append(sorters, elastic.NewFieldSort("publish_time").Desc())
default:
sorters = append(sorters, elastic.NewScoreSort().Desc())
}
// 执行搜索
result, err := s.esClient.Search().
Index("notes_index").
Query(boolQuery).
SortBy(sorters...).
From(query.Offset).
Size(query.Limit).
Do(context.Background())
return parseResult(result), err
}
6.5 图片处理服务教程
图片处理流水线
// 图片处理服务 (Go)
type ImageProcessor struct {
oss *OSSClient
cdn *CDNClient
}
func (p *ImageProcessor) ProcessImage(file multipart.File, filename string) (*ImageResult, error) {
// 1. 安全检测
if err := p.checkImageSafety(file); err != nil {
return nil, fmt.Errorf("图片安全检查不通过: %w", err)
}
// 2. 生成多种尺寸
sizes := []struct {
name string
width int
height int
quality int
}{
{"thumbnail", 200, 200, 75}, // 缩略图
{"small", 480, 0, 80}, // 小图
{"medium", 720, 0, 85}, // 中图
{"large", 1080, 0, 90}, // 大图
{"original", 0, 0, 100}, // 原图
}
result := &ImageResult{URLs: make(map[string]string)}
hash := md5Hash(file) // 内容哈希用于去重
for _, size := range sizes {
// 3. 图片压缩和缩放
processed, err := p.resizeImage(file, size.width, size.height, size.quality)
if err != nil {
return nil, err
}
// 4. WebP转换 (体积更小)
webpData, err := p.convertToWebP(processed)
if err != nil {
webpData = processed // 降级使用原始格式
}
// 5. 上传到OSS
objectKey := fmt.Sprintf("notes/%s/%s_%s.webp", getDate(), hash, size.name)
url, err := p.oss.Upload(objectKey, webpData)
if err != nil {
return nil, err
}
result.URLs[size.name] = url
}
// 6. 图片信息提取
result.Width, result.Height = getImageDimensions(file)
result.Format = "webp"
result.Size = fileSize(webpData)
// 7. CDN预热
p.cdn.Prefetch(result.URLs["medium"])
return result, nil
}
// 图片去重 (感知哈希)
func (p *ImageProcessor) CheckDuplicate(imageData []byte) (bool, string) {
// 计算感知哈希(pHash),相似的图会有相近的哈希
phash := perceptualhash.Compute(imageData)
// 查询是否有相似图片
existing := p.duplicateDB.FindSimilar(phash, threshold=10)
if existing != nil {
return true, existing.NoteID
}
return false, ""
}
🚀 七、部署与运维
7.1 Kubernetes部署方案
集群规划
| 集群 | 节点数 | 规格 | 用途 |
|---|---|---|---|
| Master集群 | 3 (高可用) | 8C16G SSD | K8s控制面 |
| 业务集群 | 200+ | 16C64G NVMe | 微服务部署 |
| 大数据集群 | 50+ | 32C128G | Spark/Flink计算 |
| 中间件集群 | 30+ | 16C64G | Redis/Kafka/ES |
Deployment配置示例
# note-service deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: note-service
namespace: xhs-production
labels:
app: note-service
spec:
replicas: 50
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app: note-service
template:
metadata:
labels:
app: note-service
spec:
containers:
- name: note-service
image: registry.xhs.com/note-service:v2.3.1
ports:
- containerPort: 8080
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-credentials
key: host
- name: REDIS_CLUSTER
value: "redis-cluster.xhs-infra:6379"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- note-service
topologyKey: kubernetes.io/hostname
HPA自动伸缩
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: note-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: note-service
minReplicas: 20
maxReplicas: 200
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
7.2 CI/CD流水线设计
GitOps工作流
CI/CD流程: 开发者Push代码 → GitLab/ GitHub │ ▼ ┌─────────────────┐ │ 代码质量检查 │ → Lint + 单元测试 + 安全扫描 │ (Pre-merge CI) │ → 覆盖率>80%才允许合并 └─────────────────┘ │ 合并到main ▼ ┌─────────────────┐ │ 构建镜像 │ → Docker build + Push to Registry │ (Build Stage) │ → 生成版本号 (semver) └─────────────────┘ │ ▼ ┌─────────────────┐ │ 部署到Staging │ → 自动部署到测试环境 │ (Staging) │ → 自动化测试 + 性能测试 └─────────────────┘ │ 测试通过 ▼ ┌─────────────────┐ │ 灰度发布 │ → 先发布10%流量 │ (Canary) │ → 监控指标无异常则扩量 └─────────────────┘ │ ▼ ┌─────────────────┐ │ 全量发布 │ → 滚动更新所有实例 │ (Production) │ → 自动回滚机制保护 └─────────────────┘
GitLab CI配置
# .gitlab-ci.yml
stages:
- lint
- test
- build
- deploy-staging
- deploy-production
variables:
DOCKER_REGISTRY: registry.xhs.com
IMAGE_NAME: $DOCKER_REGISTRY/note-service
lint:
stage: lint
image: golangci/golangci-lint:latest
script:
- golangci-lint run ./...
unit-test:
stage: test
image: golang:1.21
script:
- go test -race -coverprofile=coverage.out ./...
coverage: '/total:\s+\(statements\)\s+(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
- docker push $IMAGE_NAME:latest
deploy-staging:
stage: deploy-staging
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/note-service note-service=$IMAGE_NAME:$CI_COMMIT_SHA -n staging
- kubectl rollout status deployment/note-service -n staging --timeout=300s
environment:
name: staging
url: https://staging.xhs.com
only:
- main
deploy-production:
stage: deploy-production
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/note-service note-service=$IMAGE_NAME:$CI_COMMIT_SHA -n production
- kubectl rollout status deployment/note-service -n production --timeout=600s
environment:
name: production
url: https://www.xiaohongshu.com
when: manual # 生产环境手动触发
only:
- tags
7.3 监控告警体系
监控四层模型
| 层次 | 监控内容 | 工具 |
|---|---|---|
| 基础设施层 | CPU/内存/磁盘/网络 | Prometheus + Node Exporter |
| 容器层 | Pod状态/资源使用/重启次数 | cAdvisor + kube-state-metrics |
| 应用层 | QPS/延迟/错误率/饱和度 | APM (Jaeger/SkyWalking) |
| 业务层 | DAU/发布量/互动率/GMV | 自研BI平台 + ClickHouse |
核心告警规则
# Prometheus告警规则
groups:
- name: xhs-alerts
rules:
# 接口错误率告警
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service) > 0.05
for: 3m
labels:
severity: critical
annotations:
summary: "{{ $labels.service }} 错误率超过5%"
# 响应延迟告警
- alert: HighLatency
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.service }} P99延迟超过2秒"
# Pod重启告警
- alert: PodCrashLoop
expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
for: 1m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.pod }} 频繁重启"
# 数据库连接池告警
- alert: DBConnectionPoolExhausted
expr: db_pool_active / db_pool_max > 0.85
for: 2m
labels:
severity: warning
annotations:
summary: "{{ $labels.service }} 数据库连接池使用率超过85%"
7.4 容灾与高可用方案
多活架构
多活部署方案: ┌─────────────────────────────────────────────┐ │ 全局DNS调度 │ │ (基于地理位置的智能路由) │ └─────────────────────────────────────────────┘ │ │ ┌────▼────┐ ┌───▼─────┐ │ 华东机房 │ │ 华北机房 │ │ (主活) │◀────────▶│ (主活) │ │ 上海 │ 数据同步 │ 北京 │ └────┬────┘ └───┬─────┘ │ │ ┌────▼────┐ ┌───▼─────┐ │ 华南机房 │ │ 西南机房 │ │ (热备) │ │ (冷备) │ │ 广州 │ │ 成都 │ └─────────┘ └─────────┘ 容灾切换: - 正常情况: 用户就近接入 - 机房故障: DNS自动切换到备用机房 (< 30秒) - 数据同步: MySQL主从 + Redis跨机房复制 + Kafka Mirror
故障演练方案
- 混沌工程:定期注入故障(宕机、网络延迟、磁盘满)
- 全链路压测:每月进行生产环境全链路压测
- 红蓝对抗:安全团队定期进行攻防演练
- 应急预案:每个核心服务都有详细的应急SOP
7.5 安全防护体系
安全防护层次
| 层次 | 防护措施 | 实现方式 |
|---|---|---|
| 网络层 | DDoS防护、WAF、IP黑白名单 | 云安全中心 + 自研规则引擎 |
| 传输层 | HTTPS/TLS1.3、证书管理 | Let's Encrypt + 自研证书管理平台 |
| 应用层 | SQL注入/XSS防护、参数加密 | ORM参数化 + CSP + 请求签名 |
| 数据层 | 敏感数据加密、脱敏展示 | AES-256加密 + 动态脱敏 |
| 用户层 | 风控系统、反作弊、账号安全 | 规则引擎 + 机器学习模型 |
接口安全设计
// 请求签名防篡改
class RequestSigner {
static sign(params, secret) {
// 1. 参数排序
const sortedParams = Object.keys(params).sort()
.map(key => `${key}=${params[key]}`)
.join('&');
// 2. 拼接密钥
const signStr = sortedParams + '&key=' + secret;
// 3. HMAC-SHA256签名
return crypto
.createHmac('sha256', secret)
.update(signStr)
.digest('hex');
}
}
// 客户端请求携带签名
{
"user_id": 12345,
"timestamp": 1699900000,
"nonce": "random_string",
"sign": "a1b2c3d4..." // 服务端验签
}
// 防重放攻击
// 1. timestamp检查: 请求时间不能超过当前时间5分钟
// 2. nonce检查: Redis存储已使用的nonce,防止重放
// 3. sign验证: 确保请求未被篡改