C 端 IPTV 产品的 Java 高并发架构设计:高可用、高性能、零信任与缓存一致性
- 约 23 分钟读完 - 全文 9094 字C 端 IPTV 产品的 Java 高并发架构设计:高可用、高性能、零信任与缓存一致性
过去几年我一直在做一个面向海外的 C 端 IPTV 产品(STB 机顶盒 + 移动端,多品牌多租户,跑在 AWS 上)。这篇文章是我对这套系统架构的复盘:高并发、高可用、高性能、高安全、缓存一致性这几件事我们分别是怎么做的、踩过什么坑。过程中我们落地了不少 Netflix 的开源组件和公开实践(Hystrix、EVCache、Zuul 的网关思想、混沌工程等),但每一个决策都是经过我们自己生产环境验证或修正的,不是照搬。文中方案细节来自真实的系统设计文档(Video Cloud 目标模型、vod-auth、CloudRouter、DRM、网盘整改等),脱敏后分享。
一、先搞清楚对手:C 端 IPTV 的流量模型
设计之前先看清流量的形状。这是我做这套系统第一天就逼自己回答的问题:
| 特征 | 说明 | 架构含义 |
|---|---|---|
| 潮汐效应极强 | 晚 8 点 - 11 点是高峰,大促、球赛、爆款剧上线时流量成倍脉冲 | 弹性伸缩 + 削峰,按高峰容量做预案 |
| 读极多、写极少 | EPG 节目单、Meta 元数据、Layout 下发占了 9 成以上流量 | 缓存优先,读路径尽量不打 DB |
| 播放鉴权是超级热点 | 每次起播、切台都要鉴权取播放链接,QPS 随在线用户数线性涨 | 热点接口单独优化(缓存 + 短 TTL + 滑动续期) |
| 设备在线心跳 | app 轮询 session,百万级设备定时上报 | 心跳服务独立部署,写路径异步化 |
| 多品牌多租户 | 同一套系统服务多个品牌(巴西、俄语区等) | 分库隔离爆炸半径,account_id 前缀区分品牌 |
这个流量形状——读多写少、晚间高峰、播放路径是生命线——决定了后面所有的架构取舍。
二、总体架构:一张图看清全貌
graph TB
subgraph 终端
STB["STB 机顶盒"]
APP["移动 App"]
WEB["Web / Portal"]
end
subgraph 接入层
ELB["AWS ELB"]
APISIX["APISIX 网关<br/>限流 Sentinel / WAF / 鉴权前置"]
end
subgraph 微服务层
AUTH["vod-auth<br/>登录注册 / 账号 / 子账号"]
SESS["common-session<br/>会话管理"]
META["vod-meta-api<br/>元数据 / Layout / EPG"]
PROF["profile<br/>观看历史 / 收藏 / 偏好"]
CLOUD["vod-cloud<br/>个人网盘"]
ROUTER["CloudRouter<br/>播放链接路由"]
DRM["drmlicense<br/>DRM 鉴权与 license 下发"]
end
subgraph 数据层
MYSQL["MySQL RDS<br/>按品牌分库"]
REDIS["Redis Cluster<br/>Session / 验证码 / Meta 缓存"]
METAREDIS["Meta 专用 Redis Cluster"]
ES["Elasticsearch<br/>检索字段"]
KAFKA["Kafka MSK<br/>Meta 同步 / 网盘同步"]
end
subgraph 内容侧
STREAM["Streaming 流媒体"]
CDN["CDN / Squid 边缘缓存"]
CP["cloudProxy<br/>第三方网盘适配"]
end
STB --> ELB
APP --> ELB
WEB --> ELB
ELB --> APISIX
APISIX --> AUTH
APISIX --> SESS
APISIX --> META
APISIX --> PROF
APISIX --> CLOUD
APISIX --> ROUTER
ROUTER --> DRM
ROUTER --> STREAM
STREAM --> CDN
STREAM --> CP
META --> METAREDIS
META --> ES
AUTH --> MYSQL
AUTH --> REDIS
SESS --> REDIS
CLOUD --> KAFKA
META --> KAFKA
几个关键决策,后面逐章展开:
- 接入层:链路是
app -> ELB -> APISIX -> vod 微服务,限流用 Sentinel,网关做鉴权前置和 WAF。网关选型时我认真研究过 Netflix Zuul 2 从同步阻塞改异步非阻塞的历程——结论是网关本身绝不能成为瓶颈。我们体量没那么大,APISIX(底层同样是 Nginx + Lua 的高性能异步模型)已经够用。 - 服务拆分:按业务域拆(Auth / Profile / Meta / Cloud / Session),不是按技术层拆。领域拆分的完整过程在视频系统 DDD 实战里写过。
- 数据层:MySQL 按品牌分库(
{brand}_account_db等);Redis 承载 Session、验证码、热点数据;Meta 用独立的 Redis Cluster 全量缓存,与业务缓存物理隔离;检索字段入 ES。 - 异步化:Kafka(MSK)只干两件事——Meta 同步管道和网盘同步,不滥用。这是我给自己立的规矩:每加一个 MQ topic 都要回答「同步调用为什么不行」。
三、高并发:把流量挡在离用户最近的地方
高并发的核心心法就一句:每一层都尽量把请求消化掉,别让它穿透到下一层。越靠近 DB 越贵。
3.1 网关层:第一道削峰闸
我们在网关层做了三件事:
- 限流:APISIX 自带 limit-req / limit-count 插件做粗粒度限流,Sentinel 做服务级细粒度熔断限流(现网组合就是「ELK + Skywalking + Sentinel(APISIX)」)。
- 鉴权前置:明显非法的请求(无 token、签名错误、UA 黑名单)在网关直接 401/403,不放行到业务服务。被攻击时这一层挡掉的流量经常比业务层处理的还多。
- 静态配置下发走 CDN:
/ui/cfg/v1、/layout/v1这类终端配置接口,内容变化频率低,直接走 CDN 缓存,源站只承担回源请求。
3.2 热点接口:播放鉴权链接的缓存设计
CloudRouter(播放源路由服务)是我手里最热的路径:每次起播都要按 logicStreamId 换真实的下载/播放链接。我做的设计非常直接:
graph LR
A["App 起播请求<br/>logicStreamId"] --> B{"查 Redis"}
B -- "命中" --> C["刷新 TTL 滑动续期<br/>直接返回链接"]
B -- "未命中" --> D["查 MySQL 两表<br/>logic_stream + file_source"]
D --> E["构建下载链接 DownloadBO<br/>link / fileSize / expired"]
E --> F["写回 Redis"]
F --> G["返回"]
三个细节值得说:
- 命中即续期(滑动 TTL):热播内容的链接永不过期,冷内容自然淘汰——不需要人工预热。这个设计替我省掉了整个「热点内容运营」的麻烦。
- 缓存对象自带过期时间:链接本身有
expired字段,客户端在链接到期前会重新请求,和缓存 TTL 是两层独立的过期机制,互不相信。 - 失败快速抛出:查不到直接抛业务异常,不做 fallback 到第三方存储的慢路径——播放链路每一毫秒都值钱。
3.3 多级缓存:L1 + L2
纯 Redis 方案在超热点 key 上依然会打满单个分片(新片上线当晚的 metaId 我真实遇到过)。为了解决这个问题我研究了 Netflix 的 EVCache——它的多副本加客户端本地缓存思路给了我启发,但我们体量用不上 EVCache 全套,于是我基于 Caffeine(L1 进程内缓存)+ Redis(L2)自己实现了一个两级缓存框架并开源,见我开发的 keCache:面向 Spring Boot 3 的 L1 + L2 两级缓存框架,要点:
- L1 短 TTL(秒级),扛住瞬时洪峰;
- L1 失效消息走 Redis Pub/Sub 广播,保证多实例间 L1 不一致的窗口可控;
- 序列化用 Kryo,比 JDK 序列化小 3-5 倍。
3.4 写路径异步化:MQ 削峰
写少但集中的场景(新片上线批量导入 Meta、网盘大规模刷新)我全部走 Kafka:
graph LR
A["vod-sync-dispatcher<br/>每 30 秒轮询上游片源"] --> B["Kafka MSK"]
B --> C["vod-imdb-meta-sync<br/>元数据入 Redis + ES"]
D["vod-cloud-sync-dispatcher"] --> B
B --> E["vod-cloud-sync-worker<br/>刷新网盘"]
F["http2Msk<br/>大数据 HTTP 回调转 MQ"] --> B
B --> G["偏好计算<br/>you may like"]
生产者-消费者模型 + MQ 缓冲,让上游脉冲(片源方一次推几万条 meta)被摊平成消费者能吃得动的匀速流。当时的 MQ 选型过程见消息队列总结:RabbitMQ、Kafka、RocketMQ 与 Redis MQ。
另一个异步化的例子是我设计的转码调度:Quartz + DB 状态机驱动,每个片源转出 1-3 个分辨率、每个分辨率一个任务,用「动态归一化权重调度算法」分配执行器(priority = 客户权重 / 活跃任务数,兼顾多客户公平与并发槽位控制),30 分钟轮询任务进度。慢不要紧,稳和公平才要紧——这是典型的「离线任务用状态机换可靠性」。
四、高可用:假设一切依赖都会挂
我做高可用的两条心法,是从 Netflix 的工程实践里学来并在我们系统里反复验证过的:Design for failure(为失败而设计)和 Blast radius(控制爆炸半径)。落地到我们系统:
4.1 熔断、降级与兜底重试
- 熔断:我们早期直接用 Netflix 开源的 Hystrix 做熔断隔离,Hystrix 停更后迁移到 Resilience4j(网关层用 Sentinel)。工具换了,规则不变:下游超时率/错误率超阈值就熔断,快速失败,绝不线程排队等死。Hystrix 的线程池隔离思想对我们影响很大——核心链路(登录、起播)和非核心链路(埋点上报)的资源池必须分开。
- 降级而非失败:Meta 自动匹配失败时我定的策略是降级创建默认元数据,不影响文件源状态——内容能播永远比信息完美重要。
- 兜底重试:CloudAsset 的异常处理是我给全系统定的统一模式——3 次以内本轮调度重试,超过 3 次任务进失败状态,由独立的失败任务调度器兜底。关键设计点:主流程不被重试拖死,失败任务有独立通道排队消化,互不阻塞。
graph TB
A["任务执行"] --> B{"成功?"}
B -- "是" --> C["完成"]
B -- "否, failCount+1"] --> D{"failCount > 3?"}
D -- "否" --> A
D -- "是" --> E["status = fail"]
E --> F["独立失败任务调度器<br/>定时扫描兜底重试"]
F --> A
后来我把这类「一定要跑到」的可靠执行逻辑抽成了通用组件开源,见我开发的 KE Reliable Executor。
4.2 多租户隔离:分库就是隔舱
多品牌共享一套代码,但我在数据层做了物理隔离:
- MySQL 按品牌分库(
{brand}_account_db、{brand}_profile_db),公共数据在vod_meta_db; account_id前缀区分品牌(MFC:11、TVELA:12、WAKA:13),应用层强制路由;- 实体普遍带
env/vnoId字段,logicStreamId内嵌环境标识。
某品牌数据被打爆、误删、需要回滚时,影响面天然被限制在单库内。这就是水密隔舱——后来某品牌做数据订正时我真切体会到这个设计的价值。
4.3 依赖解耦:订阅模式
DRM 链路里我有个坚持:drmlicense 不直接依赖 Headend 和 drmencryptor 的在线接口,而是以订阅模式收 DrmContentInfoBO 和 ContentKeyBO,自己组装 logicalStream 与 PSSH 的关系。上游挂了,license 下发用已订阅到的数据照常工作。原因是算术:同步调用是可用性的乘法,异步订阅是加法——一个可用性 99.9% 的服务同步串依赖十个同样 99.9% 的服务,链路可用性就只剩 99% 了。
4.4 混沌工程:轻量版就够
Netflix 的 Chaos Monkey 是随机杀生产实例,逼所有服务习惯「邻居随时消失」。我评估后没敢直接上生产,但保留了这个思想的两个轻量实践:预发环境定期随机关停单个实例观察告警与自愈;每个季度做一次依赖演练(手动切断 Redis 从库、Kafka 单 broker),验证降级路径真的能用。我的原则:降级方案不演练就等于没有——每次演练几乎都能发现某条降级路径已经悄悄失效。
五、高性能:把钱花在读路径上
高性能章节不长,因为大部分事前面已经做了。补几个我做过的针对性的点:
| 优化点 | 做法 |
|---|---|
| Meta 全量入内存 | 元数据全量进专用 Redis Cluster,终端 /root/v1、/tree/v1 等接口零 DB 访问 |
| 检索走 ES | 只有关键检索字段入 Elasticsearch,不全量双写 |
| 播放内容边缘缓存 | Streaming 前置 Squid + CDN,热点片源命中边缘节点,源站只承担回源(对应 Netflix Open Connect 的角色,我们用商用 CDN 实现) |
| 大表治理 | 观看历史表现网 2-3 亿行,是我一直盯着的最大风险点:每天定时任务删 90 天前历史 + 按账号双表拆分(设备账号/普通账号) |
| JVM 与连接池 | G1/ZGC、HikariCP 连接池按「连接数 = (核数 × 2) + 磁盘数」起步压测调优,慢 SQL 进 Skywalking 告警 |
原则:性能优化的 80% 是让请求根本到不了慢的地方,剩下 20% 才是调参数。
六、缓存一致性与缓存高可用
这是全文我最想细说的一章。缓存是性能的来源,也是我事故最多的地方。
6.1 一致性策略:先写 DB,后删缓存
vod-auth 的账号数据我用的是标准的 Cache Aside + 写后删除:先写 DB,成功后再删缓存,下次读时回源重建。为什么选「删除」而不是「更新」?我想清楚过:「先写 DB 再删缓存」只有在「读线程恰好踩着写间隙回源了旧值」的极端窗口才会短暂不一致,而「更新缓存」在并发双写下会乱序覆盖出脏数据。删除的失败模式是 miss(回源一次,便宜),更新的失败模式是脏数据(播错内容,贵)。
对一致性要求更高的场景(套餐权益、付费状态),我在删除之上再加一层:
- 延迟双删:写 DB → 删缓存 → 延迟 N 秒 → 再删一次,兜住间隙回源;
- 或用 MQ 发缓存失效消息,由消费者负责删除 + 失败重试,把「删缓存失败」这个尾巴也闭环掉。
6.2 缓存三大经典问题的组合拳
| 问题 | 我遇到的场景 | 解法 |
|---|---|---|
| 穿透 | 恶意刷不存在的 metaId / accountId | 布隆过滤器拦截 + 空值短 TTL 缓存(30 秒) |
| 击穿 | 爆款剧上线,热点 key 过期瞬间 | 逻辑过期 + 互斥重建:value 里带逻辑过期时间,过期后只放一个线程回源,其余返回旧值 |
| 雪崩 | 大量 key 同 TTL 同时到期 | TTL 加随机抖动(基础值 ± 20%)+ Redis Cluster 多主分摊 |
逻辑过期方案有个工程细节:回源重建要用分布式锁(Meta 绑定我用的就是分布式锁,获取失败快速失败、重新调度),且锁的 TTL 必须大于回源耗时,finally 里释放——我在 CloudAsset 方案里专门写了「任何异常都会释放分布式锁、清除 ThreadLocal」,这是血泪经验,锁泄漏一次就够排查一晚上。
6.3 缓存自身的高可用
- Redis Cluster 多主 + 从:Meta 缓存我用独立集群,和 Session/验证码的集群物理隔离——Meta 集群被打挂不影响用户登录。
- 多级缓存兜底:Redis 整体不可用时,L1(Caffeine)还能扛住读过期窗口,服务降级而不是全灭。
- 从 EVCache 学到的运营观:我研究 Netflix EVCache 时最大的收获不是 memcached 本身,而是他们把缓存当成 tier-0 服务运营——独立容量规划、独立监控、独立 SLO。我照这个思路给缓存集群配了和业务服务同级别的告警与容量评审。缓存在高并发系统里不是优化项,是一级基础设施——这点很多团队要到出事才懂,我庆幸自己学得早。
七、零信任安全与必要防御
7.1 零信任:永不信任,始终验证
我们的安全体系是在几次整改后逐步按零信任思路重建的——内网不再等于可信,每一次调用都要验证身份,不论它来自哪里。落地到我的系统,是这几件事:
- 用户侧:accessToken(JWT,
sub = accountId)+ 播放票据 tk12(内含expireTime / accountId / streamId,由平台密钥签发)。App 起播时两个都要验,token 验「你是谁」,tk12 验「你能不能播这条流、能播到什么时候」。 - 设备侧:账号卡与设备绑定(
ac_device_binding),device_limit限制并发设备数,挤掉盗号共享的盈利空间。 - 服务间:内部接口(
/internal-api/...)不走公网网关,服务间调用带服务身份凭证。我评估过 mTLS + SPIFFE 这套工作负载身份方案(Netflix 零信任改造用的就是这条路),对我们体量偏重,最终落地为「内部接口不暴露 + 调用方白名单」,等规模上来再升级。 - 最小权限:cloudProxy 是我专门做的收敛层——把对第三方网盘(Mega 等)的所有访问收敛成一个适配层,只暴露
checkPassword和getDownloadLink两个接口,业务服务根本不接触第三方凭证。
7.2 一次真实的安全整改:网盘密码泄露
这是我亲手推动的整改,值得完整复盘。背景:网盘打包工具生成的加密网盘包里含账号 + 加密后的登录密码,加解密密钥一旦泄露,所有网盘账号密码全部泄露。
整改分两步走:
第一步,密码不落地。 新网盘包不再写密码,改为写入与网盘账号一一对应的 shareKey(UUID)。导入时只校验 shareKey 一致性,通过后把网盘与用户关联。按网盘包 version 字段区分新老包走新旧两条校验逻辑,平滑迁移——存量兼容是安全改造里最容易被低估的工作量,这一步我花的精力比新逻辑本身还多。
第二步,密钥体系升级。 对称加密改 RSA 非对称加密:
graph TB
A["CP / Reseller<br/>只拿到 RSA 公钥"] --> B["公钥加密网盘包<br/>公钥泄露也无法解包"]
C["VNO 上传私钥<br/>加密保存于平台 pkg_rsa_key 表"] --> D["导入时按 VNO 查私钥"]
B --> E["网盘包带签名<br/>含 pkgId + keyId"]
E --> D
D --> F["先验签再解密<br/>解密成功才关联用户"]
配套我设计的密钥轮换机制:平台同时最多两个 Key 生效;更新时旧密钥先进入最长 30 天缓冲期,再生成新密钥对;存在生效中的旧密钥则不允许再次更新——防止轮换频率失控导致历史包无法解密。
另外扫码登录链路我也推动做过同类加固:扫码站点从 S3 静态托管(明文登录)迁移到托管服务并接入加固方案,鉴权统一收口到 vod-auth。
7.3 接口消息体加密:HTTPS 之上的第二道锁
有人会问:全站都上 HTTPS 了,为什么还要在应用层再加密一次消息体?我的理由有三个,都来自实际教训:
- HTTPS 防不住终端侧的抓包。Charles/Fiddler 挂个代理证书,App 和网关之间的明文报文一览无余——协议字段、业务参数、token 用法全暴露。C 端 App 发出去的那一刻起,客户端环境就不可信。
- HTTPS 到网关就终止了。TLS 在 ELB/APISIX 卸载后,内网转发的是明文;零信任的原则是内网也不可信,敏感报文在网关之后依然应该处于加密状态。
- IPTV 场景的特殊性:播放鉴权、充值、账号接口一旦被逆向出报文格式,黑产就可以脱离 App 直接伪造请求刷接口。
我设计的方案是 TLS 为底线 + 应用层 AES 加密消息体,核心链路如下:
graph LR
A["App 启动<br/>ECDH 密钥协商<br/>网关公钥经 SSL Pinning 校验"] --> B["得到会话密钥 sessionKey<br/>按 session 绑定"]
B --> C["业务请求<br/>AES-256-GCM 加密 body"]
C --> D["明文头带 token + 时间戳<br/>+ nonce + HMAC 签名"]
D --> E["网关统一解密过滤器<br/>验签名 / 验时间窗 / 验 nonce"]
E --> F["业务服务收到明文<br/>响应同样加密回传"]
工程上的几个取舍,都是试出来的:
- 密钥协商用 ECDH,不用硬编码密钥。早期图省事在 App 里内置过 AES 密钥,App 被反编译后全线失守,改成登录/启动时 ECDH 动态协商、密钥绑定 session、服务端定期强制轮换。SSL Pinning(证书固定)挡住大部分中间人代理,让 ECDH 协商本身不被劫持。
- AES-256-GCM 而不是 CBC:GCM 自带完整性校验(AEAD),加密和防篡改一次完成;密钥 nonce 绝不复用。
- 头部明文、body 加密:token、时间戳、nonce、签名放明文头里,网关不解密就能先做限流和鉴权前置;body 整体加密,解密收敛在网关的统一过滤器里,业务服务无感知——加解密逻辑散落进每个 Controller 是维护灾难。
- 不是所有接口都加密。EPG、Meta 这类公开内容接口只走 HTTPS + 签名;登录、充值、播放鉴权、账号操作这些敏感接口才上消息体加密。全量加密的 CPU 成本和排查问题的痛苦不成比例——加密后的日志排障要靠专门的脱密工具,这也是成本。
- 绝不自研算法。所有原语都用成熟实现(AES-GCM、ECDH、HMAC-SHA256),自己拼协议可以,自己造算法不行。
7.4 必要防御:防攻击清单
| 攻击面 | 我们的防御手段 |
|---|---|
| 接口刷量 / CC 攻击 | 网关限流 + Sentinel 熔断;登录、验证码接口按 IP + 账号双维度计数 |
| 短信/邮件轰炸机 | 验证码存 Redis 带 TTL,验证成功后立即删除防重放;单账号/单 IP 频次限制 |
| 重放攻击 | 请求带时间戳 + nonce,token 短过期 |
| 抓包 / 中间人 | HTTPS + SSL Pinning + 敏感接口消息体加密(见 7.3) |
| 越权访问 | tk12 绑定 accountId + streamId,换账号播放直接鉴权失败 |
| 篡改网盘包 | RSA 签名验签,验签不过不解密 |
| 撞库 / 盗号 | 登录失败计数锁定(account_black_list)、设备绑定、子账号体系隔离 |
| 敏感信息外泄 | 交接文档脱敏(我们的交接文档里曾出现明文环境密码,是我抓出来的典型反面教材);日志脱敏;DB 密码字段只存哈希 |
7.5 DRM:内容安全的最后一道门
DRM 链路的安全设计我同样按零信任思路做:ContentKey 由独立密钥服务(drmencryptor)管理,先查缓存没有再向外部 DRM 厂商申请并落库缓存;播放优先走离线 license(有缓存用缓存),PSSH 异常才降级在线 license。license 本身带过期时间,App 本地不持久化密钥。系统支持 Goose DRM 与 Multi-DRM(Widevine)切换,切换动作就是删旧流加新流——密钥体系与业务流解耦,这个设计让后来的 DRM 厂商迁移几乎没动业务代码。
八、可观测性:没有度量就没有高可用
我们的现网组合:ELK 日志 + Skywalking 链路追踪 + Prometheus/Grafana 指标 + Nacos 配置中心,每个子系统独立 ES index。
我压测过的容量数据(v7.16 集群):只写日志约 11 万条/秒,只写 Skywalking 约 2 万条/秒,混合写时日志 6 万 + 链路 1 万/秒。
血泪教训:周一高峰 Skywalking 曾出现 4 小时写入延迟,日志量增长也让 Logstash 排队。事后我立了两条规矩:
- 日志与链路采样分离:高峰期链路数据降采样,保住日志;两者绝不能互相拖死。
- 本地日志保留 30 天,crontab 定时清理——磁盘写满导致的 ELK 雪崩比业务故障还常见。
我还坚持一点:告警要打在用户可感知的指标上(起播成功率、鉴权失败率、链接过期率),而不是只打 CPU 内存——机器好好的但用户看不了,是最尴尬的监控盲区。服务间的依赖关系复杂到一定程度后,「告警来了不知道谁影响了谁」会成为新问题,Netflix 为此专门做过一套服务拓扑系统,我研究过它的实现并整理了笔记:Netflix 服务拓扑系统笔记。
九、总结:一张 checklist
| 维度 | 我采用的核心手段 | 用到的 Netflix 开源组件 / 实践 |
|---|---|---|
| 高并发 | 网关限流、缓存优先、MQ 削峰、多级缓存 | Zuul 2 异步化思想、EVCache 启发 keCache |
| 高可用 | 熔断降级、失败兜底调度器、多租户分库隔离、订阅模式解耦 | Hystrix(后迁 Resilience4j/Sentinel)、Chaos Monkey 轻量版 |
| 高性能 | Meta 全量入内存、ES 检索、CDN/Squid 边缘缓存、大表治理 | Open Connect 的边缘缓存角色 |
| 缓存一致性 | 先写 DB 后删缓存 + 延迟双删、逻辑过期防击穿、随机 TTL 防雪崩 | EVCache 的缓存运营观 |
| 零信任 | 双票据鉴权、设备绑定、内部接口白名单、最小权限适配层 | 零信任改造思路(mTLS/SPIFFE 评估后暂缓) |
| 攻击防御 | 网关 WAF/限流、验证码防重放、RSA 验签、黑名单、敏感接口消息体加密 | — |
| 可观测 | ELK + Skywalking + Prometheus,日志链路采样分离 | 服务拓扑系统的思路 |
最后一句实话:这套架构没有一样是发明出来的,全部是踩坑踩出来的。网盘密码泄露、Skywalking 四小时延迟、2-3 亿行的历史大表——每个设计决策背后都有一次事故或一次险些事故。Netflix 的开源组件和博客帮我少踩了很多坑,但终究要自己踩过才知道坑有多深。架构师的职责不是画出完美的图,而是让每个坑只踩一次。