一個近乎完美基於Dubbo的微服務改造實踐

網易考拉(以下簡稱考拉)是網易旗下以跨境業務為主的綜合型電商,自 2015 年 1 月 9 日上線公測後,業務保持了高速增長,這背後離不開其技術團隊的支撐。


一個近乎完美基於Dubbo的微服務改造實踐


微服務化是電商 IT 架構演化的必然趨勢,網易考拉的服務架構演進也經歷了從單體應用走向微服務化的整個過程。

以下整理自網易考拉陶楊在近期 Apache Dubbo Meetup 上的分享,通過該文,您將瞭解到:

  • 考拉架構的演進過程
  • 考拉在服務化改造方面的實踐
  • 考拉在解決註冊中心性能瓶頸方面的實踐
  • 考拉未來的規劃

考拉架構的演進過程

考拉在 2015 年初上線的時候,線上只有七個工程,商品詳情頁、購物車下單頁等都耦合在中間這個 online 的工程裡面。

單體架構


一個近乎完美基於Dubbo的微服務改造實踐


在上線之初的時候,這種架構還是比較有優勢的,因為當時考拉的開發人員也不是很多,把所有的功能都耦合在一個進程裡面,利於集中開發、測試和上線,是一種比較高效和節省成本的方式。

但是隨著業務的不斷髮展,包括需求的逐步增多,開發團隊的不斷擴容,這時候,單體架構的一些劣勢就逐漸的暴露出來了。

例如開發效率低:功能之間的相互耦合,不同需求的不同分支也經常會修改同一塊代碼,導致合代碼的過程非常痛苦,而且經常會出問題。

再例如上線成本高:幾乎所有的發佈需求都會涉及到這些應用的上線,同時不斷增長的業務需求,也會使得我們的代碼越來越臃腫。

這造成維護困難、可用性差,功能之間相互耦合,都耦合在一個進程裡面,導致一旦某一個業務需求涉及的代碼或者資源出現問題,那麼就會影響其他的業務。

比如說我們曾經在 online 工程裡面,因為優惠券兌換熱點的問題,影響了核心的下單服務。

這個架構在考拉運行的 4 到 5 個月的時間裡,從開發到測試再到上線,大家都特別痛苦。所以我們就開始進行了服務化拆分的工作。

分佈式服務架構


一個近乎完美基於Dubbo的微服務改造實踐


這個是考拉現在的分佈式服務架構。伴隨著服務化的拆分,我們的組織架構也進行了很多調整,出現了商品中心、用戶中心和訂單中心等等。

拆分其實是由業務驅動的,通過業務來進行一些橫向拆分或者縱向拆分,同時,拆分也會面對一個拆分粒度的問題。

比如怎麼才算一個服務,或者說服務拆的過細,是不是會導致我們管理成本過高,又或者說是否會帶來架構上的新問題。

考拉的拆分由粗到細是一個逐步演進的過程。隨著服務化的拆分,使得服務架構越來越複雜,隨之而來產生了各種各樣的公共技術,比如說服務治理、平臺配置中心、分佈式事務和分佈式定時任務等等。

考拉的服務化實踐

微服務框架在服務化中起到了很重要的作用,是服務化改造的基石,經過嚴格的技術選型流程後,我們選用了 Dubbo 來作為考拉服務改造的一個重要支柱。

Dubbo 可以解決服務化過程中服務的定義、服務的註冊與發現、服務的調用和路由等問題。

此外,Dubbo 也具有一些服務治理的功能和服務監控的功能。下面我將介紹考拉基於 Dubbo 做的一些服務化實踐。

熔斷

首先來說一下熔斷。在進行服務化拆分之後,應用中原有的本地調用就會變成遠程調用,這樣就引入了更多的複雜性。

比如說服務 A 依賴於服務 B,這個過程中可能會出現網絡抖動、網絡異常。

或者說服務 B 變得不可用或者不好用時,也會影響到 A 的服務性能,甚至可能會使得服務 A 佔滿整個線程池,導致這個應用上其他的服務也受影響,從而引發更嚴重的雪崩效應。

因此,服務之間有這樣一種依賴關係之後,需要意識到服務的依賴其實是不穩定的。

此時,需要通過採取一些服務治理的措施,例如熔斷、降級、限流、隔離和超時等,來保障應用不被外部的異常拖垮。

