# Segment 微服务反水案的一点思考

2015 年底，Segment 博客刊登了一篇文章，[Why Microservices Work For Us（下文简称为《Work》）](https://segment.com/blog/why-microservices/)，三年多，又来了一篇更具话题性的新作：[Goodbye Microservices: From 100s of problem children to 1 superstar（下文简称为《Bye》）](https://segment.com/blog/goodbye-microservices/)，两相比较，感觉还是有一定的代表性的，这里做一点整理和记录。

## 微服务的动机

《Work》中提到，主要是因为原有架构中，故障处理不力，需要有更快的诊断和处理方法，这里提到了两个途径：

1. 问题的快速定位：借助更细致方便的监控指标，为突发事件提供明确指导。
1. 问题的快速解决：在定位问题之后，能够更好的从源码中获得支持并解决问题。

要针对某个功能加入监控指标，在原有单体架构中可能会造成不必要的影响，范围不易控制；而源码方面，越小的服务，通常也代表着相对易读的代码；这两个主要需求都指向了同样的解决方案：微服务。

## 微服务带来的好处

将原有单体应用拆分为微服务之后，不但解决了监控和排错的问题，还带来了一些额外的好处：

1. 隔离的消息队列，不同订阅互不干涉
1. 隔离系统资源，如 CPU 和内存。
1. 隔离网络，如 ELB。

这里只是原文中提到的好处，更多的照本宣科内容这里就不赘述了。

## 改造的隐忧

《Work》一文中提到了两次，新建微服务应该如何如何。作者似乎认为，微服务有必要更快的创建起来。但按照我的理解，微服务仅是通过进程隔离的强制手段使得模块之间的边界更加清晰，事实上，因为缺乏单体应用强大的上下文支持，同一系统内的不同微服务，往往会因为上下文问题，导致更加复杂的开发过程。

## 《Bye》一文中补充的拆分过程

1. 单代码库阶段

    因为缺乏有效的时间说明，这一操作让人很迷惑，不知道是不是在《Work》发表的时候，各个服务还是在共用同一套代码。也在共用同一套测试方案。《Bye》中提到，一个失败的提交会导致整体测试失败，因此我们大致可以说，这一阶段里，CI/CD 过程也没能完成分割，个人认为，这种情况不太应该算作微服务。

1. 多代码库阶段

    为微服务独立创建各自的代码库，并享用各自的测试组件。

1. 共享库阶段

    我认为这一阶段呼应了前面的隐忧，在微服务落地之处，为了更快的建立微服务，开始出现了跨服务的共享代码库。

## 败局的开始

《Bye》的共享代码库一节，罗列了不少遇到的问题：

1. 共享库版本出现碎片：因为工程需要，并不敢冒险同时更新的多个服务的共享代码，造成部分服务的共享代码滞后。
1. 伸缩能力：微服务架构经常鼓吹的能力，在这里似乎被狠狠抽了一巴掌。

## 个人的一点分析

我眼中的微服务，务虚的角度上来说，有两个关键字：妥协，怀疑。

这里所说的妥协指的是，我们的系统是存活于一个非“理想状态”中的，不管是“拥抱变化”，还是“面向故障”，都是对不完美世界的具体应对方式。

而怀疑的设计态度，最简单的证据就是对隔离的强调，系统资源、数据的隔离都如此强调，我想，代码的隔离是不言自明的。

反观 Segement 的重构之路，（可能）在宣称微服务的时候，各个服务还躺在同一个仓库里，还在共享同样的测试过程。

在进行代码分离之后，又出现了两个不太容易理解的纰漏：

- 暧昧的代码：前面提到，怀疑是微服务的基本态度，从《Bye》文中可以看到，他们在几十个服务之间共享了需要进行频繁重构的代码，抛开微服务和单体之争不谈，这个行为实在是无法理喻。
- 暧昧的负载：在拆分之后，按照《Work》的说法，每个微服务都有了独立的队列和监控，由此推论，各个微服务的负载模型应该是比较容易做个描述的，很可惜的，这里称之为一种艺术。

## 回到单体之后

此处内容给人一种感觉：“今天是个好日子”。给我的感觉是，一些原本应该在微服务阶段完成的工作，甚至是在微服务阶段之前的单体阶段完成的工作，终于完成了——例如改进的测试方案，这能算是半渡而击么？

## 牢骚

流水账写到这里，有两个深刻的印象就是，这个团队从来没想过这些微服务是彼此独立的，也从来没把每个服务作为一个单独的交付物进行完善。从这个印象出发，回到单体形态，可以说是——得其所哉。

