高可用架構之高可用的應用和服務

後開啟撒打發了發表於2017-10-28

高可用的網站架構需要網站應用每個層面的支援,本文著重介紹應用層和服務層的高可用的解決方案。

1、高可用的應用

應用層主要處理網站應用的業務邏輯,因此有時也被稱作業務邏輯層,應用的一個顯著特點是應用的無狀態性

所謂無狀態的應用是指應用伺服器不儲存業務的上下文資訊,而僅根據每次請求提交的資料進行相應的業務邏輯處理,多個服務例項(伺服器)之間完全對等,請求提交到任意伺服器,處理結果都是完全一樣的。

1.1 通過負載均衡進行無狀態服務的失效轉移

不儲存狀態的應用給高可用的架構設計帶來了巨大便利,既然伺服器不儲存請求的狀態,那麼所有的伺服器完全對等,當任意一臺或多臺伺服器當機,請求提交給叢集中的其他任意一臺可用機器處理,這樣對終端使用者而言,請求總是能夠成功的,整個系統依然可用。對於應用伺服器叢集,實現這種伺服器可用狀態實時檢測、自動轉移失敗任務的機制就是負載均衡

負載均衡,顧名思義,主要使用在業務量和資料量較高的情況下,當單臺伺服器不足以承擔所有的負載壓力時,通過負載均衡手段,將流量和資料分攤到一個叢集組成的多臺伺服器上,以提高整體的負載處理能力。目前,不管開源免費的負載均衡軟體還是昂貴的負載均衡硬體,都提供失效轉移功能。在網站應用中,當叢集中的伺服器是無狀態對等時,負載均衡可以起到事實上高可用的作用。如下圖所示:

當 Web 伺服器叢集中的伺服器都可用時,負載均衡伺服器會把使用者傳送的訪問請求分發到任意一臺伺服器上進行處理,而當伺服器 10.0.0.1 當機時,負載均衡伺服器通過心跳檢測機制發現該伺服器失去響應,就會把它從伺服器列表中刪除,而將請求傳送到其他伺服器上,這些伺服器是完全一樣的,請求在任何一臺伺服器中處理都不會影響最終的結果。

由於負載均衡在應用層實際上起到了系統高可用的作用,因此即使某個應用訪問量非常少,只用一臺伺服器提供服務就綽綽有餘,但如果需要保證該服務高可用,也必須至少部署兩臺伺服器,使用負載均衡技術構建一個小型的叢集。

1.2 應用伺服器叢集的 Session 管理

應用伺服器的高可用架構設計主要基於服務無狀態這一特性,但是事實上,業務總是有狀態的。

  • 在交易類的電子商務網站,需要有購物車記錄使用者的購買資訊,使用者每次購買請求都是向購物車中增加商品;

  • 在社交類的網站中,需要記錄使用者的當前登入狀態,最新發布的訊息及好友狀態等,使用者每次算新頁面都需要更新這些資訊。

Web 應用中將這些多次請求修改使用的上下文物件稱作會話(Session),單機情況下,Session 可由部署在伺服器上的 Web 容器(如 JBoss)管理。在使用負載均衡的叢集環境中,由於負載均衡伺服器可能會將請求分發到叢集任何一臺應用伺服器上,所以保證每次請求依然能夠獲得正確的 Session 比單機時要複雜很多。

叢集環境下,Session 管理主要有以下幾種手段。

1.2.1 Session 複製

Session 複製是早期企業應用系統使用較多的一種伺服器叢集 Session 管理機制。應用伺服器開啟 Web 容器的 Session 複製功能,在叢集中的幾臺伺服器之間同步 Session 物件,使得每臺伺服器上都儲存所有使用者的 Session 資訊,這樣任何一臺機器當機都不會導致 Session 資料的丟失,而伺服器使用 Session 時,也只需要在本機獲取即可。如下圖所示:

