1. 程式人生 > >高可用服務層

高可用服務層

高可用架構

技術分享圖片

什麽是服務層

眾所周知,服務層主要用來處理網站業務邏輯的,是大型業務網站的核心。比如下面三個業務系統就是典型的服務層,提供基礎服務功能的聚合

  • 用戶中心:主要負責用戶註冊、登錄、獲取用戶用戶信息功能
  • 交易中心:主要包括正向訂單生成、逆向訂單、查詢、金額計算等功能
  • 支付中心:主要包括訂單支付、收銀臺、對賬等功能

技術分享圖片

整體架構

業務發展初期主要以業務為導向,一般采用 「ALL IN ONE」的架構方式來開發產品,這個階段用一句話概括就是 「糙猛快」。當發展起來之後就會遇到下面這些問題

  • 文件大:一個代碼文件出現超過 2000 行以上
  • 耦合性嚴重:不相關業務都直接堆積在 Serivce 層中
  • 維護代價高:人員離職後,根本沒有人了解裏面的業務邏輯
  • 牽一發動全身:改動少量業務邏輯,需要重新把所有依賴包打包並發布

遇到這些問題,主要還是通過「拆」來解決

技術分享圖片

具體拆的方式,主要根據業務領域劃分單元,進行垂直拆分。拆分開來的好處很明顯,主要有以下這些:

  • 每個業務一個獨立的業務模塊
  • 業務間完全解耦
  • 業務間互不影響
  • 業務模塊獨立
  • 單獨開發、上線、運維
  • 效率高

無狀態設計

對於業務邏輯服務層,一般會設計成無狀態化的服務,無狀態化也就是服務模塊只處理業務邏輯,而無需關心業務請求的上下文信息。所以無狀態化的服務器之間是相互平等且獨立的。

只有服務變為無狀態的時候,故障轉移才會變的很輕松。通常故障轉移就是在某一個應用服務器不能服務用戶請求的時候,通過負責均衡的方式,轉移用戶請求到其他應用服務器上來進行業務邏輯處理

技術分享圖片

超時設置

一般網站服務都會有主調服務和被調服務之分。超時設置就是主調服務在調用被調服務的時候,設置一個超時等待時間 Timeout。主調服務發現超時後,就進入超時處理流程。

技術分享圖片

  1. 主調服務 A 調用被調服務 B 時,設置超時等待時間為 3 秒,可能由於 B 服務宕機、網絡情況不好或程序 BUG 之類,導致 B 服務不能及時響應 A 服務的調用。
  2. 此時 A 服務在等待 3 秒後,將觸發超時邏輯而不再關心 B 服務的回復情況。
  3. A 服務的超時邏輯可以依據情況而定,比如可以采取重試,對另一個對等的 B 服務去請求,或直接放棄結束這個請求調用。

超時設置的好處在於當某個服務不可用時,不至於整個系統發生雪崩反應。

異步調用

一般請求調用分為同步與異步兩種。同步請求就像打電話,需要實時響應,而異步請求就像發送郵件一樣,不需要馬上回復。

這兩種調用各有優劣,主要看面對哪種業務場景。比如在面對並發性能要求比較高的場景,異步調用就比同步調用有比較大的優勢,這就好比一個人不能同時打多個電話,但是可以發送很多郵件。

技術分享圖片

那我們什麽時候該采用異步調用?

其實主要看業務場景,如果業務允許延遲處理,那就采用異步的方式處理

那我們該怎麽實現異步調用呢?

通常采用隊列的方式來實現業務上的延遲處理,比如像訂單中心調用配送中心,這種場景下面,業務是能接受延遲處理的。

那消息隊列主要有哪些功能呢?

  • 異步處理 - 增加吞吐量
  • 削峰填谷 - 提高系統穩定性
  • 系統解耦 - 業務邊界隔離
  • 數據同步 - 最終一致性保證

那到底有多少種隊列呢?其實主要看處理業務的範圍大小

  • 應用內部 - 采用線程池,比如 Java ThreadPool 中 BlockingQueue 來做任務級別的緩沖與處理
  • 應用外部 - 比如 RabbitMQ 、ActiveMQ 就是做應用級別的隊列,方便進行業務邊界隔離與提高吞吐量

技術分享圖片

同時,技術上來講,消息隊列一般分為兩種模型:Pull VS Push

  • Pull 模型:消費者主動請求消息隊列,獲取隊列中的消息。
  • Push 模型:消息隊列主動推送消息到消費者

其中 Pull 模式可以控制消費速度,不必擔心自己處理不了消息,只需要維護隊列中偏移量 Offset。所以對於消費量有限並且推送到隊列的生產者不均勻的情況下,采用 Pull 模式比較合適。

