<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>