Dubbo 提供了降級的特性,比如可以通過 mock 參數來配置一些服務的失敗降級或者強制降級,但是 Dubbo 缺少自動熔斷的特性,所以我們在 Dubbo 上引入了 Hystrix。


一個近乎完美基於Dubbo的微服務改造實踐


消費者在進行服務調用的時候會經過熔斷器,當服務提供者出現異常的時候,比如暫時性的不可用,熔斷器就會打開,對消費端進行調用短路,此時,消費端就不會再發起遠程調用,而是直接走向降級邏輯。

與此同時,消費端會持續的探測服務的可用性,一旦服務恢復,熔斷器就會關閉,重新恢復調用。

在 Dubbo 的服務治理平臺上,可以對 Hystrix 上運行的各種動態參數進行動態的配置,包括是否允許自動熔斷,是否要強制熔斷,熔斷的失敗率和時間窗口等等。

限流

下面再說一下限流。當用戶的請求量,調用超過系統可承受的併發時,系統 QPS 會降低、出現不可用甚至存在宕機的風險。

這就需要一個機制來保護我們的系統,當預期併發超過系統可承受的範圍時,進行快速失敗、直接返回,以保護系統。

Dubbo 提供了一些基礎的限流特性,例如可以通過信號量的配置來限制我們消費者的調用併發,或者限制提供者的執行併發。

但是這些是遠遠不夠的,考拉自研了限流框架 NFC,並基於 Dubbo filter 的形式,實現了對 Dubbo 的支持,同時也支持對 URL 等其他資源的限流。

通過配置中心動態獲取流控規則,對於資源的請求,比如 Dubbo 調用會經過流控客戶端,進行處理並判斷是否觸發限流,一旦請求超出定義的閾值,就會快速失敗。


一個近乎完美基於Dubbo的微服務改造實踐


同時,這些限流的結果會上報到監控平臺。上圖中的頁面就是考拉流控平臺的一個監控頁面。

我們在頁面上可以對每一個資源(URL、Dubbo 接口)進行一個閾值的配置,並對限流進行準實時監控,包括流控比率、限流次數和當前的 QPS 等。

限流框架除了實現基本的併發限流之外,也基於令牌桶和漏桶算法實現了 QPS 限流,並基於 Redis 實現了集群級別的限流。這些措施保障系統在高流量的情況下不會被打垮。

考拉在服務化改造方面的實踐

考拉在監控服務方面的改造

在服務化的過程中,系統變得越來越複雜,服務數量變得越來越多,此時需要引入更多維度的監控功能,幫助快速的去定位並解決系統中的各類問題。


一個近乎完美基於Dubbo的微服務改造實踐


監控主要分為這四個方面:

  • 日誌
  • Metrics
  • Trace
  • HealthCheck(健康檢查)

在應用程序、操作系統運行的時候,都會產生各種各樣的日誌,通過日誌平臺對這些日誌進行採集、分析和展示,並支持查詢和操作。

Metrics 反映的是系統運行的基本狀態,包括瞬時值或者聚合值,例如系統的 CPU 使用率、磁盤使用率,以及服務調用過程中的平均延時等。

Trace 是對服務調用鏈的一個監控,例如調用過程中的耗時分析、瓶頸分析、依賴分析和異常分析等。Healthcheck 可以探測應用是否準備就緒,是否健康,或者是否還存活。

接下來,圍繞 Dubbo 來介紹一下考拉在監控方面的改造實踐。

第一個是服務監控。Dubbo 提供了服務監控功能,支持定期上報服務監控數據,通過代碼增強的方式,採集 Dubbo 調用數據,存儲到時序數據庫裡面,將 Dubbo 的調用監控功能接入到考拉自己的監控平臺。


一個近乎完美基於Dubbo的微服務改造實踐


上圖中的頁面是對 Dubbo 提供者的服務監控,包括對服務接口、源集群等不同維度的監控,除了全局的調用監控,還包括不同維度的監控,例如監控項裡的調用次數。

有時候我們更關心慢請求的情況,所以會將響應時間分為多個範圍,比如說從 0 到 10 毫秒,或是從 10 到 50 毫秒等,這樣就可以看到在各個範圍內請求的數量,從而更好地瞭解服務質量。

同時,也可以通過各種報警規則,對報警進行定義,當服務調用出現異常時,通過郵件、短信和電話的形式通知相關人員。

