將此篇文章跟 Facebook 上的朋友分享將此篇文章跟 Plurk 上的朋友分享將此篇文章跟 Twitter 上的朋友分享列印轉寄
2019/4/15

審視各主流架構轉捩點 看盡資訊大廠興衰以史鑑今

走入軟體架構演進史 見證微服務發展今昔

鄭淳尹
微服務(Microservice)是一種受到服務導向架構(Service-oriented Architecture,SOA)所啟發的架構風格,這幾年隨著容器興起而開始流行,本文主要是寫給剛接觸資訊領域的從業人員,同時提供圍繞此相關主題的個人觀點。另外,還研究了一些實務上問題,並提出部分可能的解決方案。


筆者在研究所軟工實驗室畢業之後,任職過金融業、醫療業和網購業,接觸並撰寫過IBM Mainframe大型主機COBOL程式、PL/I程式,Client-Server架構的Lotus Notes,3-tier架構的Web應用程式,到RESTful風格的Web API架構,及後端拆解成多個虛擬機或容器的微服務架構。

在這十幾年的過程中,深感資訊人員必須隨著不同的系統架構,學習對應的軟體開發工具和開發方法,當中涉及到的人員能力(人力)、硬體設備(物)、技術成熟度(時機)和公司文化生態(地點)皆必須到位,才有可能成功轉換或導入新軟體架構(事件),因而觸發撰寫此一系列軟體架構演進史的念頭,希望藉由個人過往的經驗,讓更多資訊人員去認識電腦史,以歷史哲學(https://zh.wikipedia.org/wiki/歷史哲學)的態度和方法,回頭審視各主流架構的轉捩點如何導致資訊大廠的興衰,進而以史鑑今。

微服務架構演進史(第一部分)

微服務是一種受到服務導向架構(Service-oriented Architecture,SOA)所啟發的架構風格,這幾年隨著容器興起而開始流行起來,在介紹這領域當前最新技術之前,先回顧軟體架構的歷史,進而導致了物件和服務漸增之原因,以及後來的微服務。最後,將介紹未解決的問題和未來的挑戰。

一開始,用於開發後端伺服器應用程式(如Java,C/C++和Python)的主流語言,提供了抽象化,以便將複雜的程式分解成模組。然而,這類語言是針對建立單一可執行系統(亦稱為單體,monoliths)所設計的,它們的抽象化模組倚賴著共用同一台機器的記憶體、檔案、資料庫等資源,由於多個模組每一個皆依賴著相關的共享資源,因此它們無法獨立執行。

定義1:單體式(monolith)

單體式是一種軟體應用程式的風格,其模組不能單獨執行,最經典的便是IBM Mainframe大型主機,使用特定的作業系統ZOS、程式和函式庫,只能執行在IBM Z系列硬體,這使得單體架構很難在沒有特定框架或特殊解決方案的分散式系統中使用。常見分散式方法如Network Objects、Java RMI或OMG CORBA,但即便如此,這類方式仍舊受到單體式中常見的問題所困擾和影響;下面列出最常出現的問題,標記為issue I:

I1 由於其複雜性,大規模的單體架構難以開發及維護,追踪錯誤Bugs需要透過長時間詳細閱讀程式碼庫。

I2 單體架構還會遭遇到「依賴地獄(Dependency Hell)」或「陣列地獄(Matrix Hell)」(2013年Docker在行銷宣傳時便使用此一口號),維護期間常因新增或更新程式庫導致不一致的系統無法編譯和執行,甚至更糟糕的是,營運環境中無法預料到的運行不一致現象。

I3 單體架構上每一項模組中的任何變更修改都需要重新啟動整個應用系統,對於大型專案,重新啟動通常需要相當長的停機時間,這會阻礙專案的開發、測試和維護。

I4 由於必須面對模組整併成之系統,使用者需求在資源面上所造成的衝突,單體式應用程式的部署通常就變成是次要的;有些系統可能是記憶體密集型,另一些是運算密集型的,而另一些需要特定相依元件(例如SQL關聯式數據庫),在選擇部署環境時,開發人員必須被迫接受,妥協於單一尺寸的所有配置,這對於各別單一模組而言,成本較昂貴,即便運行小模組,也因相依性問題必須使用高規格的硬體資源。

I5 單體式架構限制了可擴展性,為了處理漸漸增加的流入請求,常用策略是建立相同應用系統的新實例,並分攤負載到這些伺服器實例之間。然而,有可能的情況是,額外增加的流量僅需要模組的一部分而已,這使得當要為了其他模組分配新資源的情況時就會非常不方便。

I6 單體架構亦代表開發人員會被技術鎖定,開發人員被迫必須沿用原始應用程式的相同語言和框架。

微服務架構風格便是為了解決上述這些問題而發展出來,對於微服務的定義中,大多使用「內聚性」術語來表示需要實作哪些功能面關係密切的服務。

定義2:微服務(Microservice)

微服務是一支透過消息進行互動且具備內聚性的獨立程序,舉例來說,如果要將用於執行計算的服務,稱之為微服務,它應該要透過消息請求來提供可用的算術運算,但不應提供其他功能(主要為了鬆散耦合的緣故),如繪製和顯示等功能。

從技術角度來看,微服務應該是獨立的元件,概念上應該被隔離,並具備記憶體專用的持久性儲存工具(例如資料庫)。由於微服務架構的所有元件都是微服務,因此區別其微服務行為的方法是,透過元件之間的訊息組合與協調方式來決定。

定義3:微服務架構(Microservice Architecture)

微服務架構是一種分散式應用程式,其中所有模組都是微服務,舉個微服務架構的範例,假設想要提供一個繪製函數圖形的功能,且已存在著兩項微服務:計算器和顯示器。

第一項是前面所提到的計算器微服務,第二項是渲染和顯示圖片,為了達成服務拆分的目標,可以引入一項新的微服務,稱為「繪圖器」,它可協調計算器來運算圖形的形狀,並呼叫顯示器來渲染計算出的形狀,以圖1來解說這微服務架構的流程步驟。


▲圖1 繪圖微服務架構示意圖。


上述架構的開發人員可以各自單獨實作微服務的基礎功能、計算器和顯示器,可以使用繪圖器實現分散式應用程式的行為:

1. 使用者利用繪圖器提供的API功能。

2. 繪圖器與計算器相互溝通,以圖形函式功能計算需要呈現之圖案標記。

3. 要求顯示器顯示結果。

4. 傳回給使用者,為了說明微服務如何透過事先存在的微服務架構來達成橫向擴展,在圖1中繪出了計算器,協同呼叫兩項實作數學基礎和特殊功能的額外微服務(運算模組)。

微服務架構風格並不會偏好或禁止使用任何特定的撰寫程式範例,它只是提供了將分散式應用程式之元件劃分為各自獨立單元的指導原則,每一個單元只解決它所負責的問題,這意味著如果微服務藉由訊息傳遞提供其功能,便可如前文所提到的,在微服務內部可使用任何主流程式語言來實作其商業邏輯。

微服務架構原則上可以幫助專案經理和開發人員,它為分散式應用程式的設計和實作提供了完整指南。遵循此一原則,開發人員專注於實作和測試一些內聚性之功能,這也適用於更高層次的微服務,像涉及到協調其他微服務等功能。

總結一下整個敘述,微服務該如何應付上述所提到的單體式應用系統之問題(圖2),以下的項次,Sn是對應到In的解決方案(Solution)。


▲圖2 單體式架構與微服務架構之比較。


S1 微服務實作了有限的功能,這使得它們的程式碼庫很小,因此先天就限制了Bug的範圍。此外,由於微服務是獨立的,開發人員可以遵循系統隔離方法,在Testing或UAT環境,便可直接測試和檢查它們的功能面問題。

S2 規劃逐步轉換到新版本微服務便成為可能,新版本可以部署到舊版本旁,同時並存,只要符合既有API規格,相依之服務可於後續逐步修改,這助長了持續整合並大量地簡化了軟體維護工作。

S3 接續著上一項的優點,更改微服務架構的模組不需要完全重啟整個系統,重新啟動只會涉及到該模組的微服務,由於微服務的範圍很小,只需要很短的停機時間並在重新部署之後,程式開發人員便可以開發、測試和維護服務。

S4 因此微服務很自然地適用於「容器化」,且開發人員樂於在他們需要部署的環境中擁有高度自由的自主配置權力,進而可找出兼具成本和服務質量上的最適當設定。

S5 橫向擴展微服務架構,並不表示就是重複所有元件,開發人員可以更方便地部署和處理那些高負載相關的服務實例。

S6 唯一的限制是,運作微服務的網路所使用的通訊技術,如通訊媒體、通訊協定和通訊編碼等。除此之外,微服務不會產生額外的技術鎖定或限制,開發人員可以自由選擇最佳方案和資源(例如程式語言、框架等)來實作每一項微服務。

接下來的文章內容,將簡介分散式結構的演進,一直到它最近出現在微服務架構之中。然後,再以微服務架構之形式來詳述微服務能夠解決的問題及所提出的解決方案。後續文章則會詳細介紹目前用於發展微服務架構的解決方案,以及微服務如何影響軟體設計、開發、測試和維護的工作流程。最後一篇,將討論用來撰寫微服務架構的適合工具,以及過程中會面臨到的挑戰,若不具備這系列文章中談到的技能,就會像圖3的極客漫畫一樣,當然還有最後結論。


▲圖3 您真的準備好用微服務了嗎?(原出處:http://turnoff.us/geek/are-you-ready-for-microservices/)


微服務架構的過去

架構是一套系統,在其整個生命週期中,能不斷演進並承諾提供一定水準的服務。在軟體工程中,架構涉及到系統功能面與需求面,兩者取捨必須符合品質規格,亦是功能面與需求面之間的橋梁。

在過去幾十年,軟體架構經過徹底的研究,因而軟體工程師便提出了採用不同的方法來組合,以便提供廣泛的功能來滿足更廣泛需求的系統。接下來,探討在微服務出現之前,早期軟體架構工作上的輪廓。

早期軟體開發到物件導向設計模式(Design Pattern)

大約在1960年代左右,首次出現大規模軟體開發上的相關問題。1970年代,學術研究對軟體設計及其開發過程之影響引發了人們的極大關注和興趣,在此同時,軟體設計通常被當成與軟體實作本身無關的活動,因此須藉由一組特殊的符號與工具。到了1980年代,便將軟體設計完全整合到開發過程中,有助於將這兩者的部分活動合併,進而更難將設計和實作做出明確的區分。

軟體架構的概念亦開始出現在1980年代左右,然而,此領域的具體基礎是在1992年由Perry和Wolf(學習軟體架構的基礎知識,Foundations for the study of software architecture)的所建立,他們對軟體架構的定義與軟體設計卻是截然不同,同時比較土木架構與軟體架構本質上的差異,從那時候起,便逐漸出現了研究軟體架構的概念和實際應用的大型研究團隊,而這樣的概念得到了業界和學術界的廣泛採用。

對於此一領域研究的增長促使現有軟體架構模式(或通稱為樣式)的數量增加,因此需要針對樣式來分類,此問題在Garlan和Shaw中的「Software architecture: perspectives on an emerging discipline(軟體架構:新興學科的觀點)」一書中得到了解決方法,Bosch的研究(Software Architecture: The Next Step)提供了描述當前軟體工程和架構的研究現況,自從在1980年代出現之後,軟體架構已經發展成為一門成熟的學科,利用符號、工具和幾項技術,從單純的、偶然推敲預測的學術基礎研究領域來看,它已經轉變為對工業軟體建設攸關的重要元素。

從1980年代開始,特別是在1990年代,物件導向的出現及廣泛使用為軟體架構領域帶來了特殊貢獻,Erich Gamma等人(四人幫)的經典之作,「Design Patterns: Elements of Reusable Object-Oriented Software」(中文版:物件導向設計模式-可再利用物件導向軟體之要素,葉秉哲前輩之譯作),涵蓋了物件導向軟體的設計,以及如何將其轉換成程式碼,這些程式碼提供了一組稱為Patterns(模式)的可重用解決方案,這樣想法既不是全新的,也不是軟體工程所特有的,但這本書是第一個大規模彙整並推廣這樣的想法,在四人幫之前的年代,就已經有使用物件導向解決方案的模式,如物件導向程式所撰寫的架構設計模式經典範例便是Model-View-Controller(MVC,1978年),這是早期開發使用者圖形介面中最具原創性的一項洞見。

服務導向運算(Service-oriented Computing)誕生

近來聚焦於關注點分離(Separation of Concerns,SOC)所衍生出基於元件式的軟體工程,可以更容易控制軟體系統的設計、實作和演進發展,在過去的十年中,可看出自然而然地逐漸朝向服務優先和微服務的方向逐漸演變。

服務導向運算(SOC)是分散式計算和電子商務應用的新興典範,出自於物件導向和元件運算等技術。主要用來解決分散式系統的複雜性並可整合成各種不同的軟體應用程式,在服務導向運算中,一支程式(應該稱為服務)提供功能特性給其他元件,並透過消息傳遞來存取,服務是藉由實作將其介面來解耦(就是其他服務要如何存取此功能之定義)。

然後,最重要的是,特定的工作流程語言(例如WS-BPEL)可用來定義並描述,當中協調服務的複雜行為,這類語言基本理念來自並行理論(Concurrency Theory),這樣的氛圍促進了正規模型(Formal Models)的發展,甚至衍伸出模型檢查(Model Checking)的正規方法,以便更好去理解和驗證服務間的交互作用。

接著,說明服務導向的好處:

‧動態化:可啟動相同服務的新實例來分攤系統的負載。

‧模組化和可重用性:複雜服務由較簡單的服務所組成,不同的系統可以使用同樣的服務。

‧分散式開發:透過分散式系統的統一介面來達成一致性,不同的開發團隊亦可各自獨立同時開發。

‧異質系統和遺留系統的整合:服務只需要實作標準協議便可進行相互溝通。

第二代服務導向出現

在服務導向中使用到的元件化思維,其中一部分可追溯到物件導向程式設計(OOP)相關文獻;然而,有一些特殊的差異導致了截然不同的研究路線和社群。事實上,SOC的起源是(現在仍是)建構在OOP語言之上,最主要原因是OOP在2000年代廣為流傳並使用。然而,物件到服務的演變,首先聚焦在封裝和共用記憶體情況中隱藏的系統資訊,必須謹慎看待,而第二步是建立各自獨立的部署方式和訊息傳遞(從Slack到ChatOps)。因此,這是一種典範轉移,這兩項典範都具備元件化的共享理念,而下一步便是加入業務能力之概念,進而在這之上專注於分析和設計,以便在此一基礎上確認整個系統架構。

「第一代」的服務導向架構(SOA)定義出讓人敬而遠之且模糊不清的服務使用條件(例如服務發現和服務規範),阻礙了SOA模型的推廣和採用。微服務是SOA和SOC設計理念的第二次演進,目的是消除不必要且煩雜的前置定義部分(如SOAP、WSDL等),以便專注於有效撰寫實作出單一功能的簡單服務,就像物件導向一樣,微服務模型需要特定工具來支援開發人員,便可自然而然地引導並顯現出特定的設計模式,如Larisa Safina等人的Data-driven Workflows for Microservices研究。

首先,最重要是採用符合服務導向典範的語言。但是,大多數情況下,微服務架構仍然使用物件導向語言,如Java和JavaScript,或是函數語言(Functional Language),其他用於開發的支援工具也是必要的,例如測試案例工具、API設計工具等等。

認識微服務與物件導向設計觀念

系統架構源於設計模式的理念,最為人所熟知就是Bob大叔(Robert C. Martin)提出的五項物件導向設計原則(SOLID)闡述高內聚低耦合的設計理念:

‧單一職責原則(Single Responsibility Principle,SRP):認為物件應只具備一種單一功能的概念。

‧開閉原則(Open/Closed Principle,OCP):認為模組應該是對外開放可擴展,但是對內修改採封閉的概念。

‧里氏替換原則(Liskov Substitution Principle,LSP):認為程式中的物件應可在不改變功能正確性的前提下被它的子類別所替換,延伸出契約式設計開發和驗收的概念。

‧介面隔離原則(Interface Segregation Principle,ISP):針對不同需求的用戶只開放其對應所需的介面,避免無關的介面程式異動而造成其他用戶端異常,實務會將多用戶一起共用肥大臃腫的介面程式拆分成輕量各自獨立的介面。

‧依賴反轉原則(Dependency Inversion Principle,DIP):認為實作方法或功能應該遵循「依賴於抽象物件而非一個實例」之理念。

基於上述物件導向設計原則,Eric Evans提出了領域驅動設計(Domain Driven Design,DDD),業務邏輯拆分成核心領域(Core Domain)和領域邏輯(Domain Logic),並把設計重心放在限定上下文邊界(Bounded Context)的抽象模型上。微服務架構設計便是依循SOLID原則和DDD方法論,可參閱Sam Newman的Building Microservices(中文版:建構微服務-設計細微化的系統)有更深入的細節。

結語

本文所述為微服務架構發展的過往和原因,架構逐漸從單體式演進到低耦合的微服務設計,接下來將進入微服務架構的現況發展,以及軟體開發流程對架構和設計的影響,關於架構樣式的延伸閱讀,可參閱微軟的Azure Architecture Center中的架構樣式文件(https://docs.microsoft.com/zh-tw/azure/architecture/guide/architecture-styles/),而且歐萊禮(O'Reilly)最近發布了微服務成熟度狀況之調查報告(https://www.oreilly.com/programming/free/the-state-of-microservices-maturity.csp),已確定微服務成為主流系統架構,表示在微服務上的技術投資並不會成為泡沫,推薦微軟在edX線上學習網站上的Architecting Distributed Cloud Applications線上課程(https://courses.edx.org/courses/course-v1:Microsoft+DEVOPS200.9x+2T2018/course/),以這樣的軟體架構發展脈絡可得知,系統架構師比較適合由業務系統開發人員來擔任,且具備作業系統和網路基本知識,不斷重構系統程式,持續改進中間層效率,之後將有更深入的相關內容和實際案例,敬請期待下一期的微服務架構文章。

<本文作者:鄭淳尹,Docker/Moby.Taipei社群共同發起人,現為momo購物網資深工程師,曾任臺北榮民總醫院資訊工程師、玉山銀行資訊處專員、宏碁eDC維運工程師,系統維護及開發設計超過15年。開源技術愛好者,陸續在COSCUP開源人年會、Container Summit研討會、台灣微軟開發者大會、群益期貨和永豐金證券等分享資訊技術,並在多間大學資工系擔任Docker容器技術講師。現任微軟MVP,並翻譯審閱多本容器技術書籍。>

這篇文章讓你覺得滿意不滿意
送出
相關文章
拆解龐大核心系統 微服務仍保障既有投資
重視擴展互通 混合雲免虞鎖定
【網管人特刊】混合雲躍進轉型 公私混搭大步向前
跨雲容器傳輸 ADC新契機
授權模式貼近雲端服務 補強南北向負載平衡
留言
顯示暱稱:
留言內容:
送出