# 《我曾目睹的微服务灾难》的读后感

2011-2012 年，我所在的团队正在给某国字号企业交付一个集团级的企业应用，这个应用覆盖了除 IaaS 之外的整个上层，层级和宽度上来说都算得上是个大家伙了。在当时，这个项目有几个新鲜的点，例如硬件全部采用通用的 x86 刀片服务器，全部基础软件都使用了开源软件——例如 LAMP、Nginx、MongoDB（是的你没看错，就是 MongoDB），使用 Java、PHP、ASP.NET 等多种平台的多个厂商协作交付；除此之外，还设计了和当时大行其道的 SoA 不太一样的一种架构，这个架构有几个特点：

- 因为其中几百个子系统都是为各自不同的业务（部门）服务的，换句话说，其实我当时的建设过程是多个建设方对应多个甲方的，因此我们采取的策略是以业务为核心，用高内聚低耦合的基本原则进行子系统的拆分。
- 提供一种基于 HTTP 的二进制传输协议，不同开发商依照约定接口各自用各自的开发平台和**各自的数据库**进行开发和交付。
- 部分全局能力，例如认证鉴权、门户、PaaS 级服务及其 SDK 由总集成商提供。
- 对于高负载应用，其横向扩展能力是首要考量标准（在那个年代，扩缩容是个复杂且高危的操作），在满足业务需求的基本条件下，对子系统中的模块，按照若干原则进行拆分为不同进程：
    - 可扩缩和不可扩缩容的模块
    - 扩缩容不同步的模块
    - 负载规模不一致的不同模块

总的说来这次交付是相当成功的，然而无损创新是不太符合历史规律的，这种架构带来的问题同样挠头：

1. 数据流转：前面提到，很多子系统会使用自己的数据库，所以有些数据需要在不同子系统之间进行流转，就有可能造成数出多头的问题。
1. 监控要求高：刀片服务器和小型机相比，数量大增、性能和稳定性又稍有不足，因此对硬件和系统进行监控的需求就陡然提高，另外因为进程之间、节点之间通信次数和通信量的暴涨，对于网络连通性、磁盘和网络 IO 等的监控需求也是水涨船高。
1. 运维难度大：不同的应用、不同的平台、大量进程，还有更要命的不同的数据库，对运维团队的要求极高（事实上离开项目一年之后我还能听到来自该项目运维团队的骂声。）
1. ....

回到《灾难》一文，其中提到的很多更加具体的点，摘抄几点，一起休闲一下。

## 灾难一：服务太小

**20 名工程师组成了维护 50 项服务的小组。一人负责一项服务还不够！**：我一直认为，微服务是一个向现实妥协的过程，这个现实应该是全面的，它不仅是业务的现实、也是团队能力的现实，这个语境下所提倡的全能小团队，其能力虽然宽泛，但即使是业务软件也是有其服务上限的，不尊重业务负载和不尊重团队负载，并没有什么区别。量入为出是个基本要求。

**Another smell was when someone told me that deploying a new feature in service A also needed a deployment — at the same time — in service B.（有人要求我把一个新功能同时部署到两个不同服务之中）**：这个例子很有代表性，这里的 Someone 同时是 Service A 和 Service B 两个不同服务的所有者或者部分所有者。所以这一点就面临几个问题：

- 这两个服务应该分开的么？为什么会共享同一个功能？
- 新功能是不是应该拆分开来，成为第三个服务？
- Someone 到底是谁？

**与仅仅在 IDE 中查看一个项目不同，人们需要一次打开多个项目才能了解所有这些混乱的情况。**：其实即使是一个单体应用，只要它规模太大，在外人来说也是很难突然就能够 “make sense of all that mess” 了——别人的代码在功能和非功能层面满足服务要求，没有在边界外造成不良影响，按照契约进行开发和测试，根据讲定的边界做好各种限制各种观测，为啥非要把手伸那么长呢，是职责不清还是拆分有误？

## 灾难二：开发环境

**Mobile developers not developing a feature before it was in a development environment or backend developers who wanted to try their service didn't break any business flow. It was also problematic if someone wanted to test the whole flow in a mobile app before production.**：这个问题涉及到的是依赖服务之间的协作开发的问题，实际上所有不同实体之间的调用，不管是内部的函数调用、还是古代的 COM+、CORBA，后来的 WebService/RESTful 等等，都面临同样的环境和上下游依赖问题。至于后续的若干的问题，实际上都是全局角度上的微服务治理问题——其实不管有没有微服务，协作单元多了一定会出现这种情况，像 Grafana Stack、ES Stack、Skywalking 等观测技术，以及 Service Mesh 等的网格技术，都是为了解决这样的难题的。十二要素、云原生等方法也给出了相对具体的设计、部署和运行方法的支持。

## 灾难三：端到端测试

这个——非常特别同意。

## 灾难四：巨大的共享数据库

这个其实应该属于典型的人祸了，到现在应该没人会认为共享数据库的多个进程能够称之为微服务了。

## 灾难五：API 网关

**Suddenly, you have your "API gateway" being a single-point-of-failure — because people find it easier to handle authentication in a single place — and with some unintended business logic inside it. Instead of having a monolith getting all of the traffic, now you have a home-made Spring Boot service getting all of it!**：网关和服务网格这样的产品，发展下来经常会扩展成具备大量功能的超级工具，这给人一种联想——上了工具之后写写配置就有功能用了这实在是太棒了。然而 Java 开发者或者 YAML 工程师都会知道，**配置** 这事太难了，以至于出现了 OPA、PIPY 这样直接让配置工程师撸代码的“反潮流”工具。

事实上采用一个开源/第三方软件或者库，因其规模不同，对应的评价工作量是完全不同的，尤其对于 Kong、Istio 这样的大家伙来说，比起“用不用”的问题来说，“用多少”和“怎么用”的问题可能更加复杂，动辄“全面拥抱”可能是一个非常冒险的行为。

## 灾难六：超时、重试和弹性

这个似乎是服务间调用的普遍情况，我不确定这是微服务的锅。

## 结论

在实施转型或者说改造的过程中，难免会遇到这样那样的问题，然而微服务其实并没有什么特别的——就特别多、特别碎。如果能在全局层面做好观测、做好治理，练好基本功，让每个服务都能各司其职又不互相妨碍，是不是微服务又能有什么关系呢。微服务是为业务服务，同现状和解的，抛开目的和现实，单纯为微服务而微服务，很可能除了话题，一无所获。

## 参考阅读

- [我曾目睹的微服务灾难](https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=2651076230&idx=1&sn=b149039892829f8afa940d3cf5104a0f&chksm=bdb9ced58ace47c345f9e49dff324bacf91a154be247815c4fbb780c448376448dfed66481b9&mpshare=1&scene=1&srcid=0509sLCazm1rnEDoVIEmmfaJ&sharer_sharetime=1620528375378&sharer_shareid=1d93fb5fa2b29b35d135653bdc08e257#rd)

- [Disasters I've seen in a microservices world](https://world.hey.com/joaoqalves/disasters-i-ve-seen-in-a-microservices-world-a9137a51)

- [Segment：为什么微服务适合我们](https://blog.fleeto.us/post/segment-why-microservices/)

- [Segment 放弃了微服务](https://www.infoq.cn/article/v3VjP*BYtY3qhD3438ay)

- [微服务](https://blog.fleeto.us/post/microservice/)
