架构设计原则:实时特征平台的典型分层架构

做实时特征平台这几年,我最大的感触就是:架构设计决定了你能走多远。很多团队一开始图省事,把特征计算、存储、服务全揉在一个模块里,结果业务量一上来就崩。今天我就把实时特征平台的典型分层架构拆开来讲,顺便聊聊高可用和低延迟的那些坑。

一、为什么必须分层?

说白了,分层是为了解耦。实时特征平台要处理的数据流是持续的,每一层的职责不同,故障域也不同。我见过一个项目,计算层挂了导致存储层写爆,最后服务层也跟着雪崩——这就是没分层的后果。

实时特征平台通常分为四层:

  • 接入层:负责数据源的接入、协议转换、流量控制
  • 计算层:负责特征逻辑的实时计算(窗口聚合、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分布式缓存
预计算 复杂特征提前计算好,查询时直接取结果

七、架构总览图

下面这张图展示了实时特征平台的完整分层架构和数据流:

实时特征平台分层架构图 接入层 协议适配(Kafka / HTTP / CDC) 流量治理(限流 / 降级 / 背压) 计算层 Flink / Spark Streaming 实时计算引擎 状态管理(RocksDB + Checkpoint) 资源隔离(Slot / TaskManager 级别) 存储层 Redis Cluster(在线特征) + HBase(离线回溯) 数据分片 + 多副本 + 本地缓存加速 服务层 RESTful / gRPC 特征查询接口 多级缓存(L1进程缓存 → L2 Redis → L3 HBase) 数据流方向:数据源 → 接入层 → 计算层 → 存储层 → 服务层 → 业务方

这张图我画了很多遍,每次给团队讲架构时都会拿出来。你注意看:数据流是单向的,每一层只依赖下一层,不跨层调用。这样任何一层出问题,都不会扩散到其他层。

八、写在最后

实时特征平台的架构设计,说白了就是在延迟和可用性之间找平衡。你不可能既要毫秒级延迟,又要100%可用性——总要有所取舍。我个人习惯是:核心特征保证高可用,非核心特征追求低延迟。这样即使出问题,影响面也是可控的。

嗯,这一章的内容就到这里。记住:架构设计不是一蹴而就的,它需要随着业务的发展不断演进。刚开始可以简单点,但一定要留好扩展的余地。