Push 比較適合實時性要求比較高的情況,只要生產者消息發送到消息隊列中,隊列就會主動 Push 消息到消費者,不過這種模式對消費者的能力要求就提高很多,如果出現隊列給消費者推送一些不能處理的消息,消費者出現 Exception 情況下,就會再次入隊列,造成消費堵塞的情況。

不過互聯網業界比較成熟的隊列主要以采用 Pull 模式為主,像 Kafka、RabbitMQ(兩種方式都支持)、RocketMQ 等

冪等

什麽是冪等設計呢?

其實很簡單,就是一次請求和多個請求的作用是一樣的。用數學上的術語,即是 f(x) = f(f(x))。

那我們為什麽要做冪等性的設計呢?主要是因為現在的系統都是采用分布式的方式設計系統,在分布式系統中調用一般分為 3 個狀態:成功、失敗、超時。

如果調用是成功或者失敗都不要緊,因為狀態是明確和清晰,但是如果出現超時的情況,就不知道請求是成功還是失敗的。

技術分享圖片

如果出現這種情況,我們該怎麽辦呢?一般采取重試的操作,重新請求對應接口。如果請求接口是 Get 操作的話,那到還好,因為請求多次的效果是一樣的。但是如果是 Post 、Put 操作的話,就會造成數據不一致,甚至數據覆蓋等問題。

舉個例子:在支付收銀臺頁面進行支付的時候,因為網絡超時的問題導致支付失敗,這個時候我們都會再進行一次支付操作,但是當支付成功後,發現你的賬戶余額被減了 2 次,這個時候心裏肯定很不爽,心裏都要開始罵娘了...

造成這個問題的關鍵是:網絡超時後,不知道支付是什麽狀態?成功還是失敗呢?所以說冪等性設計是必須的,尤其在電商、金融、銀行等對數據要求比較高的行業中。

一般在這種場景下我們該怎麽解決呢?

  1. 請求方一般會生產一個唯一性 ID 標識,這個標識可以具有業務一樣,比如訂單號或者支付流水號,在發起請求時候帶上唯一性 ID。
  2. 接收者在收到請求後,第一步通過獲取唯一性 ID 來查詢接收端是否有對應的記錄,如果有的話,就直接將上次請求的結果返回,如果沒有的話,就進行操作,並在操作完成後記錄到對應的表裏

技術分享圖片

服務降級

服務降級主要解決資源不足和訪問量過大的問題,比如電商平臺在雙十一、618 等高峰時候采用部分服務不提供訪問,減少對系統的影響。

那降級的方式有哪些呢?

  • 延遲服務:比如春晚,微信發紅包就出現搶到紅包,但是賬號余額並沒有增加,要過幾天才能加上去。其實這是微信內部采用延遲服務的方式來保證服務的穩定,通過隊列實現記錄流水賬單
  • 功能降級:停止不重要的功能是非常有用的方式,把相對不重要的功能暫停掉,讓系統釋放更多的資源。比如關閉相關文章的推薦、用戶的評論功能等等,等高峰過去之後,在把服務恢復回來。
  • 降低數據一致性:在大促的時候,我們發現頁面上不顯示真實庫存的數據,只顯示到底有還是沒有庫存這兩種狀態。

技術分享圖片

剛剛說了降級的方式,那我們操作降級的時候有哪些註意點呢?

  1. 清晰定義降級級別: 比如出現吞吐量超過 X,單位時間內響應時間超過 Y 秒、失敗次數超過 Z 次等,這些閾值需要在準備的時候,通過壓測的方式來確定。
  2. 梳理業務級別:降級之前,首先需要確定哪些業務是必須有,哪些業務是可以有的,哪些業務是可有可無的。
  3. 降級開關:可以通過接入配置中心(比如攜程 Apollo、百度 Disconf )的方式直接後臺降級。但是如果公司沒有配置中心的話,可以封裝一個 API 接口來切分,不過該 API 接口要做成冪等的方式,同時需要做一些簡單的簽名,來保證其一定的安全性。

總結

總結一下今天分享的主要內容

  • 整體架構:根據業務屬性進行垂直拆分,減少項目依賴,單獨開發、上線、運維
  • 無狀態設計:應用服務中不能保存用戶狀態數據,如果有狀態就會出現難以擴容、單點等問題
  • 超時設置:當某個服務不可用時,不至於整個系統發生連鎖反應
  • 異步調用:同步調用改成異步調用,解決遠程調用故障或調用超時對系統的影響
  • 服務降級:犧牲非核心業務,保證核心業務的高可用

下期主要講高可用架構分布式緩存層,敬請期待。

高可用服務層