1. 程式人生 > >微服務理論系列(一):服務發現四問四答

微服務理論系列(一):服務發現四問四答


        在開始之前,我們先來回顧下業內對於微服務架構的定義。簡單來說,微服務就是用一組小服務的方式來構建一個應用,服務獨立執行在不同的程序中,服務之間通過輕量的通訊機制(如 RESTful 介面)來互動,並且服務可以通過自動化部署方式獨立部署。

 

1.什麼是服務發現,在微服務架構中,服務發現的作用是什麼?

       之前做單體式應用開發時很少提及服務發現,因為傳統單體應用動態性不強,不會頻繁的更新和重新發布,也較少進行自動伸縮。傳統單體應用的網路位置很少發生變化,在發生變化時,由運維人員手工更新一下它們的配置檔案,也不是什麼太大的問題。

       而微服務架構則完全不同,微服務會被頻繁的更新和重新發布,頻繁的根據負載情況進行動態伸縮,微服務例項還可能受資源排程影響而從一臺伺服器遷移到另一臺伺服器。

      總而言之,在微服務架構中,微服務例項的網路位置發生變化是一種常態,所以必須提供一種機制,使得服務消費者在服務提供者的網路位置發生變化時,能夠及時獲得最新的位置資訊,一般是提供一個網路位置穩定的服務註冊中心,服務提供者的網路位置被註冊到註冊中心,並在網路位置發生變化的時候及時更新,而服務消費者定期向註冊中心獲取服務提供者的最新位置資訊,這就是最基本的服務發現機制。較為複雜的服務發現實現除了服務提供者的位置資訊外,還可以向服務消費者提供服務提供者的描述資訊、狀態資訊和資源使用資訊,以供服務消費者實現更為複雜的服務選擇邏輯。

 

2.服務發現的具體流程是怎樣的?

服務發現的流程比較簡單,去年我翻譯了 Chris Richardson 的一些微服務文章,對服務發現的流程做了些基本的描述,比較完備的說,服務發現流程應該分為兩種模式:

客戶端發現:

  1. 服務提供者的例項在啟動時或者位置資訊發生變化時會向服務登錄檔註冊自身,在停止時會向服務登錄檔登出自身,如果服務提供者的例項發生故障,在一段時間內不傳送心跳之後,也會被服務登錄檔登出。

  2. 服務消費者的例項會向服務登錄檔查詢服務提供者的位置資訊,然後通過這些位置資訊直接向服務提供者發起請求。

服務端發現:

  1. 第一步與客戶端發現相同。

  2. 服務消費者不直接向服務登錄檔查詢,也不直接向服務提供者發起請求,而是將對服務提供者的請求發往一箇中央路由器或者負載均衡器,中央路由器或者負載均衡器查詢服務登錄檔獲取服務提供者的位置資訊,並將請求轉發給服務提供者。

       這兩種模式各有利弊,客戶端發現模式的優勢是,服務消費者向服務提供者發起請求時比服務端發現模式少了一次網路跳轉,劣勢是服務消費者需要內建特定的服務發現客戶端和服務發現邏輯;服務端發現模式的優勢是服務消費者無需內建特定的服務發現客戶端和服務發現邏輯,劣勢是多了一次網路跳轉,並且需要基礎設施環境提供中央路由機制或者負載均衡機制。目前客戶端發現模式應用的多一些,因為這種模式的對基礎設施環境沒有特殊的要求,和基礎設施環境也沒有過多的耦合性。

 