這種方案雖然簡單,從本機讀取 Session 資訊也很快速,但只能使用在叢集規模比較小的情況下。當叢集規模較大時,叢集伺服器間需要大量的通訊進行 Session 複製,佔用伺服器和網路的大量資源,系統不堪重負。而且由於所有使用者的 Session 資訊在每臺伺服器上都有備份,在大量使用者訪問的情況下,甚至會出現伺服器記憶體不夠 Session 使用的情況。

而大型網站的核心應用叢集就是數千臺伺服器,同時線上使用者可達千萬,因此並不適用這種方案。

1.2.2 Session 繫結

Session 繫結可以利用負載均衡的源地址 Hash 演算法實現,負載均衡伺服器總是將來源於同一 IP 的請求分發到同一臺伺服器上(也可以根據 Cookie 資訊將同一個使用者的請求總是分發到同一臺伺服器上,當然這時負載均衡伺服器必須工作在 HTTP 協議層上。)這樣在整個會話期間,使用者所有的請求都在同一臺伺服器上處理,即 Session 繫結在某臺特定伺服器上,保證 Session 總能在這臺伺服器上獲取。這種方法也被稱作會話粘滯。如下圖所示:

但是 Session 繫結的方案顯然不符合我們對於系統高可用的需求,因為一旦某臺伺服器當機,那麼該機器上的 Session 也就不復存在了,使用者請求切換到其他機器後因為沒有 Session 而無法完成業務處理。因此雖然大部分負載均衡伺服器都提供源地址負載均衡演算法,但很少有網站利用這個演算法進行 Session 管理。

1.2.3 利用 Cookie 記錄 Session

早期的企業應用系統使用 C/S(客戶端/伺服器)架構,一種管理 Session 的方式是將 Session 記錄在客戶端,每次請求伺服器的時候,將 Session 放在請求中傳送給伺服器,伺服器處理完請求後再將修改過的 Session 響應給客戶端。

網站沒有客戶端,但是可以利用瀏覽器支援的 Cookie 記錄 Session,如下圖所示:

利用 Cookie 記錄 Session 也有一些缺點,比如:

  • 受 Cookie 大小限制,能記錄的資訊有限;

  • 每次請求響應都需要傳輸 Cookie ,影響效能;

  • 如果使用者關閉 Cookie ,訪問就會不正常。

但是由於 Cookie  的簡單易用,可用性高,支援應用伺服器的線性伸縮,而大部分應用需要記錄的 Session 資訊又比較小。因此事實上,許多網站都或多或少地使用 Cookie 記錄 Session。

1.2.4 Session 伺服器

那麼有沒有可用性高、伸縮性好、效能也不錯,對資訊大小又沒有限制的伺服器叢集 Session 管理方案呢?

答案就是 Session 伺服器。利用獨立部署的 Session 伺服器(叢集)統一管理 Session ,應用伺服器每次讀寫 Session 時,都訪問 Session 伺服器。如下圖所示:

這種解決方案事實上是將應用伺服器的狀態分離,分為無狀態的應用伺服器和有狀態的 Session 伺服器,然後針對這兩種伺服器的不同特性分別設計其架構。

對於有狀態的 Session 伺服器,一種比較簡單的方法是利用分散式快取、資料庫等,在這些產品的基礎上進行包裝,使其符合 Session 的儲存和訪問要求。如果業務場景對 Session 管理有比較高的要求,比如利用 Session 服務整合單點登入(SSO)、使用者服務等功能,則需要開發專門的 Session 服務管理平臺。

2、高可用的服務

可服用的服務模組為業務產品提供基礎公共服務,大型網站中這些服務通常都獨立分散式部署,被具體應用遠端呼叫。可複用的服務和應用一樣,也是無狀態的服務,因此可以使用類似負載均衡的失效轉移策略實現高可用的服務。

除此之外,具體實踐中,還有以下幾點高可用的服務策略。

2.1 分級管理

