重温 2018 年的 Unix 哲学

在现代微服务环境中,构建小型、专注的应用程序的旧策略再次焕发新生。
411 位读者喜欢这篇文章。
Getting started with SQL

Opensource.com

1984 年,Rob Pike 和 Brian W. Kernighan 在 AT&T 贝尔实验室技术期刊上发表了一篇名为“Unix 环境中的程序设计”的文章,文章中他们以 BSD 的 cat -v 实现为例,论证了 Unix 哲学。简而言之,该哲学是:构建小型、专注的程序(无论使用何种语言),这些程序只做一件事,但要做好这件事,通过 stdin/stdout 进行通信,并通过管道连接。

听起来耳熟吗?

是的,我也这么认为。这与 James Lewis 和 Martin Fowler 提供的 微服务定义 非常相似。

简而言之,微服务架构风格是将单个应用程序开发为一组小型服务的 approach,每个服务都在自己的进程中运行,并通过轻量级机制(通常是 HTTP 资源 API)进行通信。

虽然一个 *nix 程序或一个微服务本身可能非常有限甚至不是很有趣,但正是这些独立工作单元的组合揭示了它们的真正好处,以及因此而来的力量。

*nix 与微服务

下表比较了 *nix 环境(例如 catlsof)中的程序与微服务环境中的程序。

  *nix 微服务
执行单元 使用 stdin/stdout 的程序 具有 HTTP 或 gRPC API 的服务
数据流 管道 ?
配置和参数化 命令行参数、

环境变量、配置文件
JSON/YAML 文档
发现 包管理器、man、make DNS、环境变量、OpenAPI

让我们更详细地探讨每一行。

执行单元

*nix(例如 Linux)中的执行单元是可执行文件(二进制文件或解释型脚本),理想情况下,它从 stdin 读取输入并将输出写入 stdout。微服务设置处理的是公开一个或多个通信接口(例如 HTTP 或 gRPC API)的服务。在这两种情况下,您都会找到无状态示例(本质上是纯粹的功能行为)和有状态示例,在有状态示例中,除了输入之外,一些内部(持久化)状态决定了会发生什么。

数据流

传统上,*nix 程序可以通过管道进行通信。换句话说,感谢 Doug McIlroy,您无需创建临时文件来传递数据,并且每个文件都可以处理进程之间几乎无尽的数据流。据我所知,除了我 2017 年基于 Apache Kafka 的小型实验之外,微服务中没有与管道标准化的东西。

配置和参数化

您如何配置程序或服务——无论是永久性的还是按需的?嗯,对于 *nix 程序,您基本上有三个选项:命令行参数、环境变量或完整的配置文件。在微服务中,您通常处理 YAML(甚至更糟,JSON)文档,这些文档定义了单个微服务的布局和配置,以及依赖项和通信、存储和运行时设置。示例包括 Kubernetes 资源定义Nomad 作业规范Docker Compose 文件。这些可能是参数化的,也可能不是;也就是说,要么您有一些模板语言(例如 Kubernetes 中的 Helm),要么您会发现自己做了很多 sed -i 命令。

发现

您如何知道有哪些程序或服务可用以及它们应该如何使用?嗯,在 *nix 中,您通常有一个包管理器以及古老的 man;在它们之间,它们应该能够回答您可能有的所有问题。在微服务设置中,在查找服务方面有更多的自动化。除了像 Airbnb 的 SmartStackNetflix 的 Eureka 这样的定制方法之外,通常还有基于环境变量或基于 DNS 的 方法,允许您动态发现服务。同样重要的是,OpenAPI 为 HTTP API 文档和设计提供了事实上的标准,而 gRPC 为更紧密耦合的高性能用例提供了相同的标准。最后但并非最不重要的一点是,考虑到开发者体验 (DX),从编写良好的 Makefile 开始,到使用(或在?)style 编写文档结束。

优点和缺点

*nix 和微服务都提供了一些挑战和机遇

可组合性

设计一个具有清晰、明确焦点并且可以与他人良好协作的东西是很难的。跨不同版本正确地实现它并引入相应的错误处理能力就更难了。在微服务中,这可能意味着重试逻辑和超时——也许将这些功能外包到服务网格中是一个更好的选择?这很困难,但如果您做对了,它的可重用性将是巨大的。

可观察性

在单体应用(2018 年)或试图完成所有事情的大型程序(1984 年)中,当事情出错时,找到罪魁祸首是相当简单的。但是,在一个