3.目前都有哪些服務發現的解決方案?各有什麼優缺點?

       其實可選方案並不多,所以選擇起來也並不糾結。DNS 可以算是最為原始的服務發現系統,但是在服務變更較為頻繁,即服務的動態性很強的時候,DNS 記錄的傳播速度可能會跟不上服務的變更速度,這將導致在一定的時間視窗內無法提供正確的服務位置資訊,所以這種方案只適合在比較靜態的環境中使用,不適用於微服務。

        基於 ZooKeeper、Etcd 等分散式鍵值對儲存服務來建立服務發現系統在現在看起來也不是一種很好的方案,一方面是因為它們只能提供基本的資料儲存功能,還需要在外圍做大量的開發才能形成完整的服務發現方案。另一方面是因為它們都是強一致性系統,在叢集發生分割槽時會優先保證一致性、放棄可用性,而服務發現方案更注重可用性,為了保證可用性可以選擇最終一致性,這兩方面原因共同導致了 ZooKeeper、Etcd 這類系統越來越遠離服務發現方案的備選清單,像 SmartStack 這種依賴 ZooKeeper 的服務發現方案也逐漸發覺 ZooKeeper 成了它的薄弱環節。

       Netflix 的 Eureka 是現在最流行的服務發現方案,服務端和客戶端都是 Java 編寫的,針對微服務場景,並且和 Netflix 的其他開源專案以及 Spring Cloud 都有著非常好的整合,具備良好的生態,如果你使用 Java 語言開發,Eureka 幾乎是你的最佳選擇。與 ZooKeeper、Etcd 或者依賴它們的方案不同,Eureka 是個專門為服務發現從零開始開發的專案,Eureka 以可用性為先,可以在多種故障期間保持服務發現和服務註冊功能可用,雖然此時會存在一些資料錯誤,但是 Eureka 的設計原則是“存在少量的錯誤資料,總比完全不可用要好”,並且可以在故障恢復之後按最終一致性進行狀態合併,清理掉錯誤資料。

        前面為什麼說 Eureka“幾乎是”最佳選擇,因為它還有個強大的對手 Consul。Consul 是 HashiCorp 公司的商業產品,它有一個開源的基礎版本,這個版本在基本的服務發現功能之外,還提供了多資料中心部署能力,包括記憶體、儲存使用情況在內的細粒度服務狀態檢測能力,和用於服務配置的鍵值對儲存能力(這是一把雙刃劍,使用它可以帶來便捷,但是也意味著和 Consul 的較強耦合性),這幾個能力 Eureka 目前都沒有。而 Consul 的商業版本功能更為強大,如果你不介意依賴單一公司提供的商業產品,也可以從 Consul 的開源版本開始用起。

        最後還有一個比較有趣的方案是 SkyDNS,這是一個結合古老的 DNS 技術和時髦的 Go 語言、Raft 演算法的有趣專案,主要在 Kubernetes 裡使用,因為 Kubernetes 有一層較為穩定的 Service 抽象,有點類似於問題 2 裡描述的服務端服務發現方式,所以不存在 DNS 時間視窗的問題。

        這裡我就不對上述的各個方案做具體功能特性上的對比了,我在做方案選型時不太喜歡做這種微觀對比,因為具體的功能特性是易變的,今天 Consul 出一個新功能,明天 Eureka 出一個新特性,如果依賴這個做選擇,會搖擺不定,我更注重這些方案背後的一些根深蒂固的必然性,比如 ZooKeeper 永遠都不會為了服務發現放棄它的強一致性,所以即使它有再多適合服務發現的功能特性,它也不會成為服務發現的優選方案,再比如 Consul 由一家商業軟體公司提供,那麼必然或多或少的存在商業軟體的某些弊端,如果你非常在意這些弊端,Consul 再強大,你也不會選擇它。

 

4.落地服務發現的難點是什麼?

       落地服務發現的難點主要來自於分散式系統本身的複雜性和對業務系統的侵入性。因為幾乎所有服務提供者和服務消費者都對服務發現服務存在依賴,如果服務發現服務出現問題,將會造成大範圍的影響,所以服務發現服務自身的可用性至關重要。

        為了保證服務發現服務本身的可用性,除了對服務發現服務進行本地的多節點部署之外,往往還需要跨越多個可用區甚至多個數據中心部署,以確保服務發現服務可以在多個層次的軟硬體故障中存活。在服務提供者和服務消費者數量眾多時,服務發現服務的效能也可能會成為問題。上述這些問題和分散式系統普遍存在的問題一致,只要在技術上做出充分準備,都是可以解決的,在此不做贅述。

        更大的難點在某種程度上是非技術問題,現有的服務發現方案都或多或少的對業務系統存在侵入性,會改變業務系統的開發模式,例如在開發階段需要引入特殊的客戶端和服務的註冊、查詢過程。給開發帶來新的複雜度倒不是什麼特別大的問題,因為這些複雜度可以通過技術工具降低,比如普元現在在做的微服務開發平臺就可以使這些開發過程中的額外工作自動化,更重要的是服務發現方案一旦確定,之後再做更換的成本會非常高,在對方案進行推廣的時候,很可能會引起業務部門的擔憂,並因此遇到阻力。