1. 有贊服務(wù)注冊(cè)與發(fā)現(xiàn)架構(gòu)演進(jìn)

        共 10781字,需瀏覽 22分鐘

         ·

        2021-10-28 22:34

        075fbb24176029b9402b375dfd555c49.webp

        點(diǎn)擊關(guān)注“有贊coder

        獲取更多技術(shù)干貨哦~

        6f3ad889358f0107f94fec9d9c320a07.webp作者:飄石

        部門(mén):技術(shù)中臺(tái)/中間件


        一、概述

        近幾年,隨著有贊業(yè)務(wù)的快速發(fā)展,應(yīng)用數(shù)目與實(shí)例規(guī)模在快速地增加。有贊的服務(wù)注冊(cè)與發(fā)現(xiàn)架構(gòu)近幾年也一直在快速平穩(wěn)地演進(jìn),以支撐業(yè)務(wù)的發(fā)展。本文主要介紹有贊近幾年服務(wù)注冊(cè)與發(fā)現(xiàn)架構(gòu)的演進(jìn)過(guò)程。

        有贊的后臺(tái)業(yè)務(wù)應(yīng)用主要是基于 Dubbo 框架開(kāi)發(fā)的,因此,服務(wù)注冊(cè)與發(fā)現(xiàn)的方案也都離不開(kāi)對(duì) Dubbo 服務(wù)模型的支持。近幾年,Dubbo 社區(qū)也一直在演進(jìn)服務(wù)注冊(cè)與發(fā)現(xiàn)解決方案,但有贊的演進(jìn)路線跟 Dubbo 社區(qū)并不相同。有贊根據(jù)內(nèi)部獨(dú)特的歷史背景以及未來(lái)規(guī)劃走出了具有自己特色的演進(jìn)道路。

        本文將分為三個(gè)階段來(lái)介紹近幾年有贊服務(wù)注冊(cè)與發(fā)現(xiàn)架構(gòu)的演進(jìn):接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn),接口級(jí)服務(wù)注冊(cè)與應(yīng)用級(jí)服務(wù)發(fā)現(xiàn),應(yīng)用級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)。為了聚焦,本文主要介紹 Dubbo 應(yīng)用相關(guān)的服務(wù)注冊(cè)與發(fā)現(xiàn),但實(shí)際上有贊的服務(wù)注冊(cè)與發(fā)現(xiàn)方案不僅僅支持 Dubbo 應(yīng)用。

        二、接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)

        2.1?架構(gòu)

        接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn),也是開(kāi)源社區(qū) Dubbo 2.7 版本之前的標(biāo)準(zhǔn)方案,有贊 2018 年 ~ 2019 年期間主要處于這種架構(gòu)階段。架構(gòu)如下圖所示:?068261962dd2dbe1e2f64982be9b3d18.webp模型上與 Dubbo 社區(qū)方案是一致的,注冊(cè)中心我們采用的是 Etcd v3。

        接口級(jí)模型示例:

        [
        {
        "interface":"com.youzan.java.demo.api.HelloService",
        "instances":[
        {
        "ip_address":"10.10.10.10",
        "port":5000,
        "protocol":"dubbo",
        "az":"qa",
        "weight":100,
        "labels":{
        "version":"stable"
        },
        "application":"java-demo",
        "methods":[
        "hello"
        ]
        }
        ]
        },
        {
        "interface":"com.youzan.java.demo.api.EchoService",
        "instances":[
        {
        "ip_address":"10.10.10.10",
        "port":5000,
        "protocol":"dubbo",
        "az":"qa",
        "weight":100,
        "labels":{
        "version":"stable"
        },
        "application":"java-demo",
        "methods":[
        "echo"
        ]
        }
        ]
        }
        ]

        2.2 問(wèn)題

        接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)可以說(shuō)是 Dubbo 框架獨(dú)有的模型,而業(yè)界主流的服務(wù)注冊(cè)與發(fā)現(xiàn)模型都是應(yīng)用級(jí)的,如 K8S、Spring Cloud、Consul 等。相比應(yīng)用級(jí)模型,接口級(jí)模型的主要問(wèn)題是粒度太細(xì),服務(wù)注冊(cè)與發(fā)現(xiàn)的開(kāi)銷(xiāo)太高。根據(jù)有贊的服務(wù)注冊(cè)與發(fā)現(xiàn)數(shù)據(jù)統(tǒng)計(jì),平均每個(gè)應(yīng)用實(shí)例的接口注冊(cè)數(shù)量和訂閱數(shù)量為幾十個(gè),同時(shí),該數(shù)量也在緩慢增長(zhǎng)。粗略估算,在大規(guī)模場(chǎng)景中,接口級(jí)比應(yīng)用級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)的成本要高 1~2 個(gè)數(shù)量級(jí)。接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)的弊端業(yè)界也基本達(dá)成了共識(shí),Dubbo 社區(qū)從 Dubbo 2.7.5 開(kāi)始支持應(yīng)用級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)。

        總結(jié)一下該架構(gòu)存在的痛點(diǎn):

        • 接口級(jí)別的服務(wù)注冊(cè)與發(fā)現(xiàn),大大增加了服務(wù)注冊(cè)與發(fā)現(xiàn)的壓力

        • 接口級(jí)別的服務(wù)注冊(cè)數(shù)據(jù)冗余度過(guò)高,同一個(gè)應(yīng)用實(shí)例的多個(gè)接口之間有大量的重復(fù)數(shù)據(jù);同一個(gè)應(yīng)用的不同實(shí)例之間同樣存在大量冗余數(shù)據(jù)

        • Etcd 作為強(qiáng)一致性的 CP 系統(tǒng),其水平伸縮能力不足,容易成為瓶頸

        • 服務(wù)注冊(cè)與發(fā)現(xiàn)由 SDK 支持,多語(yǔ)言應(yīng)用支持成本高


        三、接口級(jí)服務(wù)注冊(cè)與應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)

        3.1 架構(gòu)

        這一階段服務(wù)注冊(cè)維持不變,服務(wù)發(fā)現(xiàn)轉(zhuǎn)變?yōu)閼?yīng)用級(jí)別,有贊 2020 年期間主要處于這種架構(gòu)階段,該架構(gòu)屬于過(guò)渡階段架構(gòu)。架構(gòu)如下圖所示:?

        babc452283e03a79956cba9fe5f27d6e.webp

        服務(wù)發(fā)現(xiàn)方面主要有以下兩個(gè)變化:

        • 引入了Istio?Pilot 作為服務(wù)發(fā)現(xiàn)中心或中間層。由 Istio Pilot 對(duì)接各個(gè)注冊(cè)中心平臺(tái),抽象并統(tǒng)一服務(wù)發(fā)現(xiàn)模型,屏蔽注冊(cè)中心具體實(shí)現(xiàn)細(xì)節(jié),同時(shí)提升可伸縮能力。

        • 所有消費(fèi)端接入了 Sidecar?Tether,由 Tether 進(jìn)行服務(wù)發(fā)現(xiàn)、請(qǐng)求路由、負(fù)載均衡等,且以應(yīng)用維度進(jìn)行服務(wù)發(fā)現(xiàn)。

        下面將進(jìn)行詳細(xì)介紹。

        3.2應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)解析

        Istio Pilot 抽象并統(tǒng)一了服務(wù)發(fā)現(xiàn)模型,屏蔽掉了注冊(cè)中心具體實(shí)現(xiàn)細(xì)節(jié),使得消費(fèi)端應(yīng)用完全不需要關(guān)注服務(wù)提供端應(yīng)用是如何進(jìn)行服務(wù)注冊(cè)的。Istio Pilot 中間層避免了海量客戶(hù)端直連注冊(cè)中心,大大降低了注冊(cè)中心的壓力;同時(shí) Istio Pilot 是無(wú)狀態(tài)的,可以輕松擴(kuò)縮容,大大提升了可伸縮能力。Istio Pilot 通過(guò) xDS API 向所有 Sidecar 推送服務(wù)發(fā)現(xiàn)、配置等數(shù)據(jù)。服務(wù)發(fā)現(xiàn) API 是 EDS(Endpoint Discovery Service),客戶(hù)端通過(guò)指定 Cluster 名稱(chēng)(這里可以認(rèn)為對(duì)應(yīng)的是應(yīng)用名)列表來(lái)訂閱對(duì)應(yīng)的服務(wù)實(shí)例信息,當(dāng)服務(wù)實(shí)例信息有更新時(shí),Istio Pilot 推送給訂閱的客戶(hù)端。

        我們引入 Istio Pilot 不僅僅是作為服務(wù)發(fā)現(xiàn)中心,同時(shí)也是路由規(guī)則配置中心等。Istio Pilot 實(shí)際上是作為有贊 Service Mesh 的控制面角色來(lái)使用的,我們的數(shù)據(jù)面是自研組件 Tether。由于有贊整體業(yè)務(wù)規(guī)模龐大,以及 Dubbo 模型的復(fù)雜度,是很難直接落地為 Istio 社區(qū)那樣的 Service Mesh 形態(tài)的。因此,我們對(duì) Istio Pilot 進(jìn)行了大量擴(kuò)展與適配,來(lái)滿(mǎn)足內(nèi)部需求,以及逐步演進(jìn)的目標(biāo)。對(duì)于服務(wù)發(fā)現(xiàn)而言,我們擴(kuò)展支持了 Etcd Registry,同時(shí)實(shí)現(xiàn)了接口模型到應(yīng)用模型的轉(zhuǎn)換。為了支持 Dubbo 的服務(wù)發(fā)現(xiàn),使用原有的模型還不夠,因此我們通過(guò)xDS模型的擴(kuò)展字段來(lái)支持 Dubbo 的元數(shù)據(jù)。

        應(yīng)用級(jí)模型示例:

        {
        "application":"java-demo",
        "instances":[
        {
        "ip_address":"10.10.10.10",
        "port":5000,
        "protocol":"dubbo",
        "az":"qa",
        "weight":100,
        "labels":{
        "version":"stable"
        },
        "dubbo_rpc_metadata":[
        {
        "interface":"com.youzan.java.demo.api.HelloService",
        "methods":[
        "hello"
        ]
        },
        {
        "interface":"com.youzan.java.demo.api.EchoService",
        "methods":[
        "echo"
        ]
        }
        ]
        }
        ]
        }

        與前面提到的接口級(jí)模型對(duì)比,應(yīng)用級(jí)模型大大降低了數(shù)據(jù)冗余。

        我們通過(guò)Istio Pilot實(shí)現(xiàn)了應(yīng)用級(jí)的服務(wù)發(fā)現(xiàn),這時(shí)我們面臨一個(gè)問(wèn)題,原先Dubbo框架通過(guò)訪問(wèn)的接口直接進(jìn)行服務(wù)發(fā)現(xiàn),現(xiàn)在需要將訪問(wèn)的接口映射為訪問(wèn)的應(yīng)用,然后以應(yīng)用名進(jìn)行服務(wù)發(fā)現(xiàn)訂閱。

        Istio Pilot會(huì)根據(jù)應(yīng)用級(jí)的注冊(cè)信息在內(nèi)存中構(gòu)建一個(gè)接口到應(yīng)用的反向映射,我們擴(kuò)展了一個(gè)Interface Mapping接口,用于查詢(xún)哪些應(yīng)用暴露了這個(gè)接口。映射信息如下所示:

        [
        {
        "interface":"com.youzan.java.demo.api.HelloService",
        "providers":[
        "java-demo"
        ]
        },
        {
        "interface":"com.youzan.java.demo.api.EchoService",
        "providers":[
        "java-demo"
        ]
        }
        ]

        前面我們提到,由 SDK 進(jìn)行服務(wù)發(fā)現(xiàn),對(duì)于多語(yǔ)言應(yīng)用支持成本較高。在有贊,除了主流的 Dubbo RPC 應(yīng)用,還有 Node Web 應(yīng)用,PHP Web 應(yīng)用,ZanPHP RPC 應(yīng)用,這些應(yīng)用都需要進(jìn)行服務(wù)發(fā)現(xiàn)訪問(wèn)其他后端應(yīng)用。所以,我們將這些基礎(chǔ)能力下沉到 Sidecar Tether,所有消費(fèi)端接入 Tether,由 Tether 進(jìn)行服務(wù)發(fā)現(xiàn)、請(qǐng)求路由、負(fù)載均衡等。接入 Tether 也開(kāi)啟了有贊的 Service Mesh 之路。

        Dubbo 社區(qū)應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)方案中,為了使Dubbo盡可能的兼容和融入業(yè)界已有的應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)解決方案,元數(shù)據(jù)是通過(guò)一種服務(wù)自省的方式來(lái)獲取的。對(duì)于接口到應(yīng)用的映射,解決思路基本都是一致的。

        3.3優(yōu)化

        雖然當(dāng)前的架構(gòu)方案已經(jīng)大大緩解了服務(wù)發(fā)現(xiàn)的壓力,但是仍有幾個(gè)優(yōu)化點(diǎn)可以大大提升性能。下面簡(jiǎn)單介紹一下。

        3.3.1服務(wù)發(fā)現(xiàn)延遲聚合推送

        Dubbo 實(shí)例在啟動(dòng)時(shí),是一個(gè)接口一個(gè)接口注冊(cè)的,因?yàn)槲覀儗⒔涌谧?cè)數(shù)據(jù)轉(zhuǎn)換成了應(yīng)用實(shí)例注冊(cè)數(shù)據(jù),這也就意味著,每注冊(cè)一個(gè)接口,應(yīng)用實(shí)例數(shù)據(jù)(dubbo_rpc_metadata)就會(huì)變動(dòng)。如果每次變動(dòng)就進(jìn)行服務(wù)發(fā)現(xiàn)推送,那成本會(huì)很高,無(wú)論是對(duì)于 Istio Pilot 還是 Tether。根據(jù)統(tǒng)計(jì),一個(gè)實(shí)例一般在幾秒鐘內(nèi)會(huì)完成所有接口的注冊(cè),因此,我們會(huì)對(duì)一段時(shí)間內(nèi)注冊(cè)事件響應(yīng)進(jìn)行延遲處理。比如,如果實(shí)例注冊(cè)數(shù)據(jù)有變動(dòng),延遲 3s 再推送,如果 3s 時(shí)間到達(dá)之前期間又有變動(dòng),再延遲 3s,最長(zhǎng)不超過(guò) 10s。該方案大概率地把一個(gè)實(shí)例的多個(gè)接口注冊(cè)事件聚合成了一次推送,同時(shí),應(yīng)用發(fā)布過(guò)程一般是分批次進(jìn)行的,每個(gè)批次會(huì)有多個(gè)實(shí)例同時(shí)啟動(dòng),該方案也有很大概率把多個(gè)實(shí)例的注冊(cè)事件聚合成一次推送。

        3.3.2服務(wù)發(fā)現(xiàn)預(yù)加載

        最初 Tether 的服務(wù)發(fā)現(xiàn)是延遲加載的,即當(dāng)應(yīng)用的請(qǐng)求到達(dá) Tether 后,如果還沒(méi)有訂閱過(guò)目標(biāo)訪問(wèn)應(yīng)用,進(jìn)行服務(wù)發(fā)現(xiàn)訂閱。剛啟動(dòng)的時(shí)候,訪問(wèn)不同應(yīng)用的請(qǐng)求會(huì)陸陸續(xù)續(xù)到來(lái),每個(gè)請(qǐng)求訪問(wèn)一個(gè)本地服務(wù)發(fā)現(xiàn)數(shù)據(jù)不存在的應(yīng)用時(shí),就需要更新服務(wù)發(fā)現(xiàn)訂閱列表,發(fā)起新的 EDS 訂閱請(qǐng)求,會(huì)加大Istio Pilot 的負(fù)載,同時(shí)會(huì)一定程度增加請(qǐng)求的 RT。一個(gè)應(yīng)用的需要訪問(wèn)的其他應(yīng)用的列表是比較穩(wěn)定的,我們稱(chēng)之為服務(wù)依賴(lài)列表。我們通過(guò) Tether 定時(shí)上報(bào)最近一段時(shí)間(如 30 分鐘)訪問(wèn)過(guò)的應(yīng)用列表到 Istio Pilot 來(lái)實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)預(yù)加載。Tether 啟動(dòng)初始化階段拉取該應(yīng)用最近訪問(wèn)過(guò)的應(yīng)用列表,然后一次性的完成服務(wù)發(fā)現(xiàn)訂閱,大大降低了應(yīng)用剛啟動(dòng)時(shí)首次請(qǐng)求的 RT。

        3.3.3客戶(hù)端接口與應(yīng)用映射關(guān)系構(gòu)建

        前面我們討論過(guò),請(qǐng)求到來(lái)時(shí),我們需要根據(jù)接口查詢(xún)對(duì)應(yīng)的應(yīng)用,那是不是每個(gè)接口的首次請(qǐng)求都需要通過(guò) Istio Pilot 的 Interface Mapping 接口查詢(xún)呢?其實(shí)沒(méi)必要的,當(dāng)我們拿到一個(gè)應(yīng)用的服務(wù)發(fā)現(xiàn)數(shù)據(jù)時(shí),本地可以根據(jù)該應(yīng)用的服務(wù)元數(shù)據(jù)構(gòu)建出來(lái)所有的接口到應(yīng)用的映射關(guān)系。根據(jù)局部性原理,如果一個(gè)應(yīng)用訪問(wèn)了某個(gè)應(yīng)用的一個(gè)接口,短時(shí)間內(nèi)大概率也會(huì)訪問(wèn)該應(yīng)用的其他接口。通過(guò)該優(yōu)化,實(shí)際上只會(huì)發(fā)生很少的 Interface Mapping 查詢(xún)請(qǐng)求。當(dāng)然,應(yīng)用的服務(wù)元數(shù)據(jù)都是在變化的,因此,我們也需要定期的異步刷新,異步刷新時(shí),只會(huì)刷新最近有請(qǐng)求的接口,且我們實(shí)現(xiàn)了批量處理的接口以提升性能。對(duì)于一段時(shí)間內(nèi)沒(méi)有訪問(wèn)過(guò)的接口,當(dāng)新請(qǐng)求到來(lái)時(shí),會(huì)嘗試同步去請(qǐng)求 Interface Mapping。

        3.3.4接口元數(shù)據(jù)聚合分組

        一般來(lái)說(shuō),真實(shí)生產(chǎn)環(huán)境中,一個(gè)應(yīng)用的大部分實(shí)例的服務(wù)元數(shù)據(jù)是相同的,那么也就沒(méi)必要為每個(gè)應(yīng)用實(shí)例關(guān)聯(lián)一份完整的元數(shù)據(jù)。在有贊的場(chǎng)景中,每個(gè)機(jī)房,每個(gè)應(yīng)用一般最多有 2 個(gè)版本的服務(wù)元數(shù)據(jù),主要出現(xiàn)在發(fā)布過(guò)程中,如普通滾動(dòng)發(fā)布、灰度/藍(lán)綠發(fā)布。因此,我們可以進(jìn)行相應(yīng)的優(yōu)化。Istio Pilot 在推送服務(wù)發(fā)現(xiàn)數(shù)據(jù)前會(huì)對(duì)應(yīng)用實(shí)例服務(wù)元數(shù)據(jù)進(jìn)行聚合分組,以減少網(wǎng)絡(luò)帶寬 IO,以及客戶(hù)端解析開(kāi)銷(xiāo)等。聚合示例:

        {
        "application":"java-demo",
        "instances":[
        {
        "ip_address":"10.10.10.10",
        "port":5000,
        "protocol":"dubbo",
        "az":"qa",
        "weight":100,
        "labels":{
        "version":"stable"
        },
        "dubbo_rpc_metadata":{
        "type":"metadata",
        "data":[
        {
        "interface":"com.youzan.java.demo.api.HelloService",
        "methods":[
        "hello"
        ]
        },
        {
        "interface":"com.youzan.java.demo.api.EchoService",
        "methods":[
        "echo"
        ]
        }
        ]
        }
        },
        {
        "ip_address":"10.10.10.20",
        "port":5000,
        "protocol":"dubbo",
        "az":"qa",
        "weight":100,
        "labels":{
        "version":"stable"
        },
        "dubbo_rpc_metadata":{
        "type":"metadata_reference",
        "data":{
        "ip_address":"10.10.10.10",
        "port":5000
        }
        }
        }
        ]
        }

        應(yīng)用實(shí)例10.10.10.10:500010.10.10.20:5000服務(wù)元數(shù)據(jù)完全一致,因此10.10.10.20:5000dubbo_rpc_metadata中并不需要保存完整的服務(wù)元數(shù)據(jù)信息,僅需要保存一個(gè)引用的應(yīng)用實(shí)例信息即可??蛻?hù)端在構(gòu)建時(shí),會(huì)根據(jù)引用關(guān)系,關(guān)聯(lián)到正確的服務(wù)元數(shù)據(jù)信息。

        該優(yōu)化將服務(wù)發(fā)現(xiàn)推送的網(wǎng)絡(luò) IO 降低到原來(lái)的 30% 以下。

        3.4 問(wèn)題

        雖然該架構(gòu)解決了服務(wù)發(fā)現(xiàn)的一些問(wèn)題,但仍然有以下問(wèn)題:

        • 應(yīng)用與注冊(cè)中心耦合,多語(yǔ)言服務(wù)注冊(cè)支持成本高

        • 服務(wù)注冊(cè)數(shù)據(jù)維度過(guò)細(xì),注冊(cè)、健康檢查開(kāi)銷(xiāo)與復(fù)雜度都較高

        • 服務(wù)注冊(cè)數(shù)據(jù)冗余度過(guò)高


        四、應(yīng)用級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)

        4.1架構(gòu)

        這是有贊確定的長(zhǎng)期架構(gòu),從 2021 年開(kāi)始,有贊轉(zhuǎn)換到該架構(gòu)。架構(gòu)圖如下所示:?

        469cd711335ee6d59d01c83aec6ba6d1.webp此架構(gòu)服務(wù)與前一階段架構(gòu)相比,客戶(hù)端服務(wù)發(fā)現(xiàn)沒(méi)有變化,只有服務(wù)注冊(cè)有變化。

        應(yīng)用服務(wù)注冊(cè)通過(guò) Sidecar Tether 來(lái)完成,可以主動(dòng)調(diào)用 Tether 服務(wù)注冊(cè)接口,也可以由 Tether 主動(dòng)獲取實(shí)例服務(wù)注冊(cè)信息進(jìn)行注冊(cè),目前我們優(yōu)先支持了前一種方式。

        Dubbo 啟動(dòng)時(shí),等待所有接口暴露完成,聚合成應(yīng)用級(jí)的實(shí)例信息,發(fā)起一次服務(wù)注冊(cè)請(qǐng)求到 Tether,Tether 判斷應(yīng)用部署環(huán)境,向 Istio Pilot 發(fā)起相應(yīng)的注冊(cè)請(qǐng)求。對(duì)于 VM 部署應(yīng)用,需要注冊(cè)完整的信息;對(duì)于 K8S 部署應(yīng)用,僅需要注冊(cè)服務(wù)元數(shù)據(jù)信息即可,其他實(shí)例信息、標(biāo)簽等可以由 Istio Pilot 根據(jù) K8S Service 以及 Pod 信息獲得。有贊業(yè)務(wù)應(yīng)用基本都實(shí)現(xiàn)了 K8S 部署,所以,這里只介紹 K8S 部署應(yīng)用的注冊(cè)流程。Istio Pilot 會(huì)將注冊(cè)請(qǐng)求中的服務(wù)元數(shù)據(jù),以 CRD 的方式存儲(chǔ)到K8S中。Istio Pilot 在服務(wù)發(fā)現(xiàn)時(shí),會(huì)根據(jù) Service/Endpoints/Pod/ServiceMetadata 信息,生成完整的服務(wù)發(fā)現(xiàn)數(shù)據(jù)。此過(guò)程中,Istio Pilot 與客戶(hù)端之間的服務(wù)發(fā)現(xiàn)數(shù)據(jù)模型完全沒(méi)有變化,因此,客戶(hù)端對(duì)于該服務(wù)注冊(cè)的變動(dòng)是完全無(wú)感知的。這樣,我們又平滑地演進(jìn)到了新的架構(gòu)。

        可能有人會(huì)考慮到運(yùn)行時(shí)動(dòng)態(tài)暴露接口的場(chǎng)景,會(huì)對(duì)該注冊(cè)方案有影響。我們通過(guò)統(tǒng)計(jì),發(fā)現(xiàn)沒(méi)有該使用場(chǎng)景,所有應(yīng)用都可以在啟動(dòng)的時(shí)候確定需要注冊(cè)的接口信息。同時(shí),我們通過(guò) Dubbo 框架約束,一個(gè)實(shí)例的注冊(cè)數(shù)據(jù)一旦完成服務(wù)注冊(cè)后是不能變化的。如果后續(xù)確實(shí)出現(xiàn)了該場(chǎng)景,我們后續(xù)會(huì)再對(duì)該場(chǎng)景進(jìn)行支持,或者先使用老的注冊(cè)方式,畢竟 Istio Pilot 可以輕松支持多種注冊(cè)中心,而客戶(hù)端無(wú)感知。

        4.2服務(wù)元數(shù)據(jù)管理

        服務(wù)元數(shù)據(jù)管理這一塊有些細(xì)節(jié)值得介紹一下。

        初步考慮,可以將每個(gè)實(shí)例的服務(wù)元數(shù)據(jù)寫(xiě)到到對(duì)應(yīng)的 K8S Pod 注解里。該方案每個(gè)實(shí)例注冊(cè)都需要寫(xiě)一份完整的服務(wù)元數(shù)據(jù),但事實(shí)上大部分實(shí)例的服務(wù)元數(shù)據(jù)都是相同的,或僅存在非常少數(shù)的幾個(gè)版本。因此,存在極大的優(yōu)化空間。

        這里優(yōu)化的主要思路是,當(dāng)某個(gè)實(shí)例注冊(cè)元數(shù)據(jù)時(shí),可以先檢查一下對(duì)應(yīng)版本的服務(wù)元數(shù)據(jù)是否已存在,如果已存在,則不需要再寫(xiě)入了。那如何確定服務(wù)元數(shù)據(jù)的版本呢?根據(jù) K8S Pod 的 Labels。因?yàn)橄嗤?Labels 的 Pod 實(shí)例的鏡像、配置等都是相同的,則他們的服務(wù)能力、服務(wù)元數(shù)據(jù)也必定一致。為什么呢?因?yàn)?,Labels 相同的 Pod 都是由同一個(gè) ReplicaSet 創(chuàng)建的,按照云原生的理念它們的服務(wù)能力必定是一致的。既然每個(gè) ReplicaSet 產(chǎn)生的 Pod 它們的服務(wù)注冊(cè)元數(shù)據(jù)是一致的,那是不是該 ReplicaSet 創(chuàng)建的 Pod 的元數(shù)據(jù)可以寫(xiě)入到 ReplicaSet 的注解里。這個(gè)方案是沒(méi)問(wèn)題的。但是我們基于 K8S API Server 權(quán)限最小化的考慮,沒(méi)有采用該方案,畢竟 ReplicaSet 是 K8S 核心資源,最好不要放開(kāi)權(quán)限。我們采用 CRD 單獨(dú)保存元數(shù)據(jù)的方式,即 ServiceMetadata CRD。一個(gè) ServiceMetadata 資源里,管理一個(gè)應(yīng)用的多個(gè)版本的服務(wù)元數(shù)據(jù)。

        服務(wù)元數(shù)據(jù)模型如下所示:

        {
        "subset_metadata":[
        {
        "subset_id":"1",
        "selector":{
        "pod-template-hash":"f845f5775",
        "app":"java-demo",
        "zone":"qa"
        },
        "dubbo_rpc_metadata":{
        "interfaces":[
        {
        "name":"com.youzan.java.demo.api.EchoService",
        "methods":[
        "echo"
        ]
        }
        ]
        }
        },
        {
        "subset_id":"2",
        "selector":{
        "pod-template-hash":"67bd5c9db9",
        "app":"java-demo",
        "zone":"qa"
        },
        "dubbo_rpc_metadata":{
        "interfaces":[
        {
        "name":"com.youzan.java.demo.api.EchoService",
        "methods":[
        "echo"
        ]
        },
        {
        "name":"com.youzan.java.demo.api.HelloService",
        "methods":[
        "hello"
        ]
        }
        ]
        }
        }
        ]
        }

        當(dāng)注冊(cè)請(qǐng)求達(dá)到 Istio Pilot 時(shí),Istio Pilot 通過(guò)本地 K8S Client Cache 查詢(xún) ServiceMetadata 中是否包含對(duì)應(yīng)版本的服務(wù)元數(shù)據(jù)(通過(guò)對(duì)應(yīng)實(shí)例的 Pod Labels 與 ServiceMetadata 所有版本的 Selector 進(jìn)行匹配),如果存在,直接返回即可。不存在,向 K8S API Server 發(fā)起請(qǐng)求更新 ServiceMetadata,寫(xiě)入對(duì)應(yīng)版本的服務(wù)元數(shù)據(jù)。如果 K8S API Server 返回版本沖突錯(cuò)誤,說(shuō)明有其他注冊(cè)請(qǐng)求修改了 ServiceMetadata,則需要客戶(hù)端重試,重試時(shí)大概率會(huì)通過(guò) K8S Client Cache 發(fā)現(xiàn)對(duì)應(yīng)版本的服務(wù)元數(shù)據(jù)已經(jīng)存在。這里我們可以發(fā)現(xiàn),通過(guò)該優(yōu)化,一個(gè) ReplicaSet 下的所有 Pod 實(shí)例只需要寫(xiě)一次服務(wù)元數(shù)據(jù),即使有寫(xiě)沖突,概率也是很低的。并且,一個(gè) Deployment 創(chuàng)建新版本的實(shí)例集時(shí),都是會(huì)分多個(gè)批次創(chuàng)建新的實(shí)例集的 Pod 的,取決于 MaxSurge 參數(shù),沖突也僅會(huì)出現(xiàn)在第一個(gè)批次。由于大部分場(chǎng)景都不需要直接跟 K8S API Server 直接交互,大大降低了服務(wù)注冊(cè)的開(kāi)銷(xiāo)。

        老的版本的元數(shù)據(jù)如何刪除呢?因?yàn)槊總€(gè)版本的元數(shù)據(jù)和 ReplicaSet 是一一對(duì)應(yīng)的,所以,只要實(shí)現(xiàn)一個(gè) ReplicaSet 的自定義 Controller,當(dāng) ReplicaSet 對(duì)象被刪除時(shí),刪除對(duì)應(yīng)的版本的元數(shù)據(jù)即可。關(guān)聯(lián)關(guān)系我們采用 K8S 的 Selector 機(jī)制來(lái)處理。

        4.3多機(jī)房服務(wù)發(fā)現(xiàn)

        請(qǐng)求路由時(shí),我們一般都有本機(jī)房路由優(yōu)先原則,即如果本機(jī)房?jī)?nèi)有對(duì)應(yīng)的服務(wù)實(shí)例,請(qǐng)求路由到本機(jī)房的實(shí)例。一般來(lái)說(shuō),每個(gè)機(jī)房?jī)?nèi)的應(yīng)用部署是完備的,很少需要進(jìn)行跨機(jī)房訪問(wèn)。如果有比較大的故障,一般也會(huì)切掉整個(gè)機(jī)房的流量到其他機(jī)房。但也有如下特殊情況:

        • 內(nèi)部控制臺(tái)應(yīng)用,無(wú)需多機(jī)房容災(zāi),只部署在一個(gè)機(jī)房;

        • 大數(shù)據(jù)應(yīng)用,成本太高,也有很多離線計(jì)算服務(wù)不需要高可用,基于成本考慮只部署在一個(gè)機(jī)房;

        • 某個(gè)機(jī)房應(yīng)用實(shí)例異常,或突發(fā)流量不均衡等,需要調(diào)度部分流量或全部流量到其他機(jī)房;

        • 機(jī)房遷移時(shí),短期內(nèi)無(wú)法全量部署所有應(yīng)用,需要有能力將部分應(yīng)用流量調(diào)度到其他機(jī)房。

        多機(jī)房服務(wù)發(fā)現(xiàn)的支持,一般有三種思路:

        • 注冊(cè)中心層支持,也就是注冊(cè)中心包含所有機(jī)房實(shí)例的注冊(cè)數(shù)據(jù),實(shí)現(xiàn)方案是實(shí)例啟動(dòng)的時(shí)候注冊(cè)到所有注冊(cè)中心。該方案有不具備可伸縮性,注冊(cè)中心很容易出現(xiàn)瓶頸。

        • 中間層支持,如 Istio Pilot,監(jiān)聽(tīng)所有機(jī)房的注冊(cè)中心。同樣不具備可伸縮性。

        • 客戶(hù)端支持,如 Tether,監(jiān)聽(tīng)所有機(jī)房的注冊(cè)中心或中間層,因?yàn)槊總€(gè)應(yīng)用需要訂閱的應(yīng)用數(shù)目相對(duì)是有限的,所以可伸縮性方面沒(méi)有瓶頸。

        有贊的多機(jī)房服務(wù)發(fā)現(xiàn)采用的是客戶(hù)端支持方案。架構(gòu)圖如下所示:?

        83a07e27011c488cc1bedf7b7a2911a6.webp

        應(yīng)用服務(wù)注冊(cè)只注冊(cè)到本機(jī)房注冊(cè)中心,Istio Pilot 只對(duì)接本機(jī)房注冊(cè)中心,Tether 對(duì)接多個(gè)機(jī)房的 Istio Pilot,默認(rèn)情況下只訪問(wèn)本機(jī)房 Istio Pilot,當(dāng)因本機(jī)房應(yīng)用實(shí)例不存在或異常,需要將部分流量或全部流量切至其他機(jī)房實(shí)例時(shí),Tether 再訪問(wèn)其他機(jī)房 Istio Pilot,獲取其他機(jī)房的服務(wù)實(shí)例,進(jìn)行后續(xù)的路由調(diào)度。該方案即實(shí)現(xiàn)了多機(jī)房服務(wù)發(fā)現(xiàn)的可伸縮能力,也避免了大部分場(chǎng)景下不必要的服務(wù)發(fā)現(xiàn)開(kāi)銷(xiāo)。

        、總結(jié)

        本文介紹了有贊近幾年服務(wù)注冊(cè)與發(fā)現(xiàn)架構(gòu)演進(jìn)過(guò)程,主要包括三個(gè)階段:接口級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)、接口級(jí)服務(wù)注冊(cè)與應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)、應(yīng)用級(jí)服務(wù)注冊(cè)與發(fā)現(xiàn)。雖然這期間整體架構(gòu)變化比較大,但是我們做到了平穩(wěn)、平滑地演進(jìn),期間上層業(yè)務(wù)應(yīng)用無(wú)感知,所有功能由 Dubbo 框架、Service Mesh 等基礎(chǔ)組件來(lái)支持。雖然,有贊的演進(jìn)之路不一定適合其他公司場(chǎng)景,但也希望能為大家提供一種思路。

        感謝閱讀。

        參考資料
        • 阿里技術(shù)專(zhuān)家詳解 Dubbo 實(shí)踐、演進(jìn)及未來(lái)規(guī)劃,https://www.infoq.cn/article/IwZCAp3jo_H5fJFbWOZu 。

        • Dubbo 邁出云原生重要一步:應(yīng)用級(jí)服務(wù)發(fā)現(xiàn)解析,https://www.infoq.cn/article/GUvBbu5Mbv5uXfj1lLrL 。

        • 有贊灰度發(fā)布與藍(lán)綠發(fā)布實(shí)踐,https://tech.youzan.com/gray-deloyments-and-blue-green-deployments-practices-in-youzan/?。

        • Service Mesh在有贊的實(shí)踐與發(fā)展,https://tech.youzan.com/service-meshzai-you-zan-de-shi-jian-yu-fa-zhan/?。

        • Zan PHP Framework,http://zanphp.io/?。


        招募優(yōu)秀的你加入??





        ????
        瀏覽 35
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 国产69精品久久极品美女夜本色 | 操逼网站一区 | 男生操女生国产 | 刘亦菲张开双腿流白 | 豆花视频精品 |