第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的“大脑”。它把原始事件变成可用的审计痕迹。

说白了,你采集了一堆“谁在什么时间干了什么”,但原始数据太粗糙。分析模块要做几件事:

  1. 事件聚合:把连续操作合并成一次会话
  2. 风险标记:发现异常行为(比如凌晨批量删数据)立刻告警
  3. 报表生成:按部门、时间、操作类型拉取统计视图

分析模块应该跑在异步消费者里,不能阻塞采集和存储。我见过有人把分析逻辑塞在采集线程里,结果前端接口响应慢了3秒——这不合理。

2.3 分层依赖与调用关系

四个模块不是平级的。它们有明确的依赖链。

审计SDK核心模块分层架构 业务系统 / 应用层 数据采集模块(AOP/拦截器) 数据传输模块(MQ / 本地Buff) 数据存储模块(ES / 时序DB) 数据分析模块(聚合 / 告警 / 报表) 依赖方向 采集 → 传输 传输 → 存储 存储 → 分析 每层只依赖下层 不跨层调用

你发现没?箭头只从上往下走。采集层不关心数据怎么发出去的,传输层不关心数据存在哪,存储层不关心分析怎么做——这就是分层架构的精髓。

2.4 核心模块调用流程

咱们盘一盘一次完整调用的路径:

  1. 用户在业务系统执行操作
  2. 采集模块切面拦截,构建审计事件
  3. 事件入队到传输模块(异步,不阻塞业务)
  4. 传输模块把事件发送到存储层(失败重试+本地文件兜底)
  5. 存储模块写入数据库,返回写入确认
  6. 分析模块从存储层拉取事件,做聚合和规则匹配

关键点:每个步骤都支持超时和熔断。我记得有一次传输层MQ集群挂了,本地文件兜底扛了2小时,业务完全没感知——这就是架构的容错能力。

2.5 小结:模块协作的设计原则

说了这么多,落地时记住几条原则就行:

  • 调用链必须统一超时,层与层之间通过接口隔离
  • 存储模块保持无状态,方便横向扩展
  • 分析模块跑在独立线程池里,跟采集线程彻底分开
  • 别想着“未来可能用到”就提前设计,保持克制

嗯,模块拆分说到底就是个“边界感”问题。把每层该做的事圈死,不该做的事碰都不要碰。下一回你写SDK的时候,不妨先拿张纸画一画依赖图——相信我,这比debug省时多了。