架构设计原则:实时特征平台的典型分层架构
做实时特征平台这几年,我最大的感触就是:架构设计决定了你能走多远。很多团队一开始图省事,把特征计算、存储、服务全揉在一个模块里,结果业务量一上来就崩。今天我就把实时特征平台的典型分层架构拆开来讲,顺便聊聊高可用和低延迟的那些坑。
一、为什么必须分层?
说白了,分层是为了解耦。实时特征平台要处理的数据流是持续的,每一层的职责不同,故障域也不同。我见过一个项目,计算层挂了导致存储层写爆,最后服务层也跟着雪崩——这就是没分层的后果。
实时特征平台通常分为四层:
- 接入层:负责数据源的接入、协议转换、流量控制
- 计算层:负责特征逻辑的实时计算(窗口聚合、JOIN、UDF等)
- 存储层:负责特征结果的存储与索引(KV存储、时序存储等)
- 服务层:负责对外提供特征查询接口(RESTful、gRPC等)
每一层都可以独立扩缩容,独立做高可用。嗯,这里要注意:层与层之间必须通过异步消息或轻量级RPC通信,千万别搞成同步调用链。
核心原则:每一层只做一件事,并且把这件事做到极致。接入层不计算,计算层不存储,存储层不对外暴露查询接口。
二、接入层设计要点
接入层是数据进入平台的第一道关卡。我个人习惯把接入层拆成两个子层:协议适配层和流量治理层。
2.1 协议适配
业务数据源千奇百怪:Kafka、RocketMQ、HTTP Webhook、数据库CDC……接入层要统一转换成内部的消息格式。我建议用Avro或Protobuf做序列化,别用JSON——解析性能差一个数量级。
2.2 流量治理
实时特征平台最怕什么?流量洪峰。我曾经遇到过一个场景:双十一大促期间,某个数据源突然暴增10倍流量,直接把计算层打挂了。后来我们在接入层加了三个机制:
- 限流:基于令牌桶或漏桶算法,保护下游不被冲垮
- 降级:非核心特征的数据源可以降级丢弃
- 背压:当下游处理不过来时,接入层要能感知并减速
避坑指南:我曾经在接入层用同步HTTP调用下游,结果下游慢导致接入层线程池耗尽。后来全部改成异步非阻塞模型(Netty + 异步回调),问题才解决。
三、计算层设计要点
计算层是实时特征平台的核心。这里我重点讲三个设计原则:状态管理、容错机制、资源隔离。
3.1 状态管理
实时计算离不开状态。比如你要算过去5分钟的点击率,就得维护一个滑动窗口的状态。我建议用RocksDB做本地状态存储,配合Chandy-Lamport快照算法做定期checkpoint。这样即使任务重启,也能从最近一次checkpoint恢复。
3.2 容错机制
计算节点一定会挂。我见过最典型的场景:Flink任务因为OOM挂掉,重启后从checkpoint恢复,结果因为状态太大恢复了一小时。后来我们做了两件事:
- 增量checkpoint:只保存变化的状态,减少恢复时间
- 本地恢复:优先从本地磁盘恢复,避免远程拉取
3.3 资源隔离
不同特征的计算逻辑可能差异很大。有些特征是简单的字段映射,有些是复杂的多流JOIN。我建议用Slot或TaskManager级别的资源隔离,避免一个计算任务把整个集群的资源吃光。
警告:千万不要把所有特征计算放在同一个Flink Job里。一旦Job挂掉,所有特征都会中断。按业务域拆分成多个Job,每个Job独立运维。
四、存储层设计要点
存储层负责保存特征计算结果。实时特征对存储的要求很特殊:写多读少、低延迟、高并发。
4.1 存储选型
我推荐用Redis Cluster + 本地缓存的组合方案。Redis负责持久化存储,本地缓存(比如Caffeine)扛读流量。为什么?因为特征数据有很强的时间局部性——刚写入的特征很快就会被查询到。
| 存储方案 | 写性能 | 读性能 | 持久化 | 适用场景 |
|---|---|---|---|---|
| Redis Cluster | 高(10万+ QPS) | 极高(百万+ QPS) | AOF/RDB | 在线特征查询 |
| HBase | 中(万级 QPS) | 中(万级 QPS) | HDFS | 离线特征回溯 |
| 本地缓存 | 极高 | 极高 | 无 | 热点特征加速 |
4.2 数据分片
特征数据通常按特征ID哈希分片。但要注意热点问题——有些特征(比如用户画像)会被频繁查询。我建议对热点特征做多副本,分散到不同分片上。
五、服务层设计要点
服务层是特征平台的出口,直接面对业务方。这里我重点讲查询性能和高可用。
5.1 查询性能
特征查询的延迟要求通常是毫秒级。我建议用多级缓存:
- L1缓存:进程内缓存(Caffeine),响应时间 < 1ms
- L2缓存:Redis,响应时间 1-5ms
- L3存储:HBase/MySQL,响应时间 10-50ms
查询时先查L1,没命中再查L2,最后查L3。同时要设置合理的过期时间,保证特征数据的时效性。
5.2 高可用
服务层的高可用设计包括:
- 无状态设计:服务节点不保存任何状态,可以随意扩缩容
- 负载均衡:用Nginx或Kong做流量分发,支持健康检查
- 熔断降级:当下游存储层异常时,服务层要能快速熔断,返回降级结果
个人经验:我曾经在服务层遇到一个坑——特征查询接口返回的数据量太大,导致网络带宽打满。后来我们加了字段裁剪功能,业务方可以指定只查需要的特征字段,带宽消耗降了80%。
六、高可用与低延迟设计要点总结
我把高可用和低延迟的设计要点整理成一张清单,方便你对照检查:
| 维度 | 设计要点 | 说明 |
|---|---|---|
| 高可用 | 冗余部署 | 每层至少2个副本,跨机房部署 |
| 故障自动转移 | 计算层用Flink的HA模式,存储层用Redis Sentinel | |
| 优雅降级 | 非核心特征降级,保证核心特征可用 | |
| 低延迟 | 异步化 | 层间通信用异步消息,避免同步阻塞 |
| 多级缓存 | L1进程缓存 + L2分布式缓存 | |
| 预计算 | 复杂特征提前计算好,查询时直接取结果 |
七、架构总览图
下面这张图展示了实时特征平台的完整分层架构和数据流:
这张图我画了很多遍,每次给团队讲架构时都会拿出来。你注意看:数据流是单向的,每一层只依赖下一层,不跨层调用。这样任何一层出问题,都不会扩散到其他层。
八、写在最后
实时特征平台的架构设计,说白了就是在延迟和可用性之间找平衡。你不可能既要毫秒级延迟,又要100%可用性——总要有所取舍。我个人习惯是:核心特征保证高可用,非核心特征追求低延迟。这样即使出问题,影响面也是可控的。
嗯,这一章的内容就到这里。记住:架构设计不是一蹴而就的,它需要随着业务的发展不断演进。刚开始可以简单点,但一定要留好扩展的余地。