天天看點

技術科普丨服務發現和負載均衡的來龍去脈

問題緣由

單機時代,傳統軟體大多是單體/巨石架構(Monolithic)。大家往一個代碼倉庫送出CODE,這會導緻應用膨脹,難以了解和修改,以及擴充受限,無法按需伸縮等諸多問題。單體架構怎麼解決多人合作的問題?子產品化,對,按功能拆分,子產品之間定義程式設計接口(API),彼此關心功能而不關心實作。

技術科普丨服務發現和負載均衡的來龍去脈

随着時代發展,單機程式遇到了計算力和存儲的雙重瓶頸,分布式架構應運而生。單體應用通過函數名(辨別)便可輕松完成本地函數調用,在分布式系統中,服務(RPC/RESTful API)承擔了類似的角色,但請求服務單靠服務名還不夠,服務名隻是服務能力(服務類型)的辨別,還需要訓示服務位于網絡何處,而部署在雲中的服務執行個體IP是動态配置設定的,擴縮容、失敗和更新則讓問題變得更加複雜,靜态配置服務執行個體适應不了新變化,需要更精細化的服務治理能力,為了解決或者說簡化這個問題,服務發現作為一種基礎能力被抽象和提供,它試圖讓請求網絡服務像調用本地函數一樣簡單透明。

技術科普丨服務發現和負載均衡的來龍去脈

服務即功能(函數)。隻是服務跟網絡緊密聯系在一起,所有才會出現網絡服務這個名詞,服務提供者通過網絡釋出服務,服務使用者通過網絡請求服務,分布式系統突破了單機算力和存儲的限制,提升了系統穩定性,使得高并發高可用的海量服務成為可能,但這也增加了軟體複雜度,引入軟體分層、負載均衡、微服務、服務發現/治理、分布式一緻性等新的問題和挑戰。

服務發現

服務分服務提供者(Service Provider)和服務消費者(Service Consumer),如果要提供海量服務能力,單一的服務執行個體顯然是不夠的,如果要提供成千上萬種服務,則需要有一個地方記錄服務名到服務執行個體清單的映射,是以,有必要引入一個新的角色:服務中介,服務中介維護一個服務系統資料庫(Service Registry),可以把系統資料庫了解為服務字典,key是服務名,value是服務提供執行個體清單;服務系統資料庫是聯系服務提供者和服務消費者的橋梁,它維護服務提供者的最新網絡位置等資訊,也是服務發現最核心的部分。

服務啟動的時候,把服務資訊注冊(put)到服務系統資料庫;服務終止的時候,從服務系統資料庫删除(remove)自身的服務資訊。

服務消費者在請求服務的時候,先去服務系統資料庫按名查詢(get)服務提供者清單,然後從清單裡挑選一個服務執行個體,向該執行個體請求服務。

大道至簡,這便是最簡單的服務發現模型,也是服務發現的基本原理,至此,似乎一切都OK,但其實尚有幾個問題沒有說清楚。

問題和解法

第一個問題

服務如果不是正常停止,而是被系統kill掉,它便沒有機會通知服務系統資料庫把自身服務資訊删除,這樣系統資料庫便多了一條指向無效服務執行個體的資訊,而服務消費者卻并不知情,怎麼辦?解決的辦法很簡單:保活(keepalive),服務提供者定期(比如每隔10秒)給服務中介發送keepalive消息,服務中介收到keepalive消息後更新該服務執行個體的keepalive timestamp,服務中介定期檢查該timestamp,如果超期便把該服務執行個體從系統資料庫剔除。

第二個問題

服務執行個體清單變化如何通知服務消費者?不外乎兩種方法,輪詢和pub-sub。輪詢是消費者主動詢問服務中介服務清單是否變化,如果有變化,則把新的服務清單發送給消費者。如果消費者過多,則服務中介處理輪詢的消息會有壓力,在服務類别很多,服務清單很大的時候,它甚至會成為瓶頸。pub-sub是服務中介主動通知服務消費者,時效性相比輪詢更好,缺點是會占用單獨的線程或者連接配接資源。

技術科普丨服務發現和負載均衡的來龍去脈

第三個問題

服務中介如果挂了怎麼辦?是以我們要解決單點的問題,通常會用叢集來對抗這種脆弱性,有很多用于做服務系統資料庫的開源解決方案,比如etcd/zookeeper/consul,本質上使用分布式一緻性資料庫來儲存系統資料庫資訊,它既解決讀寫性能問題又提高了系統穩定性可用性。

第四個問題

如果服務消費者每次使用遠端服務都需要先查詢服務中介擷取執行個體清單,再請求服務,這樣效率太低效?對服務中介的壓力也不小?通常,用戶端會緩存服務執行個體清單,這樣對同名服務的多次請求,便不用重複查詢,既減少了延遲又減輕了對服務中介的通路壓力。

第五個問題

前述的keepalive有間隔,如果在這個間隔内服務執行個體不可用,那麼服務消費者還是不能感覺的,是以還是有可能把請求發送到一個無法提供服務的網絡遠端機器上去,這樣自然是沒法work。我們無法從根本上杜絕這種情況,系統需要容忍這種錯誤,但也可以做一些改進,比如向某執行個體請求服務失敗後便拉黑,避免向同一無效服務執行個體多次派發請求。

第六個問題

