架构演进与迁移模式(Strangler/防腐层/绞杀者)

共 18 题
📑 题目列表 18 题
#
★★★

1. Anti-Corruption Layer 在对接遗留系统时的适配器作用

请解释 Anti-Corruption Layer(防腐层)在对接遗留系统时的适配器作用?

  • 防腐层的定义
  • 模型/协议转换
  • 隔离遗留系统
  • Anti-Corruption Layer(ACL):在新系统与遗留系统之间建立一层隔离/转换层,防止遗留系统的模型、协议、语义"污染"新系统的领域模型。
  • 适配器作用:ACL 是适配器——把遗留系统的数据/协议转换成语义清晰、符合新系统领域模型的接口,新系统用自己干净的模型调用,不直接接触遗留系统的脏数据/复杂协议。
  • 隔离:① 模型转换(遗留的字段/语义 → 新系统的领域模型);② 协议转换(遗留的接口格式 → 新系统接口);③ 隔离依赖(遗留系统的变更不影响新系统,新系统只依赖 ACL 的稳定接口)。
  • 收益:新系统领域模型保持干净,不受遗留系统侵扰;遗留系统可逐步替换(ACL 屏蔽变化);新系统可独立演进。
  • 结论:ACL 是连接新系统与遗留系统的适配器,负责模型/协议转换与隔离,保护新系统领域模型不受遗留系统污染。

ACL 把遗留系统转换成新系统可用的干净模型,隔离耦合。是适配器模式在系统集成的应用,保护新系统领域模型。

#
★★★

2. 迁移完成后如何安全移除旧实现与兼容代码

请解释迁移完成后如何安全移除旧实现与兼容代码?

  • 迁移完成信号
  • 移除旧实现的步骤
  • 兼容代码清理
  • 迁移完成信号:确认所有流量已切到新系统、新系统稳定运行(无错误率异常、无数据差异)、旧系统不再被调用(监控确认无流量)。
  • 移除旧实现的步骤:① 确认信号(无流量、无依赖、稳定);② 灰度停用旧实现(先停读后停写,观察);③ 移除旧代码/旧服务(删除旧服务、旧模块、旧接口);④ 移除兼容代码(双写、双读、兼容层、ACL 中的旧转换);⑤ 清理数据与依赖(旧表、旧消息、旧配置)。
  • 安全移除:移除前确认无流量与依赖,移除后监控验证(无回归、无引用错误);分阶段移除,先停旧后删代码,避免回滚风险。
  • 兼容代码清理:迁移期间的双写/兼容/映射代码在迁移完成后删除,避免长期维护负担与认知负担。
  • 结论:迁移完成后按"确认无流量→停用旧实现→移除旧代码→清理兼容代码与数据"顺序安全移除,并监控验证。

安全移除旧实现靠"确认无流量依赖 → 先停旧 → 删代码 → 清兼容/数据",分阶段并监控验证,避免迁移产生的兼容代码长期残留。

#
★★★

3. 历史数据回填中大表迁移的批处理分片、进度记录与对账校验,回填失败如何断点续传

请解释历史数据回填,说明大表迁移的批处理分片、进度记录与对账校验,以及回填失败如何断点续传?

  • 批处理分片
  • 进度记录
  • 对账校验与断点续传
  • 历史数据回填:把旧表数据迁移到新表/新结构,需分批处理(大表不能一次性全量)。
  • 批处理分片:按主键范围/时间/模分片,把大表分成多个批次,每批(如 1000 条)独立迁移,避免内存/锁/超时问题,可并行。
  • 进度记录:每批迁移完成后记录进度(已迁移到哪个分片/游标/批号),记录到进度表/偏移量,支持断点续传。
  • 对账校验:迁移完成后对比新旧数据(总数、主键集合、关键字段校验和/哈希),确认数据一致,发现差异补迁。
  • 断点续传:回填失败(进程崩溃、批次失败)时,从进度记录处继续,不重头开始;失败的批次重试,幂等(按主键去重,重复迁移不产生副作用)。
  • 结论:历史数据回填用分片批处理 + 进度记录 + 对账校验,失败从断点续传,保证迁移完整一致。

