系統服務解耦重設計系統邊際開始
今天聊一下有關「系統邊際」的概念,我們都知道系統彼此之前的耦合性要越低越好,系統邊際就是用來說明系統相互劃分的一種模式。如果開發是小型系統,系統高耦合可能還不會有太大的感覺,一旦遇到複雜且大型的系統,這種模組之間的相依性關係將會變地非常恐怖,一旦變成牽一髮而動全身,整個系統將會很難維護與進行修改,更不要說是功能正常的新增了。當技術債累積到一定的程度,會讓整個開發與營運效率變得低落,漸漸喪失靈活度,系統就容易隨著時間被市場淘汰。
新增功能其實不難,但舊功能要會動就比較難!
因此,在實作大型且業務邏輯複雜的系統,解耦工作就變得相當重要。在廣義的設計模式上可以想像建立「系統邊際」也就是介面,這個概念就是你設計一個東西,然後可以不相依其他東西即可以運作,像是一台裝電池的計算機,需要計算的人就立即可以使用,你不需要在乎這台計算機是什麼牌子,只要有數字鍵有加減乘除就行了,這個通用概念就是「介面」。其中還有一種概念就是就是單向相依模式,像是烤麵包機有著一條電線,插上插座就可以使用,因此烤麵包機的插頭相依了「插座」,我們不一定要買電力公司出品的烤麵包機才可以烤麵包,使用的時候只要相依有實作插座的介面即可,這也是一種解耦的概念。
解耦在物件導向程式語言中十分常見,但我們今天要談的是在雲服務中實現「解耦」,算是系統與系統之前的關係。舉例來說,一般與網路相關的應用程式,都會分為前台 (Frontend) 與後台 (Backend),以典型的 Client/Server 架構來看,後台通常扮演著 API 服務,前台可以是 Web, App, Daemon, Agent 等等實作。目前多數雲服務的 Backend 都是使用 HTTP 協定以 Web Service 實現,我們今天來介紹一些後端的解耦設計。
找出「系統邊際」拆解服務,透過「微服務」放大吞吐
目前很多典型的架構,其實已經建立了一些很基礎的系統邊際,比如 Web Server + API Service + Database 等最常見的架構,上述這些元件都是彼此分離的,您可以把這些服務安裝在不同的伺服器中,一樣可以順利運作。那麼我們實際想要解決的問題是什麼呢?雲服務系統時常需要可以動態擴容,這時候我們就需要使用一些技巧來達成,好讓這些服務可以快速複製在許多機器中一起工作。
我們想想,當一個系統執行時同時提供了很多種的功能服務,若我們將每一個功能、業務邏輯拆解成許多小型的服務,好方便佈署、擴容與遷移,而且當某個服務出現問題的時候,不致於影響到全部的功能造成毀面性的服務中斷,這樣的系統拆解與隔離措施,這就是現在常提到的微服務架構 (Micro Service)。微服務比起傳統的單體式 (Monolith) 架構,有著更高的彈性、擴充與容錯能力。要實現這樣的架構,首先要將系統拆解為相互隔離的功能,在實作上需要透過 Stateless 技巧來達成目的。
Stateless 無狀態設計
我們用 Web Serveice 來說明 Stateless 的實踐工作。原本 Web 的架構是有狀態的,每一次送出 non-safety (非 GET, HEAD) 的 HTTP Request 都會改變 Resource 的狀態,這也是 HTTP 最基礎的 REST 設計。但這不代表我們無法將 Service 設計成 Stateless (無狀態) 模式,假設我們有很多不同的主機都提供了一樣的 Web Service,以 Web Server 最常使用的 Cookie 機制,這些 Cookie 分派資料如果是依附在當下執行的主機,這樣就是 Stateful 設計,一旦 Browser 重新發送的 Request 被派到不同的機器,就會造成 Cookie 無法被識別。比較好的作法是將 Cookie (也就是狀態) 另外儲存到一個共用空間,例如 Database (MySQL, Redis 都可以) 上,好讓所有的服務在執行 Resuest 時不需要依賴前一個 Request 的狀態,這就是 Stateless 的一種實現。
一旦所有服務改成 Stateless 設計,我們就不需要關心那一個單元負責處理,只要運算能力足夠,就可以隨意擴充運算。這樣的架構會比較符合 SaaS 雲服務隨插即用、以量計價的特性。在實際的為服務實做中,目前最好的作法就是 Container 設計,將所有的服務封裝為 Container 在管理與擴充就會方便許多,也因此造就了 DevOps 的精神。
使用 L7 Switch 或 HTTP Proxy 雖然可以實現判斷 Cookie 送到同一台機器,但是這種解法無法實現動態擴容,不屬於 Stateless 的範疇。