微服務:真正的架構模式

微服務:真正的架構模式

【編者的話】本文來自Medium,通過比較CRUD app和數據流app兩種應用類型的微服務化探索來向聽衆介紹微服務。

簡介

微服務的神祕和背後的知識令我着迷。微服務作爲概念,它屬於現代最有趣的架構之一。微服務應用廣泛,涉及不同的使用場景。但也有很多地方模糊不清,難以定論。

人們在討論微服務時,我會努力理解他們的真實意圖。儘管在上一次演講中我分享了對微服務的認識,但我很清楚其它公司和我們使用的架構是不一樣的。最近我詢問了一位同行,瞭解到他們部署微服務的方式(和我)不同。所以我打算通過比較這兩種方式向技術大會的觀衆介紹微服務。

這篇文章會舉兩個例子。第一個是我上次演講中的微服務例子,屬於困難模式。第二個例子以流式架構實現微服務,在我瞭解的爲服務使用場景中,它更接近微服務應用的“理想情況”。

微服務基礎

我認爲微服務作爲架構由以下因素演進而來:
  1. 21世紀初,大量創業公司使用一門語言像rails,來快速擴展他們的業務和團隊。遇到某些困難後,他們開始思考,可以用一門語言做哪些合理的事情。
  2. 雲計算的發展讓我們更便捷、簡單地獲得服務器實例運行軟件。
  3. 我們都認可使用分佈式系統架構,尤其是網絡調用來解決問題。

伸縮調整、易於獲取新硬件、分佈式系統與網絡,將這些因素結合起來,會發現它們在我提出的“microservice for CRUD”概念中作用巨大。如果你將一家公司拓展到將近成功的水平,但在技術/工程團隊拓展上遇到麻煩。將龐大單一的管理拆分成service-style的架構會有很大幫助。這是我的親身經歷。

微服務的要點看起來像這樣:
  • 服務支持獨立伸縮。如果系統服務的一部分有高負荷或相對其它服務有高容量的需求,你可以擴展服務來滿足需求。這在單一系統架構(譯者注:比如說Java Web中把代碼編譯成一個war包發佈)裏可行,但是有時也會變得很複雜。
  • 服務允許在可控的範圍內故障。在您系統中各個模塊可獨立操作的情況下,可以通過拆分成不同的服務來提高可用性。例如,一款商業app,如果產品的搜索流程崩潰,但是它仍然可以提供檢測服務,這將是一件好事。可是實際操作起來比理論要複雜得多,並且人們對於微服務有許多愚蠢的認識,比如暗示服務重疊沒有價值。通過設置實現可控的故障並不是必須的,但擁有它情況會好些,但讓單一架構做到這一點(設置獨立可控的故障域)並不容易。
  • 服務要能支持開發團隊人員獨立良好地工作在各自負責的模塊。再次聲明,這單一架構系統中你是可以做到的,因爲我就做到了。但整體上的挑戰(以及與monorepo(單個代碼倉庫)中服務相關的挑戰)是,當這些“域(譯者注:可理解爲範圍)”的概念以源代碼呈現時,工程師要努力去理解理論上與實際編碼上的區別。如果我能看到所有代碼,並且它們可以編譯在一起,就像是一個交付件(而不是各個服務單獨拆分構建),我更趨向把它作爲整體。這樣我就可以從這裏抓起代碼在另一個地方使用,從那裏獲取數據在這裏使用,等。

我還準備了一些筆記。人們經常弄混“Monolith”和“monorepo”。一個Monolith風格的應用是指把一組代碼編譯成單獨的服務交付件(過程中可能會產生額外的客戶端交付件)。你可以通過配置文件來讓交付件做幾乎任何你能想象的事情,包括上文所有的service-type,但是如果所有代碼沒有集中放到一個倉庫中,這樣做最後會生成很大的鏡像,也會導致混亂,因爲團隊有時會根據不同的構建工具和配置文件編譯生成不同的交付版本。我依然認爲這種架構是單一架構。

Monorepo或者是monolith repository是一種模型,它指一個單一的代碼倉庫,包含了你正在編碼的系統的所有代碼(很可能還不包括OSS/外部依賴的源代碼)。代碼倉庫中的代碼由多個獨立的應用交付件組成,這些代碼都可以獨立編譯/打包和測試,不需要整個倉庫的代碼。monorepo的一個優勢是,當修改了共享庫的版本後,依賴共享庫的開發人員可以更容易地開發,而不用去等其他團隊更新依賴庫的版本後再開發。monorepo模型最大的缺點是,支持它的OSS工具不多,因爲大部分OSS工具不是這麼構建的。所以需要大量投資開發新的工具來支持monorepo模型。

基於CRUD應用的微服務

在介紹CRUD應用由單一架構到微服務架構演進之前,我會先介紹構建中等規模CRUD平臺所需的架構設計。這種平臺有一個不錯的用例,它涉及“事務”和“元數據”。

事務:指用戶做了一個行爲,你想把它持久化,這裏數據的一致性最有價值。CRUD中的Create,Update,Delete操作頻率比Read低得多。

元數據:描述用戶的信息,但是隻能由內部內容創建者修改,外部用戶(比如審查者)很少能修改。通常具有很高的可緩存性。甚至有時能夠容忍一定程度的臨時不一致性(顯示陳舊的數據)。

CRUD依賴型公司還有哪些能做的事呢,尤其是分析領域?當然有,你可能需要根據用戶在瀏覽網站時的行爲以及其它個性化的操作來頻繁地調整結果。但是做到實時調整結果很困難,因爲你不一定總是能從用戶那裏得到足夠的數據來達到最好的效果,所以這(指上文調整結果)不是系統的一級關注點。

這種架構在遷移過程中是相對簡單的:
  • 確定獨立實體。這篇論文Life Beyond Txns有很多有趣又實用的定義。越早遷移架構越好,否則系統臃腫後不得不實現一個分佈式事務。你可能希望擁有企業產品,用戶等服務的數據,再集成其它面向企業的邏輯和聚合服務。
  • 從實體中抽象出邏輯,集成到服務實體中。儘可能不要去改變數據模型,並且通過重定向訪問新的服務實體APIs。

基本上就這些了。你需要做的是收集足夠的用戶函數、數據和術語,然後改成微服務架構並開發新的功能。

這些服務不是典型的SOA,也不是很小的微服務。擁有數據的服務會更復雜。你可能不想要太多服務,因爲你希望在滿足用戶請求的同時,不需要太多的網絡跳轉,甚至不需要分佈式事務(理想情況)。

可能你不需要每天都開發新的服務,特別是當你有一個五十人的工程團隊和一個長期的產品路線圖時,你可能不會爲了“點一下按鈕就動態添加一個新的功能”而投入大量的工程時間到開發編排和工具上。

決定投入多少時間在工具上很簡單:開發人員花費在自動添加新服務的時間VS實現和維護自動化程序的時間VS隨着時間的推移,你希望添加多少新的服務?比較下它們就可以了。顯然,如果你認爲讓人們能夠快速頻繁開發微小的服務很重要,你最好投入更多的時間和開發更多的工具。與所有工程優化決策一樣,這樣做不代表它完全正正確,但是在可預見的未來,它是正確的,而且我們還需要不斷地重新評估。

在這個實例中,我發現有許多是微服務必須具備的。比如上文中我提到過很多次的編排。但是如果你不需要自動啓動服務或頻繁地遷移服務,你則不需要動態服務發現(如果需要的話,負載均衡器是一個不錯的選擇)。

允許團隊爲每個服務選擇合適的語言、框架和數據存儲也不是必須的。事實上,對於你的團隊這樣做可能是一個令人頭疼的問題而不是福音。

爲每個微服務創建單獨的數據存儲。
不要讓所有微服務都使用同一個後端存儲服務。因爲使用單一的數據存儲,不同團隊編寫的微服務就可以容易地共享數據庫結構,也會減少許多重複的工作。但是,如果某個團隊更新了數據庫結構,使用該結構的其他服務也需要調整。
這是真實存在的,但是對於較小的團隊,可以通過事先約定好規則來禁止共享數據庫結構(如果還有顧慮,可以通過代碼審查、自動測試和檢測來預防)。如果你很仔細地定義數據相關的服務,不太可能發生這樣的事情。另一種方法在下一段介紹:

將數據分離開,可能使數據管理更加複雜,因爲分離的存儲系統更容易脫離同步或變得不一致,外鍵也可能被意外更改。這時你就需要增加工具來執行主數據管理(MDM):後臺檢測然後修復數據不一致性。例如,它可以檢測每個數據庫的用戶id,來驗證所有數據庫中的id是否一致(任何數據庫中沒有重複或多餘的id)。你可以自己寫工具或者買一個。許多商業版的關係型數據庫管理系統(RDBMS)會執行這些類型檢查,但是它們耦合性很高導致無法伸縮(出處)。
上面這段可能令很多經驗豐富的數據檢測工程師失望(譯者注:因爲商業檢測服務不支持伸縮,言下之意是要自己開發)。 正是由於數據檢測的開銷,我鼓勵小型組織,在決定使用完全獨立的數據存儲或個人存儲之前,要評估約定數據庫共享的方法。決定最終使用何種數據存儲服務是可以根據需要來推遲的。

這一版本的微服務架構在擴展CRUD應用方面令人期待,因爲這種架構允許你分塊重寫代碼。你可以重寫整個系統,或者簡單地修改對縮放最敏感的部分。你需要主動參與到涉及到分佈式系統複雜性相關問題上,仔細思索數據和基於數據的事務。可能你不需要大量的數據管道,就知道哪裏的數據需要修改。

我一定要用微服務來擴展這些嗎?答案很可能是no,但是並不意味着使用微服務來擴展系統是一個壞的作法。但是使用極端的微服務模型可能是一個壞主意,因爲你很可能不想以分佈式事務的方式來切分你的數據。

基於數據處理流的微服務

現在我們討論一個非常不同的使用案例。這個例子不是你們熟悉的CRUD應用,也不包含臃腫的企業規則和事務更新。相反,這個案例包含大量的數據流。它從不同的數據源接入許多小數據,小數據又匯聚成大量數據。不僅有大量的數據,還有大量的服務來消費、修改數據,或者存儲起來以便進一步使用。

主要關注點是接收大量不斷變化的數據,以各種方式處理它,並以合適的視圖展示給客戶。而這些在CRUD應用中是次要的。

我們以一個聚合度量(Metrics)信息的SaaS應用爲例。這款應用的客戶遍及世界,各種應用、服務、機器都將度量信息發送到聚合器。這些客戶只需要查看他們的數據,雖然任何一個客戶的數據總數可能非常大。我們的聚合器需要處理這些度量信息,並將它們發送到負責顯示給客戶的程序。面向客戶的應用能夠操作實時輸入的數據以及來自緩存或者後端存儲系統的歷史數據。數據的最大價值在於數據在移動窗口內現在/最近都發生了什麼。

這種架構從一開始就要考慮數據容量問題,而這在CRUD世界裏可能很長時間都不用考慮。另外,數據本身是隨着時間不斷更新的。“有狀態”的數據在事務上最小化更新,最有用的數據是時間序列或者事件日誌。事務性的數據,比如存儲用戶視圖、配置等,它們類似CRUD中的“元數據”,與流數據相比很少改變。開發者的時間大部分用於管理輸入流、提供新的輸入類型、對輸入流進行計算或者改變計算方式,而不是處理事務性數據的變更(CRUD)。

本例中,你可以假設一個服務,想要通過對數據流上的特定元素執行不同的計算來進行實驗。不修改現有的代碼,實驗性服務選擇一個時間點開始監聽數據流,執行新的計算得到新的結果,然後將結果推送回數據流的不同信道上。某一時刻,實驗程序從實驗客戶那提取數據,然後將實驗計算結果而不是標準結果返回給客戶。你需要記錄所有步驟,用於實驗完整與調試分析,不要求記錄非常詳細或者是系統事件甚至用戶相關的事務性信息。

本例中,爲了更快運行實驗程序,編寫新的代碼可能比更改原有的服務代碼更簡單。特別是新開發的服務不需要擔心調整數據消耗或者與現存服務的計算結果衝突。這就是我所說的“以數據流爲中心的微服務”。

如果管理實時數據流給你的企業帶來巨大的價值,並且你有非常多的開發者,要創建新的服務來消耗數據流、檢測數據和產生結果,你肯定願意投資開發新工具來儘可能簡化服務創建和生產環境部署。你會慢慢把工具應用到所有服務上。但是你也要清晰地認識到,擁有可獨立操作和實驗的動態數據是微服務化的關鍵。

Cron job作爲微服務

如果沒有提到這一點將是我的疏忽。當一切微服務化非常容易時,自然也包括傳統的cron job微服務化。

cron job是很好的概念,它沒有把一切都視作“服務”。你可以用AWS的CloudWatch Events或者是調度Lambda函數實現這個目的(將cron job微服務化)。也可以使用Gearman調度cron job,它是一個支持隊列和異步作業的運行程序。注意你的cron job必須冪等(同一個輸入運行兩次結果不變)。如果你有更簡單的方法運行服務或者是基礎的cron job,那沒問題(你不需要微服務化)。

結論

我希望這篇文章能在微服務世界中帶來多維度的突破。不斷思考和嘗試對我個人非常重要。它幫助我以極端的角度去理解人們習慣的事情。我指的是把大量時間用於流數據處理與把時間用在CRUD應用彈性伸縮這兩個角度。

原文連接:Microservices: Real Architectural Patterns(翻譯:adolphlwq)

=========================================
譯者介紹
adolphlwq,LinkerNetworks南京分公司軟件工程師,2016年7月畢業於南京信息工程大學。大學時期是校技術社團多火工作室核心成員,博客地址:QuanTalk
發佈了1 篇原創文章 · 獲贊 29 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章