大表迁移靠分片批处理(可并行)、进度记录(断点续传)、对账校验(一致性)、幂等重试(不重复副作用)。四者配合保证回填可靠。

#
★★

4. Throttling/Rate Limiting 在网关层的多租户公平策略

请解释 Throttling/Rate Limiting 在网关层的多租户公平策略?

  • 限流/节流
  • 多租户公平
  • 配额与公平调度
  • Throttling/Rate Limiting:控制请求速率(如 QPS、令牌桶/漏桶算法),防止单个客户端拖垮系统,保护下游。
  • 网关层限流:网关统一做限流,对每个租户/客户端设置配额(如各租户 QPS 上限),按租户维度限流。
  • 多租户公平:多租户共享资源,限流要公平——不能让一个租户抢占全部配额,挤占其他租户。策略:① 按租户设置独立配额(per-tenant quota);② 公平调度(如权重轮询,按租户流量比例分配);③ 隔离(租户间限流桶隔离,一个租户超限不影响其他)。
  • 实现:令牌桶/漏桶按租户 key 分桶,记录各租户用量;超限返回 429。配额可动态调整(按协议/等级)。
  • 结论:网关层限流按租户设置配额与隔离,保证多租户公平,避免单一租户抢占资源。

多租户限流公平靠"per-tenant 配额 + 桶隔离 + 公平调度",按租户 key 独立限流,一个租户超限不影响其他,保护共享资源。

#
★★

5. Strangler Fig 模式如何逐步把单体流量迁移到新服务

请解释 Strangler Fig 模式如何逐步把单体流量迁移到新服务?

  • Strangler Fig 绞杀者
  • 逐步迁移
  • 路由与切流
  • Strangler Fig(绞杀者模式):像藤蔓缠绕绞杀宿主树,逐步用新系统替换旧系统(单体),最终"绞杀"旧系统并移除。
  • 逐步迁移:把单体按业务功能逐个替换——每个功能在新系统中实现,通过路由/网关把该功能的流量切到新服务,其余流量仍走单体。每替换一个功能,停留观察,稳定后再替换下一个。
  • 路由与切流:网关/路由层根据请求(URL/功能/租户)把流量分发到新服务或旧单体,支持按比例切流、灰度、回滚。
  • 过程:功能逐个迁移 → 双写/数据同步 → 流量逐步切换 → 旧单体功能逐渐减少 → 全部迁移后移除旧单体。
  • 结论:Strangler Fig 用路由/网关按功能逐步把流量从单体切到新服务,逐个替换、逐步绞杀,降低迁移风险。

Strangler Fig 逐个功能替换,路由层切流,每步观察稳定再继续,逐步用新系统绞杀旧单体,降低一次性迁移风险。

#
★★

6. 事件最终一致下的幂等消费者设计中唯一事件 ID + 去重表/布隆过滤器,确保重复消费不产生副作用

请解释事件最终一致下的幂等消费者设计,说明唯一事件 ID + 去重表/布隆过滤器如何确保重复消费不产生副作用?

  • 幂等消费者
  • 唯一事件 ID + 去重表
  • 布隆过滤器
  • 幂等消费者:消息可能重复投递(at-least-once),消费者必须幂等——重复消费同一事件不产生副作用。
  • 唯一事件 ID + 去重表:每个事件带唯一 ID,消费者处理前先查去重表(主键=事件 ID),已处理过则跳过(返回),未处理则执行并记录(插入去重表),保证重复事件只执行一次。去重表与业务处理在同一事务,保证原子性。
  • 布隆过滤器:去重表用布隆过滤器做快速判重——先查布隆过滤器,若"可能已存在"再查去重表确认,避免每次都查库,降低判重成本。布隆过滤器有误判(可能把未处理误判为已处理),需配合去重表兜底。
  • 幂等保证:事件 ID 唯一 + 去重表(数据库唯一约束)+ 业务幂等(重复执行结果一致),三重保障。
  • 结论:幂等消费者用唯一事件 ID + 去重表(唯一约束)保证重复消费只执行一次,布隆过滤器加速判重,确保最终一致下无副作用。

