<p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">1984 年,Rob Pike 和 Brian W. Kernighan 在 AT&T 贝尔实验室技术期刊上发表了名为 “<span style="color:#4D8AD8;">Unix 环境编程</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[1]</span>” 的文章,其中他们使用 BSD 的 cat -v 例子来认证 Unix 哲学。简而言之,Unix 哲学是:构建小型、单一的应用程序 —— 不管用什么语言 —— 只做一件小而美的事情,用 stdin / stdout 进行通信,并通过管道进行连接。</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">听起来是不是有点耳熟?</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">是的,我也这么认为。这就是 James Lewis 和 Martin Fowler 给出的 <span style="color:#4D8AD8;">微服务的定义</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[2]</span> 。</p><blockquote style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;"><p>简单来说,微服务架构的风格是将单个 应用程序开发为一套小型服务的方法,每个服务都运行在它的进程中,并用轻量级机制进行通信,通常是 HTTP 资源 API 。</p></blockquote><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">虽然一个 *nix 程序或者是一个微服务本身可能非常局限甚至不是很有用,但是当这些独立工作的单元组合在一起的时候就显示出了它们真正的好处和强大。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.4em;background-color:#3F51B5;">*nix程序 vs 微服务</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">下面的表格对比了 *nix 环境中的程序(例如 cat 或 lsof)与微服务环境中的程序。</p>< 如显示不全,请左右滑动 ><table cellpadding="4" width="694" style="margin:0px 0px 10px;padding:0px;width:378px;"><tbody><tr><th style="text-align:left;font-weight:400;background:#F7F7F7;"> </th><th style="text-align:left;font-weight:400;background:#F7F7F7;">*nix 程序</th><th style="text-align:left;font-weight:400;background:#F7F7F7;">微服务</th></tr></tbody><tbody><tr><td style="border:1px solid #DDDDDD;">执行单元</td><td style="border:1px solid #DDDDDD;">程序使用 stdin/stdout</td><td style="border:1px solid #DDDDDD;">使用 HTTP 或 gRPC API</td></tr><tr><td style="border:1px solid #DDDDDD;">数据流</td><td style="border:1px solid #DDDDDD;">管道</td><td style="border:1px solid #DDDDDD;">?</td></tr><tr><td style="border:1px solid #DDDDDD;">可配置和参数化</td><td style="border:1px solid #DDDDDD;">命令行参数、环境变量和配置文件</td><td style="border:1px solid #DDDDDD;">JSON/YAML 文档</td></tr><tr><td style="border:1px solid #DDDDDD;">发现</td><td style="border:1px solid #DDDDDD;">包管理器、man、make</td><td style="border:1px solid #DDDDDD;">DNS、环境变量、OpenAPI</td></tr></tbody></table><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">让我们详细的看看每一行。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">执行单元</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">*nix 系统(如 Linux)中的执行单元是一个可执行的文件(二进制或者是脚本),理想情况下,它们从 stdin 读取输入并将输出写入 stdout。而微服务通过暴露一个或多个通信接口来提供服务,比如 HTTP 和 gRPC API。在这两种情况下,你都会发现无状态示例(本质上是纯函数行为)和有状态示例,除了输入之外,还有一些内部(持久)状态决定发生了什么。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">数据流</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">传统的,*nix 程序能够通过管道进行通信。换句话说,我们要感谢 <span style="color:#4D8AD8;">Doug McIlroy</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[3]</span>,你不需要创建临时文件来传递,而可以在每个进程之间处理无穷无尽的数据流。据我所知,除了我在 <span style="color:#4D8AD8;">2017 年做的基于 Apache Kafka 小实验</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[4]</span>,没有什么能比得上管道化的微服务了。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">可配置和参数化</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">你是如何配置程序或者服务的,无论是永久性的服务还是即时的服务?是的,在 *nix 系统上,你通常有三种方法:命令行参数、环境变量,或全面的配置文件。在微服务架构中,典型的做法是用 YAML(或者甚至是 JSON)文档,定制好一个服务的布局和配置以及依赖的组件和通信、存储和运行时配置。例如 <span style="color:#4D8AD8;">Kubernetes 资源定义</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[5]</span>、<span style="color:#4D8AD8;">Nomad 工作规范</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[6]</span> 或 <span style="color:#4D8AD8;">Docker 编排</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[7]</span> 文档。这些可能参数化也可能不参数化;也就是说,除非你知道一些模板语言,像 Kubernetes 中的 <span style="color:#4D8AD8;">Helm</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[8]</span>,否则你会发现你使用了很多 sed -i 这样的命令。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">发现</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">你怎么知道有哪些程序和服务可用,以及如何使用它们?在 *nix 系统中通常都有一个包管理器和一个很好用的 man 页面;使用它们,应该能够回答你所有的问题。在微服务的设置中,在寻找一个服务的时候会相对更自动化一些。除了像 <span style="color:#4D8AD8;">Airbnb 的 SmartStack</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[9]</span> 或 <span style="color:#4D8AD8;">Netflix 的 Eureka</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[10]</span> 等可以定制以外,通常还有基于环境变量或基于 DNS 的<span style="color:#4D8AD8;">方法</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[11]</span>,允许您动态的发现服务。同样重要的是,事实上 <span style="color:#4D8AD8;">OpenAPI</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[12]</span> 为 HTTP API 提供了一套标准文档和设计模式,<span style="color:#4D8AD8;">gRPC</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[13]</span> 为一些耦合性强的高性能项目也做了同样的事情。最后非常重要的一点是,考虑到开发者经验(DX),应该从写一份好的 <span style="color:#4D8AD8;">Makefile</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[14]</span> 开始,并以编写符合 <span style="color:#4D8AD8;">风格</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[15]</span> 的文档结束。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.4em;background-color:#3F51B5;">优点和缺点</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">*nix 系统和微服务都提供了许多挑战和机遇。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">模块性</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">要设计一个简洁、有清晰的目的,并且能够很好地和其它模块配合的某个东西是很困难的。甚至是在不同版本中实现并引入相应的异常处理流程都很困难的。在微服务中,这意味着重试逻辑和超时机制,或者将这些功能外包到服务网格service mesh是不是一个更好的选择呢?这确实比较难,可如果你做好了,那它的可重用性是巨大的。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">可观测性</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">在一个独石monolith(2018 年)或是一个试图做任何事情的大型程序(1984 年),当情况恶化的时候,应当能够直接的找到问题的根源。但是在一个</p>
<pre class=""><ol class="list-paddingleft-2"><li><p><span class="" style="color:#E28964;">yes</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#B8FFB8;">|</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#E28964;">tr</span><span class="" style="color:#B8FFB8;"> \\n x </span><span class="" style="color:#B8FFB8;">|</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#E28964;">head</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#B8FFB8;">-</span><span class="" style="color:#B8FFB8;">c </span><span class="" style="color:#3387CC;">450m</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#B8FFB8;">|</span><span class="" style="color:#B8FFB8;"> </span><span class="" style="color:#E28964;">grep</span><span class="" style="color:#B8FFB8;"> n</span></p></li></ol></pre>
<p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">或者在一个微服务设置中请求一个路径,例如,涉及 20 个服务,你怎么弄清楚是哪个服务的问题?幸运的是,我们有很多标准,特别是 <span style="color:#4D8AD8;">OpenCensus</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[16]</span> 和 <span style="color:#4D8AD8;">OpenTracing</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[17]</span>。如果您希望转向微服务,可预测性仍然可能是较大的问题。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.2em;background-color:#3F51B5;">全局状态</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">对于 *nix 程序来说可能不是一个大问题,但在微服务中,全局状态仍然是一个需要讨论的问题。也就是说,如何确保有效的管理本地化(持久性)的状态以及尽可能在少做变更的情况下使全局保持一致。</p><p class="" style="font-family:Optima-Regular, PingFangTC-light;text-align:justify;color:#FFFFFF;font-size:1.4em;background-color:#3F51B5;">总结一下</p><p style="color:#333333;font-family:Optima-Regular, PingFangTC-light;font-size:14px;text-align:justify;">最后,问题仍然是:你是否在使用合适的工具来完成特定的工作?也就是说,以同样的方式实现一个特定的 *nix 程序在某些时候或者阶段会是一个更好的选择,它是可能在你的组织或工作过程中的一个<span style="color:#4D8AD8;">最好的选择</span><span class="" style="font-size:9px;vertical-align:super;color:#FFFFFF;line-height:1em;background-color:#666666;">[18]</span>。无论如何,我希望这篇文章可以让你看到 Unix 哲学和微服务之间许多强有力的相似之处。也许我们可以从前者那里学到一些东西使后者受益。</p>