服務消費者怎麼從多個服務執行個體裡選擇一個?如何確定同一服務消費者的多次服務請求被配置設定到固定的服務執行個體(有時候需要這樣)?這其實就是負載均衡的問題,有多種政策,比如rr、優先級、比如權重随機、一緻性哈希。

服務發現模式

服務發現主要有兩種模式:用戶端發現模式(client-side discovery)和服務端發現模式(server-side discovery)。

用戶端發現模式

技術科普丨服務發現和負載均衡的來龍去脈

用戶端負責查詢服務執行個體清單并決定向哪個執行個體請求服務,也就是負載均衡政策在用戶端實作。該模式包括注冊和發現兩個部分。

服務執行個體調用服務中介的注冊接口進行執行個體注冊,服務執行個體通過keepalive做服務續期,服務中介通過健康檢查剔除不可用的服務執行個體。

服務消費者請求服務的時候,先向服務系統資料庫查詢服務執行個體清單,系統資料庫是一個服務資料庫,為了提升性能和可靠性,用戶端通常會緩存服務清單(緩存用來確定系統資料庫挂了之後還能繼續工作),拿到執行個體清單後用戶端基于負載均衡政策挑選一個執行個體發送服務請求。

優點

  • 直接,用戶端可以靈活的執行負載均衡政策。
  • 去中心化,非網關式,有效避開單點瓶頸和可靠性下降。
  • 服務發現直接SDK內建進用戶端,這種語言整合程度很好,程式執行性能也很好,排錯友善。

缺點

  • 用戶端與服務系統資料庫耦合,需要為服務用戶端使用的每種語言每種架構開發服務發現邏輯。
  • 這種侵入式的內建會導緻任何服務發現的變化都需要用戶端應用程式重新編譯和部署,強綁定違背了獨立性原則。
  • 服務上下線會對調用方有影響,導緻服務短暫不可用。

服務端發現模式

技術科普丨服務發現和負載均衡的來龍去脈

發現:服務消費者通過負載均衡器發送服務請求,負載均衡器會查詢服務系統資料庫,挑選一個服務執行個體,并将請求轉發到服務執行個體。

注冊:服務注冊/登出可以跟上述用戶端發現模式一緻,也可以通過部署平台的内置服務注冊和發現機制完成,即容器化部署平台(docker/k8s)能主動發現服務執行個體并幫助服務執行個體完成注冊登出。

對比用戶端發現模式,使用服務端發現模式的用戶端本地不儲存服務執行個體清單,用戶端不做負載均衡,這個負載均衡器既承擔了服務發現的角色,又承擔了網關的角色,是以經常叫API網關伺服器。

因為負載均衡器是中心式的,是以它也必須是一個叢集,單個執行個體不足以支撐高并發通路,針對負載均衡器本身的服務發現和負載均衡通常借助DNS。

Http伺服器,Nginx、Nginx Plus就是此類服務端發現模式的負載均衡器。

  • 服務發現對于服務消費者是透明的,服務消費者與系統資料庫解耦,服務發現功能的更新對用戶端無感覺。
  • 服務消費者隻需要向負載均衡器發送請求,不需要為每種服務消費者的程式設計語言和架構,開發服務發現邏輯SDK。

  • 由于所有請求都要經負載均衡器轉發,是以負載均衡器有可能成為新的性能瓶頸。
  • 負載均衡器(服務網關)是中心式的,而中心式的架構會有穩定性的隐憂。
  • 因為負載均衡器轉發請求,是以RT會比用戶端直連模式高。

微服務和服務發現

Service Mesh服務網格是服務于微服務應用程式的可配置基礎設施層,旨在處理服務之間的大量基于網絡的程序間通信。

技術科普丨服務發現和負載均衡的來龍去脈

Service Mesh服務網關解耦調用和通信,在非mesh下,對于協定的感覺和服務發現方法的感覺需要應用去做,用mesh之後,就隻管調用,mesh通過控制面來控制應用的資料流。

Mesh做服務發現其實是用戶端發現模式的更新版,基于sidecar和pilot實作,Sidecars,即資料面闆(Data Plane),負責發現目标服務執行個體位址清單并轉發請求。Pilots,即控制台(Control Plane),負責管理服務系統資料庫的所有服務注冊資訊。

服務注冊模式

一個選擇是服務執行個體自注冊,即self-registration模式。另一種選擇是其它的系統元件來管理服務執行個體的注冊,即third-party registration模式。

自注冊模式如前面所述,它足夠簡單,不需要第三方元件,缺點是必須為服務中用到的每種程式設計語言與架構實作注冊代碼。

第三方注冊服務執行個體不會自己完成注冊登出,它由另一個叫做Service Registrar的系統元件負責,該元件會輪詢部署環境或者跟蹤訂閱事件去感覺服務執行個體的變化,幫助服務執行個體完成自動化注冊登出。

Third-party registration模式主要的優勢在于解耦了服務和服務系統資料庫。不需要為每個語言和架構都實作服務注冊邏輯。服務執行個體注冊由一個專用的服務集中實作。缺點是除了被内置到部署環境中,它本身也是一個高可用的系統元件,需要被啟動和管理。

其他

如果某個服務對于的服務執行個體特别多,比如在一些頭部公司,一個服務名可能對應幾千幾萬個服務執行個體,這樣,服務變更的查詢和對比會很慢,IO的量會大得超過想象,通常,會用version num去解決這個問題。

點選關注,第一時間了解華為雲新鮮技術~

繼續閱讀