第2章 审计SDK架构设计:核心模块拆分与分层依赖
我记得第一次接手企业级审计SDK改造时,最头疼的不是写代码,而是理清“这玩意儿到底该拆成几块”。
很多团队一上来就往一个类里塞几百行逻辑——采集也写、传输也写、存储也写,结果后期维护简直是地狱。今天咱们就聊聊,怎么把审计SDK拆得干净利落。
2.1 为什么需要分层架构
说白了,分层是为了让每个模块只操心自己的事。我见过一个项目,数据采集和存储搅在一起,换个数据库底层就得改整个SDK——这谁受得了?
- 解耦:采集模块升级不影响传输和存储
- 复用:同一套采集代码可以对接不同审计中心
- 可扩展:未来加个压缩逻辑,只动传输层就行
个人经验:我的习惯是先画依赖图,再写代码。依赖方向搞清楚了,后面基本不会翻车。
2.2 核心模块拆分
审计SDK通常拆成四个核心模块。咱们一个一个过。
2.2.1 数据采集模块
采集层负责从业务系统里捞出审计所需的数据。你想想看,哪些数据需要审计?
- 用户登录/登出操作
- 关键业务数据的新增、修改、删除
- 权限变更动作
- 系统配置变更
实现方式上,我建议用切面(AOP)或拦截器模式。这样业务代码零侵入,采集逻辑统一收口。
// 伪代码示意
@Aspect
public class AuditCollector {
@Around("@annotation(auditLog)")
public Object collect(ProceedingJoinPoint pjp, AuditLog auditLog) {
// 1. 提取方法参数、用户上下文
// 2. 构建审计事件对象
// 3. 异步发送到传输模块
return pjp.proceed();
}
}
我曾经踩过一个坑:采集模块里用了同步阻塞队列,结果业务高峰期把主线程堵死了。后来改成环形缓冲区+异步刷盘,才扛住流量。
2.2.2 数据传输模块
采集到的数据不能丢,也不能堵。传输层要解决两个核心问题:可靠性和吞吐。
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 直接HTTP推送 | 简单直接,数据量小 | 开发测试环境 |
| 消息队列缓冲 | 削峰填谷,异步解耦 | 生产高并发场景 |
| 本地文件暂存+定时上报 | 极端网络不稳定时保底 | 弱网环境 |
我个人推荐消息队列+本地缓存降级的组合方案。数据先进队列,消费端批量上报;如果队列挂了,本地文件兜底——这叫“双保险”。
2.2.3 数据存储模块
存储层要思考:审计数据往哪放?放多久?怎么查?
嗯,这里要注意一点:别把审计数据跟业务数据混在一个库里。审计日志是写多读少、只追加不修改,用时序数据库(如InfluxDB、TimescaleDB)或者Elasticsearch都挺好。
- 实时写入:业务侧不关心存储细节,丢给存储层处理
- 分片+过期策略:按时间分片,自动清理超过留存周期的数据
- 压缩存储:重要字段保留,非关键字段可序列化为JSONB/Protobuf
实战建议:我在项目中用过Elasticsearch存储审计事件,按天建索引,查询响应基本在200ms以内。但如果数据量超过10亿行,建议上冷热分层——热数据ES,冷数据归档到对象存储。
2.2.4 数据分析模块
分析模块是审计SDK的“大脑”。它把原始事件变成可用的审计痕迹。
说白了,你采集了一堆“谁在什么时间干了什么”,但原始数据太粗糙。分析模块要做几件事:
- 事件聚合:把连续操作合并成一次会话
- 风险标记:发现异常行为(比如凌晨批量删数据)立刻告警
- 报表生成:按部门、时间、操作类型拉取统计视图
分析模块应该跑在异步消费者里,不能阻塞采集和存储。我见过有人把分析逻辑塞在采集线程里,结果前端接口响应慢了3秒——这不合理。
2.3 分层依赖与调用关系
四个模块不是平级的。它们有明确的依赖链。
你发现没?箭头只从上往下走。采集层不关心数据怎么发出去的,传输层不关心数据存在哪,存储层不关心分析怎么做——这就是分层架构的精髓。
2.4 核心模块调用流程
咱们盘一盘一次完整调用的路径:
- 用户在业务系统执行操作
- 采集模块切面拦截,构建审计事件
- 事件入队到传输模块(异步,不阻塞业务)
- 传输模块把事件发送到存储层(失败重试+本地文件兜底)
- 存储模块写入数据库,返回写入确认
- 分析模块从存储层拉取事件,做聚合和规则匹配
关键点:每个步骤都支持超时和熔断。我记得有一次传输层MQ集群挂了,本地文件兜底扛了2小时,业务完全没感知——这就是架构的容错能力。
2.5 小结:模块协作的设计原则
说了这么多,落地时记住几条原则就行:
- 调用链必须统一超时,层与层之间通过接口隔离
- 存储模块保持无状态,方便横向扩展
- 分析模块跑在独立线程池里,跟采集线程彻底分开
- 别想着“未来可能用到”就提前设计,保持克制
嗯,模块拆分说到底就是个“边界感”问题。把每层该做的事圈死,不该做的事碰都不要碰。下一回你写SDK的时候,不妨先拿张纸画一画依赖图——相信我,这比debug省时多了。