架构工作的产物是一组可解释、可验证的技术决策:系统如何划分边界,数据由谁负责,组件怎样通信,失败时如何恢复,变更怎样发布,运行状态如何被观察。
这 19 个领域覆盖架构方法、设计模式、通信协议、数据系统与运行平台,但它们解决的问题并不在同一层。六边形架构解决依赖方向,Kafka 处理持久化事件流,Kubernetes 管理容器工作负载,OpenTelemetry 统一遥测数据;四者服务于不同问题,不能互相替代。
全文按 19 个领域展开,其中「韧性设计」同时覆盖高并发与高可用,包括限流、熔断、降级、负载均衡、CAP 与共识算法。
这 19 个领域可以按上述顺序学习,也可以从当前系统最明显的约束切入。每一节都回答三个问题:它解决什么问题,常见方案如何分工,什么情况下不应增加这层复杂度。
1. 架构基础:结构、质量属性与演进
软件架构描述系统的重要结构、组件职责、交互关系和约束。代码目录只是结构的一部分;部署单元、数据所有权、故障边界、安全边界和团队责任同样属于架构。
架构决策通常围绕质量属性展开:可用性、延迟、吞吐量、一致性、安全性、可维护性、可测试性、可部署性和成本。目标之间经常互相制约。例如,多副本可以提高读取能力和容错能力,却会引入复制延迟、故障切换和一致性判断。
常见演进方式体现的是约束变化,不是一条必须按顺序升级的技术路线:
单体把功能放在一个部署单元中,调用简单、事务直接,适合边界尚未稳定或团队规模较小的系统。
MVC 与分层架构组织单体内部职责。MVC 关注输入、模型和视图协作,并不决定系统是否分布式。
SOA通过服务契约复用企业能力,常伴随集中治理和集成基础设施。微服务进一步强调围绕业务能力拆分、独立部署与团队所有权。
云原生关注容器、声明式配置、自动化、弹性和可观测的运行方式。单体同样可以运行在云原生平台上。
Serverless把部分容量管理和运行时交给平台,适合事件驱动、流量波动大或执行时间较短的任务,同时要评估冷启动、运行限制、可移植性与计费模型。
模块化单体常是更稳妥的起点。只有当独立扩缩容、故障隔离、发布节奏或团队所有权已经形成明确边界时,拆分服务才会带来足以覆盖分布式成本的收益。
2. 经典方法:边界、领域、读写与历史
六边形架构、Clean Architecture、DDD、CQRS 与 Event Sourcing 经常并列出现,实际分属不同层次。
| 方法 | 主要问题 | 核心做法 | 采用边界 |
| 六边形架构 | 业务逻辑如何隔离外部技术 | 业务核心定义 Port,数据库、HTTP、消息等 Adapter 在外层实现 | 简单 CRUD 不必为每个动作创建接口 |
| Clean Architecture | 源码依赖应指向哪里 | 依赖指向更稳定的业务规则,框架与设备位于外层 | 圆环数量不应机械映射成目录层数 |
| DDD | 复杂业务如何形成可执行模型 | 统一语言、Bounded Context、Aggregate、Entity、Value Object 与 Domain Event | 业务规则简单时只使用必要概念 |
| CQRS | 同一模型难以同时服务写入约束与复杂查询 | 分离 Command 与 Query 模型 | 不要求拆成两个服务或两个数据库 |
| Event Sourcing | 如何保留状态变化的完整历史 | 追加事件并由事件重建当前状态,投影生成查询模型 | 事件版本、重放、快照和隐私删除成本较高 |
它们可以组合。一个 Bounded Context 内部可以使用六边形架构控制依赖,部分聚合采用 CQRS,少数确实需要完整审计与时态查询的聚合再使用 Event Sourcing。Event Sourcing 与 CQRS 也能单独采用,没有必须绑定的关系。
边界的有效性最终由代码和运行时共同证明:内部模型不泄漏数据库对象,跨边界调用使用稳定契约,失败语义能被测试,团队也能独立理解和修改对应模块。
3. 软件设计原则:控制变化传播
SOLID 用于检查模块的职责、替换能力和依赖方向:
| 原则 | 设计问题 | 常见失败信号 |
| SRP,单一职责 | 一个模块因哪类原因变化 | 同一个类同时处理规则、持久化、协议与展示 |
| OCP,开闭原则 | 新行为能否通过稳定扩展点加入 | 每增加一种类型都修改多处分支 |
| LSP,里氏替换 | 子类型是否遵守父类型承诺 | 替换实现后出现额外异常、前置条件或语义变化 |
| ISP,接口隔离 | 使用方是否只依赖需要的能力 | 实现类被迫提供空方法,测试替身需要模拟无关行为 |
| DIP,依赖倒置 | 高层策略是否依赖稳定抽象 | 业务逻辑直接绑定数据库驱动、HTTP SDK 或框架对象 |
另外几组原则控制设计规模:
DRY消除同一知识的重复表达。两段相似代码承载不同业务含义时,过早合并反而会制造耦合。
KISS优先选择团队能读懂、测试和运维的最小方案。
YAGNI推迟尚无真实需求的扩展点、配置项与基础设施。
高内聚、低耦合让同一业务能力集中在边界内,并减少跨边界需要同时修改的知识。
IoC 与 DI把对象创建和依赖装配移到 Composition Root。构造函数注入通常足够,DI Container 只在装配规模确实需要时加入。
原则之间也会发生冲突。为了 OCP 预先抽象所有变化,可能违反 KISS 与 YAGNI。更可靠的做法是先识别稳定业务规则和已经发生的变化,再为它们建立接口。
4. 设计模式:为重复问题建立共同语言
模式用于描述反复出现的结构和协作方式。采用模式之前,要能指出它消除的条件分支、依赖传播或生命周期问题。
| 类别 | 模式 | 适合处理的问题 | 主要风险 |
| 创建型 | Singleton | 进程内确实只有一个生命周期的资源或注册表 | 隐藏全局状态、并发与测试依赖;优先由容器管理作用域 |
| 创建型 | Factory | 根据类型、配置或协议选择具体实现 | 工厂变成包含所有业务规则的大型分支 |
| 创建型 | Builder | 分步骤构造参数多、存在组合校验的对象 | 简单对象增加无意义样板代码 |
| 创建型 | Prototype | 从成本较高的预配置对象复制新实例 | 浅复制共享可变状态或资源句柄 |
| 结构型 | Adapter | 把外部协议转换为内部稳定接口 | 外部字段继续泄漏到领域模型 |
| 结构型 | Decorator | 在不修改核心实现时叠加缓存、日志或权限 | 多层包装后调用顺序和错误来源难以判断 |
| 结构型 | Proxy | 控制远程、延迟加载、访问或生命周期 | 远程调用伪装成本地调用,延迟与失败被隐藏 |
| 行为型 | Strategy | 在同一目标下替换计价、路由或校验算法 | 每个微小差异都创建一个策略类 |
| 行为型 | Observer | 一个事实发生后通知多个订阅方 | 顺序、重复投递和失败处理缺少定义 |
| 行为型 | Chain of Responsibility | 按顺序执行验证、过滤或审批节点 | 隐式顺序过长,跳过和终止条件不清晰 |
模式名称只负责建立共同语言。实现仍需明确输入、输出、错误、幂等性、线程安全和观测方式。
5. 系统设计:接口与通信模型
通信方式应由调用关系、延迟、兼容性和失败处理决定。
| 方式 | 适合的问题 | 需要明确的边界 |
| REST | 面向资源的 HTTP API、缓存与统一语义 | URI、HTTP method、状态码、幂等与版本策略 |
| RPC | 面向操作的服务调用 | 超时、重试、序列化、错误模型与接口演进 |
| GraphQL | 客户端需要按视图组合字段和关联数据 | 查询复杂度、N+1、字段权限与缓存 |
| gRPC | 强类型内部调用、低开销序列化与流式通信 | Protocol Buffers 演进、HTTP/2 基础设施与浏览器接入 |
| WebSocket | 客户端与服务端持续双向通信 | 连接状态、心跳、背压、重连与横向扩展 |
| SSE | 基于 HTTP 的服务端单向事件推送 | 断线续传、代理超时、连接数与事件 ID |
| API Gateway | 统一入口、路由、认证、限流与协议适配 | 避免把业务编排全部集中到网关 |
| OpenAPI | 描述 HTTP API 契约并生成文档或代码 | 需要由 CI 检查规范与实现是否漂移 |
REST 是架构风格,HTTP 提供传输语义;RPC 是调用模型,gRPC 是一种具体实现;OpenAPI 描述契约,不执行请求。把这些概念分开后,系统可以在外部公开 REST 或 GraphQL,在内部对低延迟服务使用 gRPC,并通过事件处理跨服务的异步变化。
同步调用会把下游延迟和可用性带入当前请求。增加重试前必须确认操作幂等,并设置总时间预算、退避与重试上限。异步消息可以隔离峰值和暂时故障,但会引入重复投递、乱序、积压和最终一致性。
6. 数据库架构:先确定数据所有权
数据库选型先回答四个问题:事务边界在哪里,查询形态是什么,数据规模如何增长,故障后允许丢失或延迟多少数据。
MySQL 的扩展顺序
MySQL 常见扩展顺序是索引与 SQL 优化、连接池治理、主从复制、读写分离,最后才评估分库分表。
主从复制现在也常写作 source/replica。副本可以承担只读查询和故障恢复,但复制延迟会影响「写后立即读」等一致性要求。
读写分离可以在应用或代理层实现。事务内查询、强一致读取和刚写入的数据通常仍需访问主库。
分库分表增加路由、全局唯一 ID、跨分片查询、扩容迁移和分布式事务成本。单表数据量与真实瓶颈尚未达到阈值时,不应提前拆分。
其他数据库的定位
| 系统 | 更合适的工作负载 | 需要评估的限制 |
| PostgreSQL | 复杂事务、关系查询、扩展类型与丰富 SQL 能力 | 扩展方式、连接数和高可用方案需要单独设计 |
| ClickHouse | 列式分析、聚合与高吞吐写入 | 高频小事务更新和逐行 OLTP 不是主要目标 |
| TiDB | MySQL 协议兼容、分布式 SQL、横向扩展与强一致事务 | 跨节点延迟、热点、执行计划与运维复杂度 |
| OceanBase | 分布式关系事务、多副本与大规模在线业务 | 部署资源、兼容模式、分区设计与团队经验 |
一套系统可以同时使用 OLTP 数据库、分析数据库和搜索索引,但业务事实需要唯一权威来源。ClickHouse 或 Elasticsearch 中的派生数据应能从源数据重建,并有明确的延迟与校验指标。
7. 缓存架构:用命中率换取一致性成本
Redis 常用的 6 类数据类型是 String、Hash、List、Set、Sorted Set 和 Stream。它还提供 Bitmap、Bitfield、Geospatial、HyperLogLog 等能力,因此「6 大数据结构」只能理解为高频集合。
| 类型 | 典型用途 | 容易忽略的问题 |
| String | 对象缓存、计数器、分布式状态 | 大 Key、序列化成本、非原子读改写 |
| Hash | 字段化对象、局部更新 | 字段过多和过期粒度 |
| List | 双端队列、有限时间线 | 阻塞消费与可靠投递语义有限 |
| Set | 去重、标签、集合运算 | 大集合运算阻塞与内存占用 |
| Sorted Set | 排行榜、延时调度、按分值检索 | 热点更新和分值精度 |
| Stream | 持久化消息流、Consumer Group | Pending、确认、裁剪和重复消费 |
Redis Cluster 使用 Hash Slot 分片,并通过副本和故障转移提高可用性。它不能自动提供跨任意 Key 的强事务,也不能消除网络分区期间的一致性取舍。
多级缓存把不同范围的数据放到离使用方最近的位置:
并非每个请求都要穿过全部缓存层。公开资源适合 CDN,短期页面响应可以使用 Nginx microcache,单实例高频配置适合 Caffeine,跨实例共享数据适合 Redis。
Cache Aside 是常见起点:读取未命中时访问数据库并回填,写入数据库后删除缓存。仍需处理缓存击穿、穿透、雪崩和热 Key,可以按问题选择请求合并、负缓存、Bloom Filter、TTL 抖动、预热或热点拆分。缓存失效策略必须先于缓存层数设计。
8. 消息队列:把时间与故障解耦
| 系统 | 主要优势 | 更合适的用途 | 重点成本 |
| Kafka | 分区日志、高吞吐、保留与重放 | 事件流、日志、CDC、流处理输入 | 分区顺序、Consumer Lag、再均衡与容量规划 |
| RabbitMQ | Exchange 路由、确认机制与成熟的工作队列语义 | 任务分发、复杂路由、请求削峰 | 队列堆积、投递确认、死信与集群拓扑 |
| RocketMQ | 顺序消息、事务消息、延时消息等业务能力 | 交易与事件驱动业务 | Topic/Queue 规划、重试与运维生态 |
| Pulsar | Broker 与 BookKeeper 存储分离、多租户与分层存储 | 多租户消息与流平台、长期积压 | 组件数量、BookKeeper 运维与容量模型 |
| Redis Stream | 能复用 Redis,提供 Stream 与 Consumer Group | 中小规模内部事件、轻量任务流 | 保留策略、Pending 恢复和 Redis 资源竞争 |
选型前先写出消息语义:消息是 Command 还是 Event,顺序需要覆盖单个 Key、单个分区还是全局,允许至少一次还是至多一次投递,消费者如何幂等,失败何时重试或进入死信队列,积压多久后触发告警。
「Exactly once」通常只覆盖特定产品和处理边界。消息系统、数据库与外部 API 组成完整业务操作时,仍需要业务唯一键、幂等记录、Outbox 或状态机保证重复执行安全。
9. 微服务:围绕业务能力建立独立运行边界
一组常见的 Spring Cloud 组件可以这样分工:
Nacos、Consul、Eureka提供服务注册与发现。Nacos 还提供配置管理,Consul 还覆盖健康检查与 KV 等能力。
OpenFeign是声明式 HTTP Client,用 Java interface 描述 HTTP 调用。它不定义一套新的 RPC 传输协议。
Sentinel提供流量控制、熔断降级和系统保护等能力。
Spring Cloud Gateway处理入口路由、Filter、认证集成和限流等横切职责。
这些组件来自不同生态,不需要全部组合。Kubernetes Service 已经提供服务发现时,额外注册中心要有独立配置、跨集群发现或非 Kubernetes 工作负载等明确理由。
微服务边界应与业务能力、数据所有权和团队责任一致。每个服务独立发布,却共同写同一组核心表,会把部署边界与事务边界分开,形成更难治理的分布式单体。
落地微服务还需要同时设计契约版本、超时预算、重试与熔断、异步一致性、配置与 Secret、日志与 Trace、发布回滚、容量和故障演练。注册中心只能解决「实例在哪里」,无法解决这些运行问题。
10. 云原生:管理可重复的工作负载
Docker Image 保存应用及运行依赖,Container 是 Image 的运行实例。镜像应保持可重复构建、最小依赖、非 root 运行,并通过外部配置注入环境差异。
Kubernetes 的常用对象各自承担明确职责:
| 对象 | 职责 | 注意事项 |
| Pod | 最小调度单元,共享网络与部分存储 | Pod 可替换,不应依赖本地临时状态 |
| Deployment | 管理无状态 Pod 副本与滚动更新 | Readiness、资源请求和更新策略决定发布质量 |
| Service | 为一组 Pod 提供稳定访问入口 | 选择器、端口与流量策略需要和工作负载一致 |
| Ingress | 描述集群外部 HTTP/HTTPS 路由 | Ingress API 已冻结,新能力优先评估 Gateway API |
| ConfigMap | 保存非敏感配置 | 配置变更是否热加载由应用和挂载方式决定 |
| Secret | 保存敏感数据 | Base64 不是加密,需要启用静态加密和最小权限 |
| HPA | 根据指标调整副本数 | 指标延迟、冷启动和下游容量会影响扩容效果 |
容器与 Kubernetes 提供运行基础,应用仍需遵守无状态、健康检查、优雅退出、资源限制、幂等任务和可观测性要求。数据库、消息系统等有状态工作负载是否放入集群,应根据备份恢复、存储性能和运维能力单独判断。
11. DevOps:让每次变更都可验证、可追溯
DevOps 关注开发与运行反馈的连续性。工具可以沿着变更过程分工:
Git保存可审查的变更历史。
GitFlow使用 feature、develop、release 与 hotfix 等分支支持计划式版本发布。高频持续交付团队通常更适合短分支和 Trunk-Based Development,避免长期分支合并成本。
CI在每次变更上执行格式、静态分析、测试、安全扫描与构建。
CD把同一构建产物按环境推进,并保留审批、回滚与审计记录。
Jenkins 或 GitHub Actions承载 Pipeline。选型取决于托管方式、插件治理、权限和现有平台。
Helm把 Kubernetes Manifest 组织成可版本化的 Chart。
Argo CD持续比较 Git 中的期望状态与集群实际状态,并执行 GitOps Reconciliation。
流水线成功只证明自动检查覆盖的契约成立。发布还需要渐进式流量、运行指标、错误预算和回滚条件。构建一次、多环境复用同一产物,可以减少环境间不可解释的差异。
12. 韧性设计:高并发与高可用共同处理
高并发控制资源竞争,高可用控制故障影响。两者共享容量、隔离、恢复和数据一致性约束,因此放在同一个领域讨论。
先保护入口,再隔离故障
令牌桶以固定速率补充令牌,允许一定突发流量;漏桶以较稳定速率排出请求,更强调平滑输出。
超时限制资源占用时间,熔断在连续失败时暂时停止调用,降级返回受限能力或旧数据,排队把即时压力转为可控积压。
Nginx常用于 HTTP 七层代理与负载均衡,LVS/IPVS工作在四层,CDN把可缓存内容和部分安全防护前移到边缘。
排队没有消除压力,只是把压力从并发数变成等待时间和积压量。队列容量、过期时间、拒绝策略和恢复速度必须一起定义。
用目标描述高可用
主备、双活与多活描述流量和数据副本的部署方式:
主备结构简单,故障时切换,重点是切换检测、RTO 和数据 RPO。
双活让两个站点同时承载业务,需要处理流量分配、状态复制与冲突。
多活把业务扩展到多个地域,要求数据按地域或业务单元划分,并为跨地域故障定义降级方式。
CAP 定理讨论网络分区发生时,一致性与可用性的取舍,不表示正常运行时只能任选两个字母。BASE 是工程实践中描述基本可用、软状态与最终一致性的常用概括,不是与 CAP 等价的数学定理。
Paxos 与 Raft 用于让多个节点对复制状态达成共识。ZooKeeper 与 etcd 在此基础上提供协调状态、选主、配置或租约等能力。它们不能替业务完成跨订单、库存和支付的分布式事务。
13. 分布式系统:处理跨节点状态
分布式问题通常从四类需求出现:互斥修改、跨资源事务、节点变化后的路由,以及全局标识。
分布式锁
一个可用的分布式锁需要唯一持有者标识、原子获取、有限租约、安全释放和续租边界。锁超时后,旧持有者可能继续写入,因此关键资源还需要 Fencing Token 或数据库版本检查。分布式锁减少并发,不替代幂等与数据库约束。
分布式事务
XA由协调者管理多个资源的准备与提交,能提供强原子性,但会延长锁持有和协调时间。
TCC由业务显式实现 Try、Confirm、Cancel,适合可以预留资源的短流程,开发与幂等成本较高。
Saga把长事务拆成多个本地事务,并为已完成步骤定义补偿,适合跨服务业务流程。补偿本身也会失败,需要持久化状态与人工处理入口。
Seata是分布式事务框架,提供 AT、TCC、Saga、XA 等模式。它不代表一种独立于上述模型的新事务理论。
路由与标识
一致性 Hash把节点和 Key 映射到 Hash Ring,节点变化时只迁移部分 Key;Virtual Node 用于改善分布。它适合缓存或分片路由,不能自动解决热点、数据复制与跨分片事务。
Snowflake ID通常组合时间戳、节点标识和序列号,在不访问中心数据库的情况下生成大致有序的唯一 ID。实现必须处理时钟回拨、节点 ID 冲突、同毫秒序列耗尽和信息暴露。
14. 搜索架构:词项、语义与排序
Elasticsearch 通过倒排索引把词项映射到文档,适合全文检索、过滤、聚合和相关性排序。向量检索把文本、图片或其他对象编码为 Vector,用相似度寻找语义接近的内容。
两者分别擅长不同信号:
词法检索擅长产品编号、人名、错误码和精确术语。
向量检索擅长同义表达、语义相近和自然语言问题。
Hybrid Search 组合词法与向量结果,再通过加权、RRF 或 reranker 排序。
搜索索引通常是派生数据。业务数据库保存权威事实,Outbox 或 CDC 把变化同步到索引;系统监控同步延迟、失败和数据差异,并通过 Alias 与 Reindex 完成 Mapping 演进。
向量数据库是否独立部署,取决于数据规模、过滤需求、索引算法、更新频率、延迟与团队运维能力。现有搜索引擎或关系数据库已经满足目标时,无需为了 Vector 概念增加新系统。
15. 大数据架构:批处理、流处理与湖仓表格式
| 技术 | 主要职责 | 典型用途 |
| Hadoop | HDFS 分布式存储与 MapReduce 等批处理生态 | 大规模离线存储和批量计算 |
| Spark | 通用分布式计算,覆盖 Batch、SQL、Streaming 与 ML | ETL、交互分析、批流任务 |
| Flink | 有状态流处理、Event Time、Checkpoint 与一致性状态 | 实时指标、事件处理、持续 ETL |
| Hive | 面向数据仓库的 SQL、Metastore 与表管理 | 离线数仓查询和数据治理入口 |
| Iceberg | 开放表格式,提供 Snapshot、Schema Evolution 与 Partition Evolution | 多计算引擎共享数据湖表 |
| Delta Lake | 开放表格式,提供事务日志、Schema Enforcement 与 Time Travel | Lakehouse 表与批流统一数据管理 |
HDFS 或对象存储负责保存文件,Spark 与 Flink 执行计算,Hive Metastore 管理元数据,Iceberg 或 Delta Lake 定义表级事务和快照。这些组件分层组合,不能用「数据湖」一个词代替所有职责。
选型要写清楚数据时效、乱序容忍、状态大小、重放方式、Schema 演进、数据质量和成本。分钟级报表不一定需要持续流处理,无法重放的数据流也无法仅靠 Checkpoint 完成灾难恢复。
16. AI 架构:知识、连接、执行与治理
AI 应用把非确定性模型放进传统软件系统后,需要额外管理 context、工具、状态、评估和成本。
| 能力 | 负责的问题 | 关键设计 |
| RAG | 为本次生成检索外部证据 | 切分、索引、Hybrid Search、reranking、引用与检索评估 |
| MCP | 统一 AI Host 连接 Resources、Tools 与 Prompts 的协议 | 认证、授权、Schema、用户确认与 Server 信任边界 |
| Agent | 根据目标和执行结果动态选择下一步 | 工具范围、步骤上限、状态、失败恢复与结果验证 |
| Workflow | 用预定义步骤和分支执行稳定流程 | 状态持久化、重试、补偿、超时与人工节点 |
| Vector DB | 保存向量并执行近似相似度检索 | 索引算法、Metadata Filter、更新与召回质量 |
| AI Gateway | 统一模型入口、路由、限流、日志、成本与故障切换 | 敏感数据、供应商差异、缓存语义和可观测性 |
固定流程优先使用普通代码或 Workflow。只有下一步确实依赖环境反馈、规则无法完整预写,并且结果可以验证时,Agent 才能提供相应价值。RAG 提供证据,MCP 提供连接,两者都不替代 Agent 的规划,也不要求必须引入 Agent。
评估应拆开进行:检索系统测 Recall 与排序质量,模型调用测事实一致性和结构约束,工具调用测参数与成功率,完整任务测结果是否满足验收条件。只看对话是否流畅,无法证明系统完成了工作。
17. 安全:身份、权限、传输与输入边界
身份相关概念需要准确分工:
OAuth 2.0是授权框架,解决 Client 如何代表 Resource Owner 获得受限访问权限。
OpenID Connect在 OAuth 2.0 之上增加身份认证与 ID Token。
JWT是一种紧凑的 Claim 表达格式,不等于登录、OAuth 2.0 或权限系统。签名、过期、Issuer、Audience、密钥轮换和撤销策略都要单独设计。
RBAC按角色授予权限,适合稳定职责;ABAC根据主体、资源、动作与环境属性判断,表达力更强,策略治理成本也更高。
HTTPS/TLS 保护传输中的机密性和完整性。服务端仍需校验证书、协议版本、Host、输入长度和授权上下文,敏感数据在日志、缓存、消息与备份中的保护也要单独处理。
常见 Web 攻击需要在代码边界防御:输出编码与 CSP 限制 XSS,SameSite Cookie 与 CSRF Token 防止跨站请求伪造,参数化查询防止 SQL 注入。WAF 可以拦截部分已知恶意流量和异常模式,但不能修复应用中的身份越权、业务校验和不安全查询。
安全设计从 Threat Model 开始,明确资产、信任边界、攻击面与滥用方式,再落到最小权限、Secret 管理、依赖更新、审计日志、速率限制和应急响应。
18. 可观测性:从信号还原系统行为
可观测性常用三类遥测信号:
Logging记录离散事件与上下文,适合查询错误细节和业务状态。
Metrics聚合数值与时间序列,适合趋势、容量、SLO 和告警。
Tracing记录请求跨服务和组件的 Span,适合定位延迟与依赖失败。
OpenTelemetry 提供生成、采集和导出 Trace、Metric 与 Log 的标准 API、SDK 和 Collector。它不负责长期存储与可视化。Prometheus 负责时间序列采集、查询和告警,Grafana 负责 Dashboard、探索和告警展示;实际系统也可以替换其中任一后端。
三类信号要通过 Trace ID、Request ID 与稳定业务标识关联。日志字段需要控制敏感数据,Metric Label 需要控制 Cardinality,Trace Sampling 需要保留错误和高延迟请求。
告警应直接对应用户影响和 SLO,例如错误率、延迟、可用性、消费积压与数据新鲜度。仅在 CPU 或内存达到固定阈值时告警,既可能漏掉业务失败,也可能制造没有行动价值的噪声。
19. 性能优化:测量运行时的真实瓶颈
性能优化从工作负载和 Profile 开始。先区分 CPU、内存分配、GC、锁竞争、网络、磁盘、数据库和下游等待,再决定修改代码、数据结构、并发模型还是基础设施。
JVM 与 Go Runtime
JVM 调优需要同时观察吞吐量、Tail Latency、Heap、Allocation Rate 和暂停时间。G1、ZGC 等收集器有不同目标,Heap 变大也会增加内存成本与故障恢复时间。
Go GMP把 Goroutine(G)调度到线程(M)上,并由 Processor(P)提供执行 Go 代码所需的调度资源。阻塞系统调用、Goroutine 泄漏、锁竞争和过量分配都可能使并发代码变慢。
GC 算法在吞吐、暂停、内存占用和实现复杂度之间取舍。对象生命周期与 Allocation Rate 往往比单个 GC 参数更值得先测量。
Linux I/O
零拷贝减少数据在 Kernel Space 与 User Space 之间的复制或上下文切换,常见机制包括
sendfile。它是否有效取决于数据是否需要在应用层解析或修改。epoll通知应用 File Descriptor 的就绪状态,适合管理大量网络连接。应用仍需正确处理 Non-blocking I/O、背压和每次读取的工作量。
io_uring通过 Submission Queue 与 Completion Queue 提交和完成异步 I/O,覆盖文件、网络等多类操作。它能减少部分系统调用与切换成本,但收益取决于 Kernel、驱动、工作负载与库实现。
JVM 可以使用 JFR、GC Log 和 async-profiler,Go 可以使用 pprof 与 Execution Tracer,Linux 可以使用 perf 和 eBPF 工具。优化结果必须回到同一套压测条件,对比吞吐、P95/P99 延迟、错误率、资源成本和恢复行为。
用 ADR 记录架构决策
一个架构决策可以按固定顺序写入 ADR:
记录业务目标、约束和需要改善的质量属性。
标出模块、服务、数据与信任边界,指定所有者。
比较两个或三个可行方案,写清收益、代价与拒绝原因。
定义正常请求、超时、重复、乱序、分区和恢复行为。
给出测试、指标、告警、容量和回滚条件。
约定复查时间,以及触发重新评估的流量、团队或业务变化。
延伸阅读
MCP、RAG 与 AI Agent:知识、工具与执行的分工:进一步区分 AI 系统中的知识、连接和执行层。
从 TCP 到生产级架构:Go 后端开发的 5 个工程判断:从网络、存储、分层和生产运行理解架构取舍。
好的 API 设计:继续阅读 API 契约、命名、错误和演进方法。