微服務化之無狀態化與容器化
此文已由作者劉超授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
一、為什麼要做無狀態化和容器化
很多應用拆分成微服務,是為了承載高併發,往往一個程序扛不住這麼大的量,因而需要拆分成多組程序,每組程序承載特定的工作,根據併發的壓力用多個副本公共承擔流量。
將一個程序變成多組程序,每組程序多個副本,需要程式的修改支撐這種分散式的架構,如果架構不支援,僅僅在資源層建立多個副本是解決不了問題的。
很多人說,支撐雙十一是靠堆機器,誰不會?真正經歷過的會覺得,能夠靠堆機器堆出來的,都不是問題,怕的是機器堆上去了,因為架構的問題,併發量仍然上不去。
阻礙單體架構變為分散式架構的關鍵點就在於狀態的處理。如果狀態全部儲存在本地,無論是本地的記憶體,還是本地的硬碟,都會給架構的橫向擴充套件帶來瓶頸。
狀態分為分發,處理,儲存幾個過程,如果對於一個使用者的所有的資訊都儲存在一個程序中,則從分發階段,就必須將這個使用者分發到這個程序,否則無法對這個使用者進行處理,然而當一個程序壓力很大的時候,根本無法擴容,新啟動的程序根本無法處理那些儲存在原來程序的使用者的資料,不能分擔壓力。
所以要講整個架構分成兩個部分,無狀態部分和有狀態部分,而業務邏輯的部分往往作為無狀態的部分,而將狀態儲存在有狀態的中介軟體中,如快取,資料庫,物件儲存,大資料平臺,訊息佇列等。
這樣無狀態的部分可以很容易的橫向擴充套件,在使用者分發的時候,可以很容易分發到新的程序進行處理,而狀態儲存到後端。而後端的中介軟體是有狀態的,這些中介軟體設計之初,就考慮了擴容的時候,狀態的遷移,複製,同步等機制,不用業務層關心。
如圖所示,將架構分為兩層,無狀態和有狀態。
容器和微服務是雙胞胎,因為微服務會將單體應用拆分成很多小的應用,因而運維和持續整合會工作量變大,而容器技術能很好的解決這個問題。然而在微服務化之前,建議先進行容器化,在容器化之前,建議先無狀態化,當整個流程容器化了,以後的微服務拆分才會水到渠成。
二、無狀態化的幾個要點
前面說對於任何狀態,需要考慮它的分發,處理,儲存。
對於資料的儲存,主要包含幾類資料:
-
會話資料等,主要儲存在記憶體中。
-
結構化資料,主要是業務邏輯相關
-
檔案圖片資料,比較大,往往通過CDN下發
-
非結構化資料,例如文字,評論等
如果這些資料都儲存在本地,和業務邏輯耦合在一起,就需要在資料分發的時候,將同一個使用者分到同一個程序,這樣就會影響架構的橫向擴充套件。
對於儲存在記憶體裡的資料,例如Session,可以放在外部統一的快取中。
對於業務相關的資料,則應該儲存在統一的資料庫中,如果效能扛不住,可以進行讀寫分離,如文章微服務化的資料庫設計與讀寫分離
如果效能還是抗住不,則可以使用分散式資料庫。
對於檔案,照片之類的資料,應該存放在統一的物件儲存裡面,通過CDN進行預載入,如文章微服務的接入層設計與動靜資源隔離
對於非結構化資料,可以存在在統一的搜尋引擎裡面,例如ElasticSearch。
如果所有的資料都放在外部的統一儲存上,則應用就成了僅僅包含業務邏輯的無狀態應用,可以進行平滑的橫向擴充套件。
而所有的外部統一儲存,無論是快取,資料庫,物件儲存,搜尋引擎,都有自身的分散式橫向擴充套件機制。
在實行了無狀態化之後,就可以將有狀態的叢集集中到一起,進行跨機房的部署,實現跨機房的高可用性。而無狀態的部分可以通過Dubbo自動發現,當程序掛掉的時候,自動重啟,自動修復,也可以進行多機房的部署。
三、冪等的介面設計
但是還有一個遺留的問題,就是已經分發,正在處理,但是尚未儲存的資料,肯定會在記憶體中有一些,在程序重啟的時候,資料還是會丟一些的,那這部分資料怎麼辦呢?
這部分就需要通過重試進行解決,當本次呼叫過程中失敗之後,前序的程序會進行重試,例如Dubbo就有重試機制。既然重試,就需要介面是冪等的,也即同一次交易,呼叫兩次轉賬1元,不能最終轉走2元。
介面分為查詢,插入,更新,刪除等操作。
對於查詢介面來講,本身就是冪等的,不用做特殊的判斷。
對於插入介面來講,如果每一個數據都有唯一的主鍵,也能保證插入的唯一性,一旦不唯一,則會報錯。
對於更新操作來講,則比較複雜,分幾種情況。
一種情況是同一個介面,前後呼叫多次的冪等性。另一種情況是同一個介面,併發環境下呼叫多次的正確性。
為了保持冪等性,往往要有一個冪等表,通過傳入冪等引數匹配冪等表中ID的方式,保證每個操作只被執行一次,而且在實行最終一致性的時候,可以通過不斷重試,保證最終介面呼叫的成功。
對於併發條件下,誰先呼叫,誰後呼叫,需要通過分散式鎖如Redis,Zookeeper等來實現同一個時刻只有一個請求被執行,如何保證多次執行結果仍然一致呢?則往往需要通過狀態機,每個狀態只流轉一次。還有就是樂觀鎖,也即分散式的CAS操作,將狀態的判斷、更新整合在一條語句中,可以保證狀態流轉的原子性。樂觀鎖並不保證更新一定成功,需要有對應的機制來應對更新失敗。
四、容器的技術原理
無狀態化之後,實行容器化就十分順暢了,容器的不可改變基礎設施,以及容器基於容器平臺的掛掉自動重啟,自動修復,都因為無狀態順暢無比。
關鍵技術一:Dockerfile
例如下面的Dockerfile。
為什麼一定要用Dockerfile,而不建議通過儲存映象的方式來生成映象呢?
這樣才能實現環境配置和環境部署程式碼化 ,將Dockerfile維護在Git裡面,有版本控制,並且通過自動化的build的過程來生成映象,而映象中就是環境的配置和環境的部署,要修改環境應先通過Git上面修改Dockerfile的方式進行,這就是IaC。
關鍵技術二:容器映象
通過Dockerfile可以生成容器映象,容器的映象是分層儲存,對於Dockerfile中的每一個語句,生成一層容器映象,如此疊加,每一層都有UUID。
容器映象可以打一個版本號,放入統一的映象倉庫。
關鍵技術三:容器執行時
容器執行時,是將容器映象之上加一層可寫入層,為容器執行時所看到的檔案系統。
容器執行時使用了兩種隔離的技術。
一種是看起來是隔離的技術,稱為namespace,也即每個namespace中的應用看到的是不同的IP地址、使用者空間、程號等。
另一種是用起來是隔離的技術,稱為cgroup,也即明明整臺機器有很多的CPU、記憶體,而一個應用只能用其中的一部分。
cgroup
網易雲端計算基礎服務深度整合了 IaaS、PaaS 及容器技術,提供彈性計算、DevOps 工具鏈及微服務基礎設施等服務,幫助企業解決 IT、架構及運維等問題,使企業更聚焦於業務,是新一代的雲端計算平臺,點選可免費試用。