監控平臺也會對異常堆棧進行採集,例如說這次服務調用的異常的原因,是超時還是線程滿了的,可以在監控平臺上直接看到。同時生成一些監控報表,幫助我們更好地瞭解服務的性能,推進開發去改進。


一個近乎完美基於Dubbo的微服務改造實踐


第二個是 Trace。我們參考了 Dapper,自研了 Trace 平臺,並通過代碼增強的方式,實現了對 Dubbo 調用鏈路的採集。

相關調用鏈參數如 TarceID,SpanID 等是通過 Dubbo 的隱式傳參來傳遞的。Trace 可以瞭解在服務調用鏈路中的一個耗時分析和瓶頸分析等。

Trace 平臺上可以展示一次服務調用,經歷了哪些節點,最耗時的那個節點是在哪裡,從而可以有針對性的去進行性能優化。

Trace 還可以進行依賴分析,這些依賴是否合理,能否通過一些業務手段或者其它手段去減少一些不合理的依賴。

Trace 對異常鏈路進行監控報警,及時的探測到系統異常並幫助我們快速的定位問題,同時和日誌平臺做了打通,通過 TraceID 可以很快的獲取到關聯的異常日誌。


一個近乎完美基於Dubbo的微服務改造實踐


第三個是健康檢查。健康檢查也是監控中很重要的一個方面,以更優雅的方式上線應用實例。我們和自動部署平臺結合,實現應用的健康檢查。

服務啟動的時候可以通過 Readiness 接口判斷應用依賴的各種資源,包括數據庫、消息隊列等等是否已經準備就緒。

只有健康檢查成功的時候才會觸發出注冊操作。同時 Agent 也會在程序運行的過程中定時的檢查服務的運行狀態。

同時,也通過這些接口實現更優雅的停機,僅依賴 shutdownhook,在某些情況下不一定靠譜,比如會有 shutdownhook 執行先後順序的問題。

應用發佈的時候,首先調用 offline 接口,將註冊服務全部從註冊中心反註冊,這時不再有新的流量進來,等到一段時間後,再執行停機發布操作,可以實現更加優雅的停機。

考拉在服務測試方面的改造

下面來介紹一下考拉在服務測試方面的實踐。服務測試分為接口測試、單鏈路壓測、全鏈路壓測和異常測試四個維度。

接口測試

通過接口測試,可以來驗證對外提供的 Dubbo 服務是否正確,因此我們也有接口測試平臺,幫助 QA 更好的進行接口測試,包括對接口的編輯(入參、出參),用例的編輯和測試場景的執行等。


一個近乎完美基於Dubbo的微服務改造實踐


單鏈路壓測

單鏈路的壓測,主要面對單個功能的壓測,比如要上線一個重要功能或者比較重要的接口之前,必須通過性能測試的指標才可以上線。

全鏈路壓測

考拉作為電商平臺,在大促前都會做全鏈路壓測,用以探測系統的性能瓶頸,和對系統容量的預估。

例如,探測系統的各類服務的容量是否夠,需要擴容多少,以及限流的閾值要定多少合適,都可以通過全鏈路壓測來給出一些合理的值。

異常測試

對服務調用鏈路中的一些節點進行系統異常和服務異常的注入,也可以獲取他們的強度依賴關係。

比如一個非常重要的接口,可以從 Trace 獲取的調用鏈路,然後對調用鏈的依賴的各個服務節點進行異常注入。

通過接口的表現,系統就會判斷這個接口的強度依賴關係,以改善這些不合理的強依賴關係。

考拉在 API 網關方面的改造

隨著考拉服務化的發展,我們自研了 API 網關,API 網關可以作為外部流量的統一接口,提供了包括路由轉發、流控和日誌監控等一些公共的功能。


一個近乎完美基於Dubbo的微服務改造實踐


考拉的 API 網關是通過泛化調用的方式來調用後臺 Dubbo 的服務的。Dubbo 原生的泛化調用的性能比普通 API 調用要差一些。

所以我們也對泛化調用性能做了一些優化,也就是去掉了泛化調用在返回結果時的一次對象轉換。最終壓測的結果泛化的性能甚至比正常的調用性能還要好些。

考拉在多語言方面的改造