幂等消费者靠唯一事件 ID 去重(去重表唯一约束),布隆过滤器加速判重。重复事件只执行一次,最终一致下无重复副作用。

#
★★

7. 错误分类(可重试/不可重试/程序错误)对系统弹性意义

请解释错误分类(可重试/不可重试/程序错误)对系统弹性的意义?

  • 错误分类
  • 重试策略
  • 弹性意义
  • 错误分类:把错误分为可重试(瞬时错误,如超时、网络抖动、临时 5xx)、不可重试(业务确定错误,如参数错误、404、业务校验失败)、程序错误(Bug,如空指针、逻辑错误)。
  • 对弹性的意义:分类决定应对策略——可重试错误用重试(指数退避 + 抖动)恢复,提升系统弹性(韧性);不可重试错误不重试(重试无意义,浪费资源),直接处理/返回;程序错误暗示 Bug,需修复代码而非重试。
  • 正确分类:避免错误重试(浪费资源、放大故障)与漏重试(失去恢复机会)。配合熔断(避免重试风暴)、超时、幂等。
  • 结论:错误分类让系统对"可重试错误"自动恢复、对"不可重试错误"不浪费重试、对"程序错误"定位修复,提升系统弹性与可靠性。

错误分类决定重试策略:可重试(瞬时)用重试恢复,不可重试(业务确定)不重试,程序错误(Bug)要修复。正确分类提升弹性。

#
★★

8. 防腐层(ACL)如何在新旧系统间做模型与协议转换

请解释防腐层(ACL)如何在新旧系统间做模型与协议转换?

  • ACL 的模型转换
  • 协议转换
  • 隔离与封装
  • 防腐层(ACL):在新旧系统之间做模型与协议转换,让新系统用自己干净的领域模型与旧系统交互,不直接接触旧系统的复杂模型/协议。
  • 模型转换:把旧系统的数据模型(字段、语义、命名)转换为新系统领域模型。如旧系统"客户表"转换为新系统的"Customer"聚合,字段映射、语义转换、数据清洗。
  • 协议转换:把旧系统的接口协议(SOAP、旧格式、自定义协议)转换为新系统接口(REST、内部服务)。ACL 封装旧系统的调用细节,对外提供统一接口。
  • 隔离:新系统只依赖 ACL 的稳定接口,不依赖旧系统;旧系统变更由 ACL 屏蔽,新系统独立演进。
  • 结论:ACL 通过面对面转换(模型映射 + 协议适配)隔离新旧系统,新系统用干净模型,旧系统被封装,可逐步替换。

ACL 做模型映射与协议适配,把旧系统转换成新系统可用的干净模型,隔离耦合。新系统只依赖 ACL 稳定接口,旧系统可替换。

#
★★

9. API 版本兼容演进中新增字段给默认值、删除字段先废弃后移除,语义化版本如何约束破坏性变更

请解释 API 版本兼容演进,说明新增字段给默认值、删除字段先废弃后移除,以及语义化版本如何约束破坏性变更?

  • 向后兼容策略
  • 字段演进(新增默认值、删除先废弃)
  • 语义化版本
  • API 版本兼容演进:保证 API 演进(新增/删除字段)不破坏现有消费者,向后兼容。
  • 新增字段给默认值:新增字段时给默认值(或 null 兼容),旧消费者不传也能工作,向后兼容;删除字段先废弃(deprecated)后移除,给消费者迁移时间。
  • 删除字段先废弃后移除:先把字段标记废弃(文档/deprecated 注解),保留一段时间,消费者迁移后再移除,避免破坏性变更。
  • 语义化版本(SemVer):用 MAJOR.MINOR.PATCH 版本约束变更——破坏性变更(不兼容)必须升 MAJOR(主版本),兼容新增/修改升 MINOR(次版本),bug 修复升 PATCH。SemVer 向消费者明确"哪些变更是破坏性的"。
  • 多版本并存:破坏性变更可提供多版本(v1/v2)并存,网关路由,逐步迁移。
  • 结论:API 演进用"新增字段默认值、删除字段先废弃后移除"保证向后兼容,语义化版本(MAJOR 破坏性变更)约束破坏性变更,多版本并存平滑迁移。

向后兼容靠新增默认值、删除先废弃;SemVer 用 MAJOR 表达破坏性变更,约束契约演进。多版本并存平滑迁移。

#

10. Circuit Breaker 与重试/超时在云原生的标准组合

请解释 Circuit Breaker 与重试/超时在云原生的标准组合?

  • 熔断器
  • 重试与超时
  • 标准组合
  • Circuit Breaker(熔断器):当下游连续失败达到阈值时熔断,快速失败(返回错误),不再调用下游,避免故障扩散;经过冷却时间后半开尝试恢复,成功则闭合。
  • 重试:对瞬时错误重试(指数退避 + 抖动),恢复成功。
  • 超时:为调用设置超时,避免无限等待占用资源。
  • 标准组合:超时 + 重试 + 熔断是云原生标准组合——超时防止无限等待;重试处理瞬时错误(但需防重试风暴);熔断在连续失败时停止调用,避免超时/重试放大故障。熔断是"防重试风暴"的兜底,重试是"熔断闭合前尝试恢复"的补充。
  • 组合要点:重试需指数退避 + 抖动 + 熔断配合,避免重试风暴;熔断阈值与重试次数协调。
  • 结论:超时防无限等待、重试恢复瞬时错误、熔断防故障扩散,三者标准组合保证云原生调用弹性。

超时 + 重试 + 熔断是云原生标准:超时防等待、重试恢复瞬时、熔断防扩散(避免重试风暴)。三者协调保证弹性。

#

11. Outbox 模式如何保证领域事件可靠发布,本地事务同时写业务表与 Outbox 表,独立进程轮询 Outbox 发布到消息队列

请解释 Outbox 模式如何保证领域事件可靠发布,说明本地事务同时写业务表与 Outbox 表、独立进程轮询 Outbox 发布到消息队列?

  • 本地事务双写
  • 独立进程发布
  • 可靠发布
  • Outbox 模式:解决"业务库更新 + 发布领域事件"的原子性/可靠性问题,避免"先写库后发消息"导致的丢消息/不一致。
  • 本地事务双写:业务操作在同一本地事务中,同时写业务表和 Outbox 表(事件表),保证原子提交——要么业务表和事件都提交,要么都不提交。事件作为 Outbox 记录落库。
  • 独立进程发布:独立进程(或任务)轮询 Outbox 表,读取未发布事件,发布到消息队列,发布后标记已发布(/删除)。进程与业务解耦,可异步、可重试。
  • 可靠发布:事件先落库(与业务同事务),再发布,即使发布失败也可重试(Outbox 记录未删),不丢事件;消费者幂等消费兜底重复。
  • 结论:Outbox 用"本地事务双写业务表 + Outbox 表"保证事件不丢,独立进程轮询发布到 MQ,实现领域事件可靠发布,避免分布式事务。

Outbox 靠同事务双写(事件先落库)+ 独立进程轮询发布(可重试),保证事件可靠发布,避免"先写库后发消息"丢消息问题。

#

12. Bulkhead(舱壁)模式如何隔离故障避免雪崩

请解释 Bulkhead(舱壁)模式如何隔离故障避免雪崩?

  • 舱壁隔离
  • 资源隔离
  • 防雪崩
  • Bulkhead(舱壁/隔离舱):把系统资源按依赖/服务隔离成独立舱(如独立的线程池、连接池、队列),像一个船舱不漏水——一个舱故障不影响其他舱。
  • 隔离方式:为每个下游/服务分配独立的资源池(线程池、连接池、信号量),一个下游耗尽/故障只影响自己的舱,不拖垮其他下游。
  • 避免雪崩:若所有下游共享一个线程池,一个慢下游会占满线程池,导致其他下游得不到资源,级联故障(雪崩)。Bulkhead 隔离资源,一个依赖故障只影响对应舱,其他依赖正常,防止雪崩。
  • 与熔断配合:Bulkhead 隔离资源,熔断在故障时快速失败,两者结合增强弹性。
  • 结论:Bulkhead 用资源隔离(独立线程池/连接池)把故障限制在舱内,避免一个依赖拖垮整体,防止雪崩。

