第4章:SDK模块化设计——核心层、路由层、配置层、监控层
很多团队做SDK,一开始就是个“大泥球”。
所有代码揉在一起,灰度逻辑、配置加载、上报埋点全在一个类里。嗯,我见过最夸张的一个文件有8000行。改个配置读取方式,能把流量分发逻辑搞挂。说白了,模块化不是“好看”,是让你在灰度发布失败时,能精准切走那根出问题的神经,而不是把整个胳膊砍掉。
4.1 为什么必须是四层?
我个人习惯把SDK拆成四层:核心层、路由层、配置层、监控层。每一层只干一件事,层间通过定义好的接口通信。为什么是四层,不是三层或五层?
你想想看,灰度发布中流量从哪里来、往哪里去、怎么判断用哪个版本、出事了怎么知道——刚好对应这四个问题。少一层,责任就模糊了。多一层,调用链就复杂了。
我曾经遇到一个事故:配置层改了JSON解析库,结果核心层的初始化因为依赖冲突全挂了。从那以后,我规定配置层只能用纯数据接口,不允许引入任何业务依赖。
4.2 核心层(Core)
核心层是SDK的心脏。它封装了最底层的业务能力——比如网络请求、数据加解密、本地缓存。这一层不关心灰度,不关心版本号,只负责把事情“做对”。
举个例子。我们做推送SDK,核心层就提供三个能力:
- 建立长连接——TCP连接的建立、心跳、重连
- 消息编解码——Protobuf序列化/反序列化
- 本地存储——把离线消息写到SQLite
灰度发布的时候,路由层告诉核心层“你现在用V2.1的协议版本”,核心层只需要知道怎么编解码V2.1的包,不需要知道为什么要用V2.1。
// 核心层接口示例
public interface CoreEngine {
// 建立连接,不关心灰度
boolean connect(String host, int port);
// 发送数据,只关心编解码
boolean send(Message msg);
// 注册监听,只关心事件通知
void registerListener(CoreListener listener);
}
4.3 路由层(Router)
路由层是灰度发布的“大脑”。它的职责很单一:根据规则,决定流量走哪个版本的逻辑。
我在项目中遇到过一种常见场景:同一个用户,在iOS和Android上需要走不同的灰度比例。路由层就是干这个的——它读取配置层的规则,对每个请求做判断,然后把流量分到对应的核心层实例上。
路由层常见的判断依据:
- 用户ID哈希——通过Hash取模,实现一致性分流
- 设备类型——Android/iOS/鸿蒙各自不同版本
- 地域——先灰度某个城市
- 自定义标签——比如白名单用户
| 路由策略 | 适用场景 | 注意点 |
|---|---|---|
| Hash取模 | 全量灰度,比例固定 | 要确保Hash结果稳定,不要因字段变化导致分流漂移 |
| 标签匹配 | 内测、白名单 | 标签数据要同步到客户端,注意隐私合规 |
| 比例采样 | 渐进式灰度 | 配合监控层,当错误率超过阈值时自动切回 |
4.4 配置层(Config)
配置层是整个灰度体系的“遥控器”。它负责从远端拉取灰度规则,缓存到本地,然后在适当时机推给路由层。配置层不能挂,挂了SDK就失去了调整能力。
我设计的配置层通常包含:
- 配置拉取——HTTP轮询或长连接推送
- 本地缓存——防止网络不通时SDK“失忆”
- 版本管理——配置自身的版本号,避免重复应用
配置层最容易被忽视的问题是:配置格式的前向兼容。我记得有次团队新增了一个字段,老版本的SDK解析报错直接挂了。从那以后,配置层必须用宽松模式解析JSON——不认识的就跳过。
// 配置层接口示例
public interface ConfigProvider {
// 获取灰度规则列表,返回不可修改的副本
List<GrayRule> getGrayRules();
// 注册配置变更监听器
void registerListener(ConfigChangeListener listener);
// 手动触发一次拉取
void refresh();
}
4.5 监控层(Monitor)
监控层是SDK的“体检报告”。没有它,灰度就是盲人摸象。监控层采集三类核心指标:成功率、延迟、稳定性。
我曾经遇到过一个问题:灰度版本成功率99.9%,但是平均延迟从200ms飙到了2s。如果没有延迟监控,这问题可能要等用户投诉才知道。所以监控层必须三个维度都覆盖。
监控层的核心能力:
- 指标采集——埋点上报,涵盖核心层、路由层、配置层的所有关键路径
- 异常告警——当某指标超过阈值时,自动触发告警
- 自动回滚——结合路由层,当错误率超过预设值时自动切回上一个稳定版本
| 指标类型 | 采集点 | 告警阈值 |
|---|---|---|
| 成功率 | 核心层的请求/响应 | 低于99%且持续1分钟 |
| 延迟 | 核心层的耗时 | P99超过500ms |
| 稳定性 | SDK崩馈率 | 单日崩馈率超过0.1% |
关于自动回滚,我有个惨痛教训。一开始我设了“失败率超过5%就回滚”,结果某个CDN节点波动导致全量回滚,事故反而扩大了。现在我的做法是:回滚前观察至少2个独立数据中心的数据,确认不是偶发问题再执行。
模块化设计,说白了就是把复杂问题拆成四个独立的小问题。配置层管规则,路由层管决策,核心层管执行,监控层管反馈。每层只做一件事,才能保证灰度发布时,你能快速定位问题并安全回滚。嗯,这个骨架搭好了,后面的章节我们就在这四层之上构建真正的灰度策略。