考拉在業務發展的過程中產生了不少多語言的需求,例如,我們的前端團隊希望可以用 Node 應用調用 Dubbo 服務。

對比了易用性,選用了開源的 jsonrpc 方案,然後在後端的 Dubbo 服務上暴露了雙協議,包括 Dubbo 協議和 json rpc 協議。


一個近乎完美基於Dubbo的微服務改造實踐


但在實施的過程中,也遇到了一些小問題,比如說,對於 Dubbo 消費者來說,不管是什麼樣的協議提供者,都是 invoker。

通過一個負載均衡策略,選取一個 invoker 進行調用,這個時候就會導致原來的 Java 客戶端選用一個 jsonrpc 協議的提供者。

這樣如果他們的 API 版本不一致,就有可能導致序列化異常,出現調用失敗的情況。

所以,我們對 Dubbo 的一些調用邏輯做了改造,例如在 Java 客戶端的消費者進行調用的時候,除非顯示的配置,否則默認只用 Dubbo 協議去調用。

另外,考拉也為社區的 jsonrpc 擴展了隱式傳參的功能,因為可以用 Dubbo 隱式傳參的功能來傳遞一些全鏈路參數。

考拉在解決註冊中心性能瓶頸方面的實踐

註冊中心瓶頸可能是大部分電商企業都會遇到的問題,考拉也不例外。我們現在線上的 Dubbo 服務實例大概有 4000 多個,但是在 ZooKeeper 中註冊的節點有一百多萬個,包括服務註冊的 URL 和消費者訂閱的 URL。


一個近乎完美基於Dubbo的微服務改造實踐


Dubbo 應用發佈時的驚群效應、重複通知和消費者拉取帶來的瞬時流量一下就把 ZooKeeper 集群的網卡打滿,ZooKeeper 還有另外一個問題,他的強一致性模型導致 CPU 的利用率不高。

就算擴容,也解決不了 ZooKeeper 寫性能的問題,ZooKeeper 寫是不可擴展的,並且應用發佈時有大量的請求排隊,從而使得接口性能急劇下降,表現出來的現象就是應用啟動十分緩慢。

因此,在今年年初的時候就我們決定把 ZooKeeper 註冊中心給替換掉,對比了現有的一些開源的註冊中心,包括 Consul、Eruka、etcd 等,覺得他們並不適合 Dubbo 這種單進程多服務的註冊模型,同時容量能否應對未來考拉的發展,也是一個問號。

於是,我們決定自研註冊中心,目前正在註冊中心的遷移過程當中,採用的是雙註冊中心的遷移方案,即服務會同時註冊 ZooKeeper 註冊中心,還有新的註冊中心,這樣對原有的架構不會產生太大的影響。

考拉新的註冊中心改造方案和現在社區的差不多,比如說也做了一個註冊數據的拆分,往註冊中心註冊的數據只包含 IP, Port 等關鍵數據,其他的數據都寫到了 Redis 裡面。

註冊中心實現使用了去中心化的一個架構,包括使用最終一致性來換取我們接口性能的一個提升。

後面如果接入 Dubbo,會考慮使用 Nacos 而不是 ZooKeeper 作為註冊中心。

未來規劃


一個近乎完美基於Dubbo的微服務改造實踐


考拉最近也在進行第二機房的建設,通過兩個機房獨立部署相同的一套系統,以實現同城雙活。

針對雙機房的場景,Dubbo 會做一定的改造,例如同機房優先調用,類似於即將發佈的 Dubbo 2.7.0 中的路由特性。

在 Dubbo 服務註冊的時候,讀取系統環境變量的環境標或者機房標,再將這些機房標註冊到註冊中心,然後消費端會做一個優先級路由,優先進行同機房的服務調用。

容器化也是我們在規劃的一個方向。隨著服務化進程的演進,服務數也變得越來越多,通過容器化、DevOps 可以提升測試、部署和運維效率。

Service Mesh 在今年非常火,通過 Service Mesh 將服務框架的能力比如註冊發佈,路由和負載均衡,服務治理等下沉到 Sidecar,使用獨立進程的方式來運行。

對於業務工程的一個解耦,幫助我們實現一個異構系統,對多語言支持,也可以解決中間件升級推動困難以及各種依賴的衝突,業務方也可以更好的關注於業務開發,這也會是未來探索的一個方向。


分享到:


相關文章: