# 拆代码之前，先想好怎么拆客户

> 银行要隔离和季度更新，零售商要新功能尽快上线，这时部署标记比微服务更关键。

原文: https://fdetimes.net/zh/analysis/deployment-stamps-vs-microservices-multi-tenant/

设想你要把同一套方案交付给七个客户：一家银行和六家中型零售连锁。银行不希望自己的数据和别人的放在一起，而且每季度只接受一次更新。零售连锁则希望新功能越快上线越好。

工程团队面对架构，第一个问题往往是选单体还是微服务。对前线部署工程师（FDE）来说，更重要的问题是怎么拆分客户：谁共享基础设施，谁拿到专属副本，谁在什么时候收到更新。

微服务拆的是代码，部署标记拆的是客户。把两者混为一谈，结果就是系统很贵，却依然满足不了最挑剔的那个客户。

## 微服务解决的是另一个问题

AWS 介绍微服务时强调了一个明确的好处：服务之间相互独立，应用的容错能力更强。这个好处是真实的，但有代价。早在 2014 年，Martin Fowler 就指出，分布式事务出了名地难做对。

Fowler 的建议也很务实：先从单体开始，保持模块化，等单体开始带来问题时再拆成微服务。这很适合 FDE 的工作场景：人手通常不多，时间很紧，系统得先在客户环境里跑起来，才轮得到讨论架构是否优雅。

也有一个常被引用的数字指向相反方向。据 DevClass 在 2023 年 5 月的报道，Amazon 的一份内部案例研究显示，负责监控音视频质量的 Prime Video 团队把分布式组件合并成单个进程，基础设施成本下降了 90% 以上。

这个结果只适用于该团队自身的情况，但它提醒我们：拆分从来不是免费的。

回到开头那家银行。把系统拆成 12 个服务，它的两项要求一项也满足不了。

即便有 12 个服务，银行仍然和其他所有客户共用一个数据库、一套发布计划。

## 合并计算很便宜，但一切都绑在一起

Azure 架构中心有一个模式，和单体“合并以省钱”的思路相近：计算资源整合（Compute Resource Consolidation），即让多个任务共享计算资源以降低成本。这个模式关注的是任务如何在基础设施上运行，而不是代码如何组织，但它清楚地展示了合并的代价。

按照该文档的说法，更新一个任务，就得停止、重新部署并重启同一计算单元里的所有其他任务；合并后的任务还共享同一个故障域和安全上下文。

同一份文档指出，处理高度敏感或隐私数据的任务不应合并，因为它们需要独立的安全上下文。放到面向多个客户交付的场景里，所有人都跑在一个整体中的局限就很明显了：它能一直用下去，直到第一个客户要求数据隔离。

即使保留单体，也有一个错误应当从一开始就避开。Microsoft 把所有数据塞进同一个数据存储列为反模式，因为这会造成资源争用、拖慢性能。建议是按数据的使用方式分开存放。

代码层面是单体，并不意味着所有数据只能放在一个数据库里。

## 部署标记：按客户拆分，而不是按功能拆分

部署标记从另一个方向解决问题。它不把应用拆成多个部分，而是部署整个应用的多个独立副本，每个副本有自己的数据存储。在多租户环境中，每个部署标记服务一组预先确定的租户。

Microsoft 描述的一个场景与 FDE 的工作非常接近：部分大客户可能需要专属实例，小客户则共享一个多租户部署。部署标记还能解决更新节奏问题。

有的客户接受频繁更新，有的客户厌恶风险、希望少更新，因此每个部署标记可以采用不同的部署环。

套用到开头的七个客户：标记 A 只服务银行，拥有独立的数据和安全上下文；标记 B 由六家零售连锁共用。

每个新版本先发到标记 B，平稳运行几周后，才进入标记 A。代码仍是模块化单体，变的只是部署方式。

| 标准 | 计算资源整合 | 微服务 | 部署标记 |
|---|---|---|---|
| 拆分轴线 | 不拆分；多个任务共享一个计算单元 | 按功能拆分代码 | 按租户组拆分客户 |
| 发布节奏 | 改一个任务就要重新部署整个单元 | 无法按客户设定节奏 | 每组客户有各自的部署环和节奏 |
| 敏感数据隔离 | 共享安全上下文，不适用 | 无法按客户解决 | 大客户可以有专属部署标记 |
| 主要代价 | 共享故障域 | 分布式事务难做对 | 基础设施大增，租户难迁移 |
| 适用场景 | 任务不需要独立的安全上下文 | 单体确实已经带来问题 | 客户在隔离或更新节奏上有差异 |