運維上將伺服器進行分級管理,核心應用和服務優先使用更好的硬體,在運維響應速度上也格外迅速。顯然,使用者及時付款比能不能評價商品更重要,所以訂單、支付比評價服務有更高優先順序。

同時在服務部署上也進行必要的隔離,避免故障的連鎖反應。低優先順序的服務通過啟動不同的執行緒或者部署在不同的虛擬機器上進行隔離,而高優先順序的服務則需要部署在不同的物理機上,核心服務和資料甚至需要部署在不同地域的資料中心。

2.2 超時設定

由於服務端當機、執行緒死鎖等原因,可能導致應用程式對服務端的呼叫失去響應,進而導致使用者請求長時間得不到響應,同時還佔用應用程式的資源,不利於及時將訪問請求轉移到正常的伺服器上。

在應用程式中設定服務呼叫的超時時間,一旦超時,通訊框架就丟擲異常,應用程式根據服務排程策略,可選擇繼續重試或將請求轉移到提供相同服務的其他伺服器上。

2.3 非同步呼叫

應用對服務的呼叫通過訊息佇列等非同步方式完成,避免一個服務失敗導致整個應用請求失敗的情況。如提交一個新使用者註冊請求,應用需要呼叫三個服務:將使用者資訊寫入資料庫,傳送賬號註冊成功郵件,開通對應許可權。如果採用同步服務呼叫,當郵件佇列阻塞不能傳送郵件時,會導致其他兩個服務也無法執行,最終導致使用者註冊失敗。

如果採用非同步呼叫的方式,應用程式將使用者註冊資訊傳送給訊息佇列伺服器後立即返回使用者註冊成功響應。而記錄使用者註冊資訊到資料庫、傳送使用者註冊成功郵件、呼叫使用者服務開通許可權這三個服務作為訊息的消費者任務,分別從訊息佇列獲取使用者註冊資訊非同步執行。即使郵件伺服器佇列阻塞,郵件不能成功傳送,也不會影響其他服務的執行,使用者註冊操作可順利完成,只是晚一點收到註冊成功的郵件而已。

當然不是所有服務呼叫都可以非同步呼叫,對於獲取使用者資訊這類呼叫,採用非同步方式會延長響應時間,得不償失。對於那些必須確認服務呼叫成功才能繼續下一步操作的應用也不適合使用非同步呼叫。

2.4 服務降級

在網站訪問高峰期,服務可能因為大量的併發呼叫而效能下降,嚴重時可能會導致服務當機。為了保證核心應用和功能的正常執行,需要對服務進行降級。降級有兩種手段:拒絕服務及關閉服務。

  • 拒絕服務

    決絕低優先順序應用的呼叫,減少服務呼叫併發數,確保核心應用正常使用;或者隨機拒絕部分請求呼叫,節約資源,讓另一部分請求得以成功,避免要死大家一起死的慘劇。

  • 關閉服務

    關閉部分不重要的服務,或者服務內部關閉不重要的功能,以節約系統開銷,為重要的服務和功能讓出資源。

2.5 冪等性設計

應用呼叫服務失敗後,會將呼叫請求重新傳送到其他伺服器,但是這個失敗可能是虛假的失敗。比如服務已經處理成功,但因為網路故障應用沒有收到響應,這是應用重新提交請求就導致服務重複呼叫,如果這個服務是一個轉賬操作,就會產生嚴重後果。

服務重複呼叫時無法避免的,應用層也不關心服務是否真的失敗,只要沒有收到呼叫成功的響應,就可以認為呼叫失敗,並重試服務呼叫。因此必須在服務層保證服務重複呼叫和呼叫一次產生的結果相同,即服務具有冪等性。

有些服務天然具有冪等性,比如將使用者性別設定為男性,不管設定多少次,結果都一樣。但是對於轉賬交易等操作,問題就會比較複雜,需要通過交易編號等資訊進行服務呼叫有效性校驗,只有有效的操作才能繼續執行。

相關文章