做者 | 張遠征
來源|阿里巴巴雲原生公衆號緩存
導讀:Dubbo 做爲分佈式微服務框架,衆多公司在實踐中基於 Dubbo 進行分佈式系統架構。重啓開源後,咱們不只看到 Dubbo 3.0 最新的 Roadmap 發佈,並且還看到阿里在自身電商開始推動 Dubbo 和內部 HSF 的融合,並在 雙11 上開始使用 Dubbo 3.0。本文是工商銀行基於 Dubbo 構建金融微服務架構的分享,主要講述了服務發現的應對策略和成果,後續將發佈工行大規模服務監控治理的實踐,以及從企業角度怎麼去對 Dubbo 二次開發等內容。歡迎關注。
工行傳統的業務系統通常都是基於 JEE 的單體架構,面對金融業務線上化及多樣化的發展趨勢,傳統架構已經沒法知足業務的需求。所以從 2014 年開始,工行選擇了一個業務系統作服務化的嘗試,並驗證、評估、對比了當時的幾個分佈式服務框架,最終選擇了相對完善、而且國內已經有較多公司落地使用的 Dubbo。與此同時,工行還對 Dubbo 作了企業定製,幫助這個業務系統完成了服務化的落地,上線以後也收到了很是好的效果。網絡
2015 年,工行開始擴大服務架構的落地範圍,一方面幫助傳統業務系統進行架構轉型,另外一方面也逐漸沉澱了相似中臺的超大規模服務羣組,支撐業務系統快速服務的組合和複用。隨着經驗積累,工行也不斷對 Dubbo 進行迭代優化和企業定製,同時圍繞服務也逐步打造了完善的服務生態體系。session
2019 年,工行的微服務體系也正式升級爲工行開放平臺核心銀行系統的關鍵能力之一,助力工行 IT 架構實現真正的分佈式轉型。架構
工行的微服務體系組成以下圖所示:併發
通過工行多年的落地實踐,本文共總結了如下兩方面的最大挑戰:app
本文將先從服務發現方面,來分享一下工行的應對策略及成效。框架
在 Dubbo 中,服務的註冊訂閱及調用是一個標準範式,服務的提供者初始化時註冊服務,服務消費者初始化時訂閱服務並獲取全量提供者列表。而運行期間,服務提供者發生變化時,服務消費者可獲取最新的提供者列表。消費者與提供者之間點對點 RPC 調用,調用過程不經註冊中心。運維
在註冊中心的選擇上,工行在 2014 年就選擇了 Zookeeper。Zookeeper 在業界的各種場景下有大規模的應用,而且支持集羣化部署,節點間數據一致性經過 CP 模式保證。分佈式
在 Zookeeper 內部,Dubbo 會按服務創建不一樣的節點,每一個服務節點下又有 providers、consumers、configurations 及 routers 四個字節點:ide
在線上生產環境,Zookeeper 分數據中心部署了多個集羣,每一個集羣配置了 5 個選舉節點,若干個 Observer 節點。Observer 節點是 Zookeeper3.3.3 版本引入的一個新的節點類型,它不參與選舉,只聽取表決結果,其餘能力則和 Follower 節點相同。Observer 節點有如下幾方面的好處:
工行根據這幾年線上 Zookeeper 的使用心酸血淚史,總結了 Zookeeper 在做爲服務註冊中心時面臨的問題:
綜上,能夠得出的結論是:整體上 Zookeeper 做爲註冊中心仍是比較稱職的,但在更大規模服務量場景下,須要進一步優化。
工行最主要的優化措施包括下面這幾方面:訂閱延遲更新、註冊中心採起 multiple 模式、升級到按節點註冊等。
工行對 Zookeeper 客戶端組件 zkclient 作了優化,把消費者收到事件通知後獲取提供者列表作了一個小的延時。
當 zkclient 收到 childchange 一次性的事件後,installWatch() 經過 EventThread 去恢復對節點的監聽,同時又使用 getChildren() 去讀取節點下的所有子節點獲取提供者列表,並刷新本地服務提供者緩存。這就是前面說的「5050 條數據」問題的根源。
工行在 zkclient 收到 childchange() 的事件後,作了個等待延遲,再讓 installWatch() 去作它原來該作的事情。這個等待過程當中若是服務提供者發生變化,則不產生 childchange 事件。
有人會問,這是否是違背了 zookeeper 的 CP 模型呢,其實並非,zookeeper 服務端的數據是強一致的,消費者也收到了事件通知,只是延後去讀取提供者清單,後面執行 getChildren() 時,讀取到的已是 zookeeper 上的最新數據,因此是沒有問題的。
內部壓測結果顯示,服務提供者大規模上線時,優化前,每一個消費者收到了總計 422 萬個提供者節點的數據量,而延遲 1 秒處理後,這個數據量則變成了 26 萬,childchange 事件次數和網絡流量都變成了原來的 5% 左右,作完這個優化,就能從容應對投產高峯期大量服務的上下線。
工行採納並優化改造了 Dubbo 新版本中 registry-multiple 的 SPI 實現,用於優化多註冊中心場景下的服務訂閱。
Dubbo 中服務消費者原有處理邏輯是這樣:當存在多個註冊中心的時候,消費者按註冊中心對應的 invoker 緩存去篩選提供方,第一個註冊中心對應的緩存中若是沒找到,則去找第二個註冊中心對應的緩存。若是此時第一個註冊中心出現可用性問題,推送給消費者的數據有缺失,甚至爲空,就會影響消費者的這個篩選過程,如出現無提供方的異常、調用負載不均衡等。
而 multiple 註冊中心是把多個註冊中心推送的數據合併後再更新緩存,因此即便單個註冊中心故障,推送了數據不完整或者爲空,只要有其餘任意一個註冊中心的數據使完整的,就不會影響最後合併的數據。
而且,multiple 註冊中心機制也用於異構的註冊中心場景,出現問題能夠隨時把註冊中心下線,這個過程對服務節點的服務調用則徹底透明,比較適合灰度試點或者應急切換。
更進一步,還有額外的收益,消費者端 Reference 對象是比較佔用 JVM 內存,經過 multiple 註冊中心模式,能夠幫消費者端節省一半的 invoker 對象開銷,所以,很是推薦多個註冊中心場景採用 multiple 模式。
工行反向移植 Dubbo2.7 及 Dubbo3.0 的服務發現邏輯,使用「按節點註冊」的服務註冊-發現模型。這裏即配置中心、元數據中心、註冊中心這個鐵三角組合:
這個模型的變化,對於消費者的服務調用則沒有任何影響。消費者端根據元數據中心上「服務節點名稱」與「服務」的關係,以及註冊中心「服務節點名稱」與實際 ip 端口的關係,生成兼容存量模式的服務提供方 invoker 緩存。
壓測結果顯示,按節點註冊可讓註冊中心上的數據量變成原來的 1.68%,這對量就對線上的 Zookeeper 來講毫無壓力,10 萬級別的服務量和 10 萬級別的節點量都可以輕鬆支撐。
將來,工行也但願能有機會走出去,深度參與到社區中,把自身在 Dubbo、Zookeeper 服務端、zkclient 上的好的 feature 貢獻出來,好比除了上面的優化點外,工行還在 Dubbo 上作了 RPC 結果的精細化識別,PAAS 的適配,同端口多協議、自隔離等能力,還在 Zookeeper 上增長了註冊熔斷機制,同時正在研究 Observer 的同步機制避免數據全量同步帶來的一系列問題。
另外,從微服務的發展看,Mesh 已是目前的熱點之一。工行的痛點主要在服務 SDK 版本升級上,Istio 不爭氣,MCP 生死未卜,如何能讓存量 Dubbo 服務平滑過渡到 MESH 架構,目前已經有初步的方案,但還有很是多的技術難關須要克服。
歡迎 Dubbo 有實踐的同窗們一塊兒來探討大規模場景下的問題和心得,共同把 Dubbo 的企業落地作的更好!
更多企業落地實踐內容,可下載雲原生架構白皮書瞭解詳情!