这张表很容易被读成三选一，其实不然。部署标记是一种部署方式，单体与微服务之争是代码组织方式，所以模块化单体完全可以跑在多个部署标记上，就像上面银行和零售连锁的例子。

## 每多一个副本都要花钱

Microsoft 对代价也说得很清楚：部署多份基础设施会显著增加运营成本。把租户从一个部署标记迁到另一个也很困难。因此，哪个客户放在哪个部署标记上，应当在客户调研阶段就慎重决定，而不是等客户已经进入生产环境再说。

运行多个部署标记，需要基础设施即代码和漂移控制。没有它们，每次有人在客户环境里手动改动，各个部署标记之间就会多分化一点。Microsoft 还建议至少部署两个部署标记，因为只有一个时，很容易把只在单实例下成立的假设写死在代码里。

**要点:** 如果你预计会用到部署标记，从一开始就部署两个：第二个副本的成本，远低于日后清除“只有一个实例”这类假设的成本。

反过来，部署标记也不是默认选项。按照 Microsoft 的说法，如果方案很简单、不需要大规模扩展，或者单个实例就能承担负载，就不应使用这个模式。如果你只有一个客户，或者所有客户都乐于共享，那么数据分得合理的模块化单体就够了。

## 客户要求数据留在特定区域时

还有一种选择：Geode 模式，其中每个实例都能服务任何用户，而不像部署标记那样只服务一组客户。Microsoft 表示，Geode 的设计和构建通常复杂得多，适合对数据位置没有限制的云原生平台。

所以在考虑 Geode 之前，先在一开始就问清客户对数据位置有没有要求。Microsoft 指出，在有数据驻留要求，或应用需要为会话保存临时状态的情况下，Geode 并不适用。

这些情况下，Microsoft 建议把部署标记与全局路由层结合，由路由层把请求导向正确的部署标记。

如果你的客户要求数据绝不离开某个区域（银行、医院或政府机构常有这种要求），答案几乎是现成的：每个区域一个部署标记，前面加一层路由。它在理论上未必最优雅，但你能向客户的合规团队讲清楚。

## 写给想做 FDE 的开发者

阅读 FDE 或解决方案工程师岗位的职位描述时，留意 multi-tenant、single-tenant deployment、dedicated instance 和 infrastructure as code 这类说法。它们说明公司正面临这类拆分客户的问题，需要同时理解两条架构轴线的人。

简历上不要只写“把系统迁移到微服务”。把你如何决定部署方式、为什么这么决定写清楚：哪些客户拿到了专属实例、原因是什么，发布节奏如何划分，又做了什么来防止各环境之间产生漂移。

一个权衡过取舍的决策故事，比一串技术名词有说服力得多。

面试中如果被问到“单体还是微服务？”，好的回答通常先反问：有多少个客户，他们之间有什么差异？在 FDE 岗位上，怎么拆客户往往比怎么拆代码更能决定项目的成败。

**本周动手试试:**

- 为你服务的客户（真实或假设的）做一张表，列三栏：数据敏感程度、可接受的更新节奏、数据位置要求。然后试着把他们分配到不同的部署标记上。
- 为一个个人项目编写基础设施即代码，用不同配置部署两个副本。记下代码中所有假设“只有一个实例”的地方。
- 找出你现有代码中把所有数据堆在一个数据库里的地方，草拟一个按使用方式拆分的方案。

## 信源

- [Deployment Stamps Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/deployment-stamp)

- [Geode pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/geodes)

- [Compute Resource Consolidation Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/compute-resource-consolidation)

- [Monolithic Persistence Antipattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/antipatterns/monolithic-persistence/)

- [Microservices](https://martinfowler.com/articles/microservices.html)

- [Introduction to Microservices (AWS)](https://aws.amazon.com/microservices/)

- [Reduce costs by 90% by moving from microservices to monolith: Amazon internal case study raises eyebrows](https://devclass.com/2023/05/05/reduce-costs-by-90-by-moving-from-microservices-to-monolith-amazon-internal-case-study-raises-eyebrows/)
