第4章:令牌化服务架构设计
这一章是骨架。说白了,就是你要把令牌化系统拆成几层,每层该放什么,彼此怎么协作。我刚开始做这类系统时,喜欢把所有逻辑塞到一个服务里——结果呢?改个存储就得重新部署整个模块,线上出了故障都分不清是API超时还是令牌生成器卡住了。嗯,后来才明白,分层不仅是代码整洁问题,更是命脉。
4.1 三层架构总览
我把令牌化服务拆成三个层:API层、业务层、存储层。各层职责清晰,互不越界。你想想看,每一层只关心自己那点事,出了毛病也容易定位。
- API层:处理外部请求,做认证、限流、参数校验。不参与任何业务逻辑。
- 业务层:核心逻辑所在——生成令牌、映射关系管理、脱敏/还原。也是状态最复杂的地方。
- 存储层:负责持久化。令牌映射表、密钥、审计日志,统统丢给它。
核心要点:层与层之间只通过接口通信,不允许业务层直接操作数据库API或API层调用存储层的内部方法。我在项目中吃过这个亏——直接调了存储层一个内部函数,结果存储层改表结构,业务层跟着全崩。从那以后,我强制团队用gRPC或REST明确定义接口边界。
4.2 模块划分——我建议这样拆
每个层内部再拆成独立的模块。为什么?因为模块可以单独部署、单独扩缩容。举个例子,如果令牌生成压力大,我只扩容生成器模块,不用动整个业务层。看看这张表,你就能快速对齐每个模块的职责:
| 模块名 | 所在层 | 主要职责 | 我个人偏好的实现 |
|---|---|---|---|
| Gateway | API层 | 统一入口,认证限流 | Kong / Envoy + OAuth2 Proxy |
| TokenGenerator | 业务层 | 生成随机令牌或保格式令牌 | 基于UUIDv4或FF1算法 |
| MappingManager | 业务层 | 维护明文↔令牌的双向映射 | 内存中LRU缓存 + 数据库持久化 |
| KeyRotation | 业务层 | 定期轮换加密密钥 | CronJob + Vault Transit Engine |
| AuditLogger | 业务层 | 记录所有令牌操作行为 | 异步写入Kafka + Elasticsearch |
| StorageProxy | 存储层 | 提供统一的DAO接口 | MyBatis-Plus + ShardingSphere |
一个小技巧:模块间的接口定义要尽量早定。我习惯先写protobuf文件,再实现业务逻辑。这样团队并行开发时不会打架,而且后期修改接口的成本也低。
4.3 高可用设计——避免单点故障
令牌化服务不能挂。你想想,一旦停了,所有依赖它做脱敏的业务都得阻塞。我自己经历过一次线上事故:令牌生成器模块的单节点宕机,导致调用方全部超时,五分钟内警报打爆手机。从那以后,我对高可用的要求变成了:任何单点故障都不能导致服务不可用。
怎么做?分三层来看:
API层高可用
- 多实例部署,至少2个节点。前端加负载均衡(NLB或HAProxy)。
- 健康检查接口(/health)必须包含对业务层的连通性检测。
- 限流策略要独立,防止某个恶意租户打爆整个API层。
业务层高可用
- 所有业务模块无状态化。Session信息不存本地,丢进Redis。
- 令牌生成器采用多活模式:每个节点都可以独立生成全局唯一令牌。我们使用Snowflake派生的方案,每个节点分配唯一workId。
- 映射管理器必须支持主备切换。一旦主节点出问题,备节点自动接管。
我曾经踩过的坑:映射管理器的缓存忘记做故障转移。主节点挂了之后,备节点没有预热缓存,请求大量穿透到数据库,直接把DB打挂了。正确做法:备节点定时从Redis同步热点映射,或者用一致性哈希让每个映射只落在固定节点上。
存储层高可用
- 数据库采用主从复制 + 自动故障转移。我一般用MySQL Group Replication或Galera Cluster。
- Redis用Sentinel或Redis Cluster。注意:令牌映射的缓存不能丢,必须开启AOF持久化。
- 密钥存储使用HashiCorp Vault,Vault本身需要3-5个节点组成高可用集群。
4.4 整体架构设计文档示例
纸上谈兵完了,给你一个简化版的架构设计文档模板(YAML风格)。实际项目中我会用这样的描述来对齐团队理解:
# 令牌化服务架构设计(简版)
service: tokenization
version: v1
layers:
- name: api-layer
components:
- gateway: [rate_limiter, auth_checker, request_validator]
- endpoints: [tokenize, detokenize, alias_management]
ha: [multiple_instances, health_check, lb]
- name: business-layer
components:
- _generator: [random_strategy, fpe_strategy, vault_strategy]
- mapping_manager: [cache(redis), db_persistence, dual_write]
- key_rotation: [cron_trigger, vault_transit, no_downtime]
ha: [stateless, active_active, circuit_breaker]
- name: storage-layer
components:
- mapping_db: [mysql_cluster, read_write_separation, auto_failover]
- cache: [redis_sentinel, aof_enabled, key_eviction_policy=noeviction]
- vault: [3_node_cluster, seal_unseal, auto_unseal]
ha: [replication, backup, disaster_recovery]
这段配置不是能直接跑的配置,但能帮你快速给新人讲解整个服务是怎么搭起来的。我每次做方案评审时,都会先把这种层级图+模块表+HA策略丢出来。大家一看就懂,少了很多无效沟通。
4.5 高可用测试你做了吗?
架构设计完了,不测试等于白做。我个人建议至少覆盖下面几个场景:
- 单节点故障测试:拔掉一个API节点,看服务是否仍可用。
- 缓存穿透测试:停掉Redis集群中的一个节点,观察请求是否被正确路由到主节点。
- 存储故障测试:杀掉主数据库,看应用是否能自动切换到从库。
- 密钥轮换不对等测试:在业务高峰期触发密钥轮换,看是否有令牌生成失败。
强调一点:高可用不是写死代码就能得到的,它贯穿在架构设计、编码实现、运维监控的每一个环节。我见过太多团队把HA写在PPT上,结果生产环境一崩再崩。所以,测试一定要做实,别走形式。