Bulkhead 隔离资源,一个依赖故障只影响自己的舱,避免共享池被一个慢依赖耗尽导致雪崩。是防级联故障的弹性手段。

#

13. 事件驱动架构如何降低界限上下文间的耦合

请解释事件驱动架构如何降低界限上下文间的耦合?

  • 事件驱动解耦
  • 上下文间通过事件交互
  • 异步与最终一致
  • 事件驱动架构:界限上下文(或服务)通过发布/订阅事件交互,而非直接同步调用。一个上下文发布事件,其他上下文订阅并响应。
  • 降低耦合:① 发布方不知道订阅方(通过事件总线/消息解耦),不直接依赖;② 订阅方只依赖事件契约,不依赖发布方实现;③ 上下文可独立演进、独立部署(加新订阅方不影响发布方)。
  • 异步与最终一致:事件异步传播,上下文间解耦、最终一致,不阻塞调用方。
  • 收益:上下文通过事件契约解耦,降低相互依赖,便于独立演进与扩展。
  • 注意:事件需确定性契约(schema)、幂等消费、可靠发布(Outbox),保证解耦后的可靠性与一致性。
  • 结论:事件驱动让上下文通过事件解耦——发布方不依赖订阅方,订阅方只依赖事件契约,异步最终一致,降低耦合。

事件驱动让上下文通过事件(而非直接调用)交互,发布方不依赖订阅方,订阅方只依赖事件契约,降低耦合、便于独立演进。

#

14. 如何用路由/网关在调用方无感下切流新旧实现

请解释如何用路由/网关在调用方无感下切流新旧实现?

  • 路由/网关切流
  • 调用方无感
  • 灰度与回滚
  • 用路由/网关切流:在调用方与新旧实现之间加路由/网关层,根据规则(流量比例、租户、功能、灰度标记)把请求分发到新实现或旧实现,切换对新旧实现透明。
  • 调用方无感:调用方只调用网关的统一地址,不感知新旧实现;网关后端可以平滑切换(按比例切流、灰度、回滚),调用方无需改动。
  • 切流机制:网关按权重/规则路由(如 1% 用户走新实现,逐步放量);支持灰度(按用户/标记分流)、蓝绿(新旧并存切换)、回滚(失败切回旧实现)。
  • 收益:调用方无感,切换风险可控,可灰度、可回滚,新旧实现平滑过渡。
  • 结论:加路由/网关层,按规则切流新旧实现,调用方无感,支持灰度与回滚,平滑迁移。

路由/网关作为统一入口按规则切流,调用方无感,支持按比例/灰度/蓝绿切流与回滚,降级迁移风险。

#

15. 绞杀者模式与蓝绿/灰度发布的配合关系

请解释绞杀者模式与蓝绿/灰度发布的配合关系?

  • 绞杀者模式
  • 蓝绿/灰度发布
  • 配合关系
  • 绞杀者模式(Strangler Fig):逐步用新系统替换旧系统,按功能逐个迁移。
  • 蓝绿发布:新旧两套环境并存,流量从旧(蓝)切换到新(绿),切换快、回滚快(切回蓝)。
  • 灰度发布:按比例/用户逐步放量到新版本,观察稳定后再全量。
  • 配合关系:绞杀者模式用蓝绿/灰度作为切流手段——每迁移一个功能,用蓝绿(新旧环境并存切换)或灰度(按比例放量)把该功能流量切到新实现,观察稳定再继续。绞杀者是"迁移策略"(逐个替换),蓝绿/灰度是"切流+发布方式"(如何安全切换)。
  • 结合:绞杀者按功能逐个替换,每个功能用蓝绿/灰度安全切流,保留回滚能力,逐步完成迁移。
  • 结论:绞杀者决定"迁移哪些功能",蓝绿/灰度决定"如何安全切流",两者配合实现逐步、可控、可回滚的迁移。

绞杀者是迁移策略(逐个替换),蓝绿/灰度是切流发布方式(安全切换)。配合实现逐步可控可回滚的迁移。