yes | tr \\n x | head -c 450m | grep n

或涉及 20 个服务的微服务设置中的请求路径中,您甚至如何开始弄清楚哪个服务行为不端?幸运的是,我们有标准,特别是 OpenCensusOpenTracing。如果您正在考虑迁移到微服务,可观察性可能仍然是最大的单一障碍。

全局状态

虽然对于 *nix 程序来说可能不是什么大问题,但在微服务中,全局状态仍然是一个讨论的问题。即,如何确保有效地管理本地(持久化)状态,以及如何尽可能轻松地使全局状态保持一致。

总结

最后,问题仍然是:您是否为给定的任务使用了正确的工具?也就是说,就像专门的 *nix 程序实现一系列功能可能是某些用例或阶段的更好选择一样,单体应用 可能是您组织或工作负载的最佳选择。无论如何,我希望本文能帮助您看到 Unix 哲学和微服务之间的许多强烈相似之处——也许我们可以从前者中学到一些东西来使后者受益。

mh9 pic
Michael 是红帽公司 Kubernetes 和 OpenShift 的开发者倡导者,他在那里帮助应用运维人员构建和运营应用程序。他的背景是大规模数据处理和容器编排,并且他在 W3C 和 IETF 的倡导和标准化方面经验丰富。在加入红帽之前,Michael 曾在 Mesosphere、MapR 以及爱尔兰和奥地利的两家研究机构工作。

5 条评论

我认为 unix 哲学的真正重点不是进程和管道。如果您查看他们使用的任何示例,他们都没有构建工具然后将它们连接起来。他们采用现有的、强大的工具并将它们组合在一起。真正的重点是通过利用现有的工作来避免做太多的工作。大多数微服务教学法都不是这样工作的。您自己构建所有服务,然后将它们组合在一起。Unix 工具使用管道是为了使组合工具变得容易。如果一切都以文本形式运行,那么两个工具很可能能够协同工作。如果您自己构建工具,您可以完全控制它们的接口方式。您可以使用 JSON 或二进制 IPC,或者您的语言的调用约定。主要的相似之处可能是这两种方法都将工作单元划分为不同的进程。但我认为部分原因是这样做的原因可能不同。Unix 这样做是为了节省内存。如果管道中的第二个进程在第一个进程完成之前无法运行,那么您必须将第一个进程的所有输出保存在内存中。通过并行运行,您只需一次存储一行。另一方面,微服务对使用并行性来跨 CPU 核心和跨机器拆分工作感兴趣。

尽管如此,您可以使用微服务来实现此目的。我最近调查了在我的服务器上使用一个名为 stunnel 的工具。前提很简单:stunnel 接受多播上的 tls 请求,然后将原始 tcp 转发到本地主机上运行的某些东西。您不必在编写的每个应用程序中处理 tls,您可以让 stunnel 开发人员为您处理它。这是类似于 unix 的组合,接口是 tcp 套接字(毕竟 tcp 套接字是仿照管道建模的)。

感谢您的评论 syrrim,信不信由你,Doug McIlroy 本人通过电子邮件对此发表了评论。以下是我今天收到的他的消息的原文

> 您关于“重温 Unix 哲学”的评论中有一位评论者没有抓住管道的重点。
> 我写了以下回复,但在被要求注册时犹豫了。我不会躲藏,最
> 不会躲藏在社交媒体的墙后。欢迎您将其传递下去。
>
> “Unix 这样做是为了节省内存。” 我以前从未听说过这种说法。
> 管道当然避免了分配内存、用其名称弄乱程序、
> 和释放内存的麻烦。管道也可能节省内存带宽和延迟。但节省内存本身
> 很少,甚至从未成为重点。
>
> 然而,您的观察,“通过并行运行,您只需一次存储一行,”
> 至关重要。它允许人们实时与管道交互。并且它允许管道提供不间断的
> 服务。这在无 IPC 中间文件模型中是不可能的。

回复 ,作者:syrrim(未验证)

我想这就是为什么 Linux 正在被一个几乎完成所有事情的单一进程接管的原因。Unix 哲学真是荡然无存。

我不确定您所说的“Linux 正在被一个几乎完成所有事情的单一进程接管”是什么意思。您能详细说明一下吗?

回复 ,作者:Jay Sanders(未验证)

他的意思是 systemd。

回复 ,作者:asgs(未验证)

Creative Commons 许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.