前言
微服務從大規模使用到現在已經有很多年了,從之前的探索到一步步的不斷完善與成熟,微服務已經成為眾多架構選擇中所必須面對的一個選項。服務註冊與發現是相輔相成的,所以一般會合起來思索。其依託元件有很多,比如Zookeeper,Consul,Eureka等等。
本文,我們將探討服務註冊和發現的概念及其使用機制,以使得微服務能夠在不知道其確切位置(通常是URL)的情況下消費其他服務。由於本文主要是個人實踐的一些總結,總會有不足之處,也希望各位看官幫忙完善。本系列前一篇文章請移步總述
服務註冊與發現探討
為什麼需要
服務是在具有單獨部署週期的不同計算機上執行的單個或較小的程式碼庫,可明確解決相關的問題域。不正確的服務設計可能導致重覆的服務建立,同時也無法進行架構層面的復用。
如果沒有服務註冊與服務發現,那麼服務的位置將會耦合到消費者服務裡面,最終將會導致整個系統的架構變得死板而又難以維護。事實上,在比較簡單的系統架構裡,採用靜態配置的方式是非常簡單而又效果顯著的,畢竟所有服務都在同一位置,而且很少發生變更。
而在微服務裡,其標的之一就是使系統能夠獨立開發、部署及升級擴充套件,隨著系統的進一步複雜,服務位置也發生變更,那麼靜態配置就會變得十分雞肋,此時我們需要變更我們的策略,那就是在消費其他服務的時候採用靜態配置。那麼核心問題就來了,服務是如何發現它們索要消費的服務的IP地址和埠號的?既是動態配置,在多實體場景下,配置在專門的系統裡是最好的選擇,ZK和Consul等也就應運而生。
簡單的通訊流程
我們來看一下一個簡單的服務通訊流程
透過這張圖,我們可以知道,服務呼叫時,無需知道標的服務的真實地址,只需要知道服務Key,然後到服務發現系統裡獲取對應的地址即可。
這張圖雖然比較簡單,但是傳遞的資訊卻有很多:
服務如何確定自身的IP地址及埠?
在消費方拿到被消費方的地址以後,應採用何種方式呼叫?
我們如何新增新服務並刪除已棄用的服務?
如果服務在執行過程中出現問題,如何快速發現並提前通知?
作為集中化管理的服務註冊與發現中心掛了,咋整?
服務資訊註冊
一般可以建立服務登錄檔,該服務登錄檔是服務及其實體及其位置的資料庫。服務實體在啟動時註冊到服務登錄檔,併在關閉時登出。客戶端查詢服務登錄檔以查詢服務的可用實體。服務登錄檔可能會呼叫服務實體的執行狀況檢查API來驗證它是否能夠處理請求。我們已經有了現成的工具,就是Consul等,那麼我們的關註點就是服務地址的註冊了。
最簡單的解決方案就是手動配置。我們思考一個問題,如果我們需要對該服務進行水平擴充套件,再增加一個實體的時候,仍然需要我們手動配置,但是我們已經不想手動配置了,在DevOps時代,再使用手動配置,就顯得不那麼專業了,而且人工的介入,也增加了系統運維的成本與風險。
所以我們選擇,自動獲取本機IP地址,但是這裡有一個問題,也是我在實際運用中遇到的問題就是本地會有多個網絡卡的情況,這是比較麻煩的,所以那時候我建議每臺機器只配置一個網絡卡,以減少不確定性,但是後來,遇到一個新的問題就是,當我想在伺服器上安裝代理,以獲取請求包資訊的時候,容易改變系統的執行環境,依然存在著不確定性。後來我在網上檢視是否其他方案的時候,始終沒有一個比較好的解決方案,後來有人說,直接往服務註冊中心傳送一個Socket連線就可以了,透過Socket實體獲取本機IP,網上也有人介紹這種方案。
至於埠獲取,相對簡單一點,我們使用的就是直接配置在服務裡。
服務註冊本身就是要在有限人力幹預的情況下,支援不同應用之間的執行與互動。
服務註冊擴充套件至之其他資訊的註冊
這個地方更多的是考慮服務消費方的負載均衡策略、呼叫策略以及容錯的可配置性。
在被消費方做了叢集部署的時候,這就是要求我們根據實際執行情況及時調整對服務的呼叫,那麼該如何判斷呢?在實現上可以考慮權重引數,該引數的設定應該來源於自身以及服務治理系統。
來源於自身,是因為我們的服務可能部署在相對較差的機器上或者該服務本身只是一個備用系統,不應該承載過大的流量。
來源於服務治理系統,是因為在實際執行中,可能該系統已經出現問題,或者需要暫時下線,我們可以設定權重繫數為0,以從消費服務本身來停止對該服務的呼叫。
權重引數只是其中一個,也是最容易想到和實現的一個,當然還有其他引數,只要是為了更好的最佳化服務間的呼叫,那麼就可以註冊進去,同時實現相應的呼叫策略。比如版本號,TTL等等。
一旦資訊多了,就需要考慮如何及時獲取變更,以調整呼叫策略。
服務發現
服務發現可以分為客戶端發現或伺服器端發現來確定要向其傳送請求的服務實體的位置。客戶端發現比較簡單一些,直接向服務發現中心獲取所需的被消費者服務的資訊。而服務端發現相對來說,比較複雜,需要建立一個路由中心,由路由中心去發現被消費者的請求。我們這邊用的比較多的是客戶端發現。
確保發現的穩定性,首先就要確保服務註冊中心的穩定性,其地址不應該頻繁發生變化,所以我們可以在服務中配置我們的服務註冊中的地址。此處需要多說明一下,就是作為服務註冊中心繫統,一定要保持高可用性,可以透過叢集以及負載均衡來增強可用性。
服務實體的數量及其位置是動態變化的。通常為虛擬機器和容器分配動態IP地址。最複雜的問題自然是如何及時獲得被消費者服務變化後的地址?我們可以建立一個路由功能,使用者可以客戶端查詢服務註冊中心以查詢服務的可用實體。服務註冊中心可能會呼叫服務實體的執行狀況檢查API來驗證它是否能夠處理請求。
我們在系統啟動的時候,會做個Reload,以載入最新資訊。那麼之後如何及時獲取最新資訊呢?通常情況下有兩種,主動輪詢和獲取推送訊息。
主動輪詢帶有隨機性,如果恰恰好,可能會很及時,大多數情況下,可能沒那麼恰恰好,而且還要增加服務發現中心的負載壓力。
推送方式在效果上可能更好一些,在具體實現上可能沒有那麼簡單。
健康檢查
一般而言,健康檢查包括,客戶端心跳和服務端主動探測兩種方式,
首先來說,客戶端心跳,就是客戶端透過TCP或者HTTP的方式,告訴服務端自身的執行情況,當然客戶端通知是有弊端的,比如客戶端在某一時間點出現故障時,無法及時通知服務端,事後恢復執行後,告知的也只是當前的執行狀態,即便這時再告訴服務端之前的執行狀況,也對服務呼叫沒有什麼太大意義了,只是對服務本身的執行分析起到了一定作用。即便是保持正常連線的情況下,服務也未必是可以呼叫的,比如資料庫掛掉。
一般而言,採用服務端探測的比較多一些,呼叫方式和客戶端心跳差不多,但是仍然需要我們註意的是,客戶端所提供的探測介面必須具有通用性,比如可以查一下次資料庫等等,這樣可以比較全面的反應系統的執行情況。
容災與故障轉移
容災的原因有很多,比如服務不再使用、雙11大流量的湧進,使得其中一些服務不可用,也就不得不針對性的對一些服務進行容災處理,以集中資源給核心應用。在容災過程中,可以給服務下線功能可以制定一些策略,以豐富功能的使用。.NET Core裡面可以使用IApplicationLifetime來顯示下線功能。當然也需要提供手動下線的介面。
容災主要考慮方向是客戶端容災和服務端容災。客戶端容災中,如果服務註冊中心掛掉了,恢復執行可能需要一段時間,在這個過程中如果保證服務正常執行。在實際執行中非常有可能發生這種情況,我們可以將服務發現中心獲取的資訊快取起來,快取方式有很多,無外乎是本地記憶體、檔案以及外部儲存,本地記憶體還要還要考慮該服務重啟過程中的資料丟失,所以可選的方式就有記憶體+檔案方式。反之,為了達到容災的要求,我們可以在獲取服務發現中心傳回的資料的過程中將資料存在到記憶體和檔案中,可以採用非同步方式儲存。
如下圖所示,在發現過程中,使用快取功能,如果快取都失效了,那就真的要game over一陣子了。
服務端容災,就比較簡單了,因為現在大多數做的都是服務端容災,比如叢集等水平擴充套件方式, 可主動從其他節點同步相應的資料,也可等待master的同步。
總結
服務註冊與發現在服務生命週期中發揮著重要作用。動態的服務註冊和發現變得非常重要,它可以避免服務中斷。在處理服務實體的容災與故障轉移時,儘量實現自動轉移,並提供手動方式處理,而對於跨服務呼叫,尤其是該服務擁有多實體服務的時候,需要考慮負載平衡。
朋友會在“發現-看一看”看到你“在看”的內容