#

16. 绞杀者模式中如何保证新旧系统双写的数据一致性窗口与对账机制?

请解释绞杀者模式中如何保证新旧系统双写的数据一致性窗口与对账机制?

  • 双写
  • 一致性窗口
  • 对账机制
  • 双写:迁移期间,写操作同时写入新旧两个系统,保证两边数据一致,才能支撑读取切换与对比。
  • 一致性窗口:双写是异步的(或两写间有延迟),新旧系统数据存在短暂不一致——一致性窗口(从旧系统写入到新系统同步完成的时间)。设计上尽量缩短窗口,优先切写路径,再过渡读路径。
  • 对账机制:定期对账新旧数据,对比差异(主键集合、字段校验和/哈希),发现不一致则修复(补写、重同步),保证最终一致。
  • 可靠性:双写需防丢(outbox/重试)、幂等;对账审计发现裂缝并修复。
  • 结论:双写期间通过"缩短一致性窗口 + 对账机制"保证新旧数据最终一致,写路径优先切换、读路径过渡,对账补差修复。

双写有短暂一致性窗口,靠缩短窗口(先写后读)+ 对账机制(对比差异、修复补差)保证新旧数据最终一致。

#

17. 单体拆分时如何用“模块边界先行”(模块化单体)降低迁移风险,什么信号表明可以拆成微服务?

请解释单体拆分时如何用"模块边界先行"(模块化单体)降低迁移风险,以及什么信号表明可以拆成微服务?

  • 模块化单体先行
  • 降低迁移风险
  • 拆微服务的信号
  • 模块边界先行:先不拆服务,而是把单体内部划清模块边界(模块间通过接口、数据隔离),形成模块化单体。先理顺边界,再考虑拆成微服务。
  • 降低迁移风险:模块化单体让边界清晰、可独立测试,为拆微服务打基础;拆服务时按模块边界切割,风险低(已有清晰边界,不会拆乱);模块化单体本身也提升可维护性。
  • 拆微服务的信号:① 模块边界已清晰稳定;② 需要独立部署/独立扩展(不同模块发布节奏不同、伸缩需求不同);③ 团队规模大(按服务划分归属,减少协调);④ 数据边界清晰(各模块独立数据,可独立演化);⑤ 性能瓶颈明确(某个模块需独立扩展)。
  • 结论:先模块化单体划清边界,再按"独立部署需求 + 团队规模 + 数据边界清晰"信号拆微服务,降低迁移风险。

模块边界先行(模块化单体)先理顺边界,降低拆服务风险;拆微服务信号是独立部署/扩展需求、大团队、数据边界清晰、明确瓶颈。

#

18. 并行运行期的监控中新旧系统错误率与延迟基线对比、差异告警与自动回滚阈值如何设定

请解释并行运行期的监控,说明新旧系统错误率与延迟基线对比、差异告警与自动回滚阈值如何设定?

  • 新旧系统基线对比
  • 差异告警
  • 自动回滚阈值
  • 并行运行期监控:新旧系统并行运行时,持续监控两者指标,对比差异,确保切流安全。
  • 错误率与延迟基线对比:先采集新系统的基线(错误率、延迟 P99/P50),与旧系统对比——新系统错误率应接近或低于旧系统,延迟应接近或可接受(不显著劣化)。对比差异判断新系统是否正常。
  • 差异告警:设定差异告警——新旧系统指标差异超过阈值(如错误率偏差 > 1%、延迟 P99 超旧系统 20%)触发告警,提醒异常。
  • 自动回滚阈值:设定自动回滚阈值——新系统错误率/延迟超过阈值(如错误率超 1%、P99 超基线 2 倍)时,自动切回旧系统(回滚),保护系统。阈值要留有余量(避免误判)但足够灵敏(尽早回滚)。
  • 结论:并行运行期监控新旧系统错误率与延迟基线,设差异告警与自动回滚阈值,异常时触发告警并自动回滚,保障切流安全。

并行期监控靠"基线对比 + 差异告警 + 自动回滚阈值"。新系统指标与旧基线对比,超阈值告警/回滚,平衡灵敏与误判。