1984 年,Rob Pike 和 Brian W. Kernighan 在 AT&T 贝尔实验室技术期刊上发表了一篇名为“Unix 环境中的程序设计”的文章,他们在文章中以 BSD 的 cat -v 实现为例,论证了 Unix 哲学。简而言之,该哲学是:构建小型、专注的程序——无论使用何种语言——只做一件事,但要做好这件事,通过 stdin/stdout 进行通信,并通过管道连接。
听起来熟悉吗?
是的,我也这么认为。这与 James Lewis 和 Martin Fowler 提供的 微服务定义 非常相似
简而言之,微服务架构风格是将单个应用程序开发为一组小型服务的方案,每个服务都在自己的进程中运行,并使用轻量级机制(通常是 HTTP 资源 API)进行通信。
虽然一个 *nix 程序或一个微服务本身可能非常有限,甚至不是很有趣,但正是这些独立工作单元的组合揭示了它们的真正优势,因此也揭示了它们的力量。
*nix 与微服务对比
下表比较了 *nix 环境(如 cat 或 lsof)中的程序与微服务环境中的程序。
*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 的 SmartStack 或 Netflix 的 Eureka 等定制方法之外,通常还有基于环境变量或基于 DNS 的 方法,允许您动态发现服务。同样重要的是,OpenAPI 为 HTTP API 文档和设计提供了事实上的标准,而 gRPC 为更紧密耦合的高性能案例做了同样的事情。最后但并非最不重要的一点是,考虑到开发者体验 (DX),从编写良好的 Makefile 开始,到使用(或在?)style 编写文档结束。
优点和缺点
*nix 和微服务都提供了一些挑战和机遇
可组合性
设计出既具有清晰、鲜明的重点,又能与他人良好协作的东西是很困难的。在不同版本中将其做好并引入相应的错误处理能力就更难了。在微服务中,这可能意味着重试逻辑和超时——也许将这些功能外包到服务网格中是一个更好的选择?这很困难,但如果您做得对,它的可重用性将是巨大的。
可观察性
在单体应用(2018 年)或试图完成所有任务的大型程序(1984 年)中,当事情出错时,很容易找到罪魁祸首。但是,在一个
yes | tr \\n x | head -c 450m | grep n
或者在涉及例如 20 个服务的微服务设置中的请求路径中,您甚至如何开始弄清楚哪个服务行为不端?幸运的是,我们有标准,特别是 OpenCensus 和 OpenTracing。如果您正考虑迁移到微服务,可观察性仍然可能是最大的单一障碍。
全局状态
虽然对于 *nix 程序来说可能不是什么大问题,但在微服务中,全局状态仍然是一个讨论的问题。即,如何确保有效地管理本地(持久)状态,以及如何尽可能轻松地使全局状态保持一致。
总结
最后,问题仍然是:您是否为给定的任务使用了正确的工具?也就是说,就像专门的 *nix 程序实现一系列功能可能是某些用例或阶段的更好选择一样,单体应用 可能是您的组织或工作负载的最佳选择。无论如何,我希望本文能帮助您看到 Unix 哲学和微服务之间许多强烈的相似之处——也许我们可以从前者中学到一些东西来使后者受益。
5 条评论