国产精品久久99,51久久成人国产精品麻豆,亚洲欧洲免费三级网站,最近中文字幕mv,重口老太大和小伙乱

首頁(yè)>娛樂(lè) > 正文

深度剖析線上應(yīng)用節(jié)點(diǎn)流量隔離技術(shù) 天天速看料

2023-06-24 09:00:39來(lái)源:架構(gòu)師社區(qū)

為什么要做流量隔離

Aliware


(相關(guān)資料圖)

源于一個(gè) EDAS 客戶遇到的棘手情況:他們線上的一個(gè) Pod CPU 指標(biāo)異常,為了進(jìn)一步診斷問(wèn)題,客戶希望在不重建此 Pod 的情況下保留現(xiàn)場(chǎng),但診斷期間流量還會(huì)經(jīng)過(guò)這個(gè)異常 Pod,導(dǎo)致影響服務(wù)質(zhì)量,于是詢問(wèn)我們有沒(méi)有辦法可以把流入異常節(jié)點(diǎn)的流量摘除掉,形成一個(gè)隔離的診斷環(huán)境。經(jīng)診斷后,如果異常可以修復(fù),待修復(fù)完成后,再解除流量隔離,節(jié)點(diǎn)恢復(fù)正常工作。 除了在診斷場(chǎng)景需要對(duì)所有輸入流量進(jìn)行隔離外,在一些線上演練中還需對(duì)特定流量進(jìn)行隔離以實(shí)現(xiàn)模擬演練效果。面對(duì)這類流量隔離問(wèn)題時(shí),我們首先考慮的是全鏈路流量控制。目前,EDAS 上的全鏈路流控能夠在不重啟應(yīng)用節(jié)點(diǎn)的情況下控制流量走向。然而,全鏈路流控僅能控制微服務(wù)框架流量,無(wú)法滿足隔離所有或特定流量的需求。 為此,我們進(jìn)行了深入研究,實(shí)現(xiàn)了一套開(kāi)箱即用的流量隔離工具,能夠動(dòng)態(tài)隔離特定流量,并在隔離后可隨時(shí)恢復(fù),以滿足各種場(chǎng)景下的流量隔離需求。

隔離哪些流量

Aliware

流量隔離的目的是阻斷應(yīng)用節(jié)點(diǎn)的流入流量,首先明確下微服務(wù)應(yīng)用節(jié)點(diǎn)流入的流量有哪些。 流入微服務(wù)應(yīng)用節(jié)點(diǎn)的流量大致可以分為兩大類:服務(wù)流量、事件流量。以常見(jiàn)的微服務(wù)應(yīng)用為例,其流量組成如下圖所示。 服務(wù)流量指一個(gè)微服務(wù)應(yīng)用的所有節(jié)點(diǎn)作為一個(gè)網(wǎng)絡(luò)實(shí)體,對(duì)外提供一組服務(wù),被其他系統(tǒng)、服務(wù)或用戶發(fā)起請(qǐng)求產(chǎn)生的調(diào)用。對(duì)于服務(wù)流量,節(jié)點(diǎn)本身不直接決定流量的流入與否,而是由一套服務(wù)注冊(cè)與發(fā)現(xiàn)機(jī)制維護(hù)流量路徑的邏輯關(guān)系。節(jié)點(diǎn)經(jīng)過(guò)注冊(cè),成為服務(wù)的一個(gè)端點(diǎn)。調(diào)用方對(duì)服務(wù)發(fā)起請(qǐng)求時(shí),被調(diào)用方是服務(wù)的邏輯地址,經(jīng)過(guò)轉(zhuǎn)發(fā)和地址轉(zhuǎn)換,請(qǐng)求被路由到服務(wù)端點(diǎn)的實(shí)體節(jié)點(diǎn)。隔離服務(wù)流量的一個(gè)可選方案是破壞服務(wù)調(diào)用的通信連接,但這種方法勢(shì)必會(huì)影響服務(wù)質(zhì)量。在保持服務(wù)整體功能正常運(yùn)行的同時(shí),一個(gè)更優(yōu)雅的方案是破壞服務(wù)與實(shí)體節(jié)點(diǎn)之間的映射關(guān)系。這樣,在路由過(guò)程中,流量將按照預(yù)期避開(kāi)特定節(jié)點(diǎn),而被引導(dǎo)至其他節(jié)點(diǎn)。服務(wù)流量主要涵蓋了 K8s Service 以及使用 Nacos 等注冊(cè)中心發(fā)布的由 Spring Cloud、Dubbo 等微服務(wù)框架構(gòu)建的服務(wù)。 事件流量指應(yīng)用內(nèi)部的事件驅(qū)動(dòng)架構(gòu)產(chǎn)生的流量,包括由中間件傳遞至應(yīng)用節(jié)點(diǎn)的事件或消息,這類通信通常是異步的,例如來(lái)自消息隊(duì)列 RocketMQ 的消息流量,來(lái)自調(diào)度框架 SchedulerX 觸發(fā)調(diào)度的事件流量。中間件和應(yīng)用節(jié)點(diǎn)之間通常遵循 client-server 通信,因此可以考慮通過(guò)破壞通信連接來(lái)隔離中間件發(fā)來(lái)的消息或事件流量。

服務(wù)流量隔離

Aliware

K8s Service
對(duì)于使用 K8s Service 暴露服務(wù)的應(yīng)用,Service 聲明的服務(wù)與應(yīng)用 Pod 之間的映射關(guān)系由 Endpoints 對(duì)象維護(hù)。Endpoints 對(duì)象的 subsets 字段表示 Serivce 的一組端點(diǎn),每個(gè)端點(diǎn)代表一個(gè)應(yīng)用 Pod 的網(wǎng)絡(luò)地址,即一個(gè)實(shí)際提供服務(wù)的 Pod 實(shí)例。subsets 字段包含了這些端點(diǎn)的詳細(xì)信息,如 IP 地址和端口。Endpoints 控制器通過(guò) API Server 監(jiān)聽(tīng) Pod 的變更情況,并隨后同步更新 Endpoints 的端點(diǎn)列表。因此,要隔離 K8s Service 的流量,需要破壞 Endpoints 對(duì) Pod 的指向,將待隔離的 Pod 網(wǎng)絡(luò)地址從 Endpoints 的端點(diǎn)列表中移除。同時(shí),需要通過(guò) Informer 機(jī)制監(jiān)聽(tīng) Endpoints 對(duì)象的變化,以保證 Endpoints 在后續(xù)變更或控制器 Reconcile 過(guò)程中也能維持預(yù)期狀態(tài)。 Dubbo對(duì)于使用注冊(cè)中心暴露服務(wù)的應(yīng)用,注冊(cè)中心負(fù)責(zé)管理服務(wù)節(jié)點(diǎn)。只要注冊(cè)關(guān)系存在且應(yīng)用節(jié)點(diǎn)存活,注冊(cè)中心會(huì)將流量調(diào)度到該應(yīng)用節(jié)點(diǎn)。而破壞服務(wù)注冊(cè)關(guān)系的操作被稱為服務(wù)注銷,應(yīng)用節(jié)點(diǎn)進(jìn)行服務(wù)注銷之后,注冊(cè)中心便不會(huì)將流量導(dǎo)入到注銷節(jié)點(diǎn),也就形成了流量隔離。

要實(shí)現(xiàn) Dubbo 微服務(wù)的動(dòng)態(tài)注銷,首先需要從源碼級(jí)別了解 Dubbo 服務(wù)注冊(cè)原理 。以 Dubbo 2.7.0 為例,其服務(wù)注冊(cè)模塊的大致結(jié)構(gòu)如下:

Dubbo 應(yīng)用中存在一個(gè) AbstractRegistryFactory 單例,負(fù)責(zé)注冊(cè)中心 Registry 的容器初始化。類屬性 REGISTRIES 維護(hù)了微服務(wù)列表與注冊(cè)中心實(shí)例的映射關(guān)系。

AbstractRegistry 實(shí)現(xiàn)了 Registry 接口,作為一個(gè)模板,實(shí)現(xiàn)了特定的公共方法,如服務(wù)注冊(cè)(register)、服務(wù)注銷(unregister)等。它還維護(hù)了已注冊(cè)服務(wù) URL 列表。

FailbackRegistry 基于 AbstractRegistry,提供了失敗重試機(jī)制。同時(shí),它提供了注冊(cè)中心的 doRegister 和 doUnregister 抽象方法。當(dāng)執(zhí)行 register/unregister 時(shí),會(huì)調(diào)用 doRegister/doUnregister 方法。

注冊(cè)中心(如 NacosRegistry、RedisRegistry)實(shí)現(xiàn)了具體的服務(wù)注冊(cè)(doRegister)和服務(wù)注銷(doUnregister)邏輯。

由源碼可見(jiàn),Dubbo 的服務(wù)注冊(cè)模塊已經(jīng)內(nèi)置了可動(dòng)態(tài)注銷/重注冊(cè)服務(wù)的方法。因此, Dubbo 微服務(wù)隔離可通過(guò)主動(dòng)觸發(fā)其注冊(cè)中心對(duì)象的服務(wù)注銷方法來(lái)實(shí)現(xiàn)。同理,如果需要恢復(fù)服務(wù)節(jié)點(diǎn),主動(dòng)觸發(fā)服務(wù)注冊(cè)方法,更新注冊(cè)中心的服務(wù)映射關(guān)系。

在確定「觸發(fā)注冊(cè)中心對(duì)象的服務(wù)注銷方法」這一技術(shù)方向之后,需要解決如何獲取對(duì)象和觸發(fā)方法這兩個(gè)問(wèn)題。在 Java 環(huán)境中,我們很容易想到使用 Agent 技術(shù)對(duì)進(jìn)程行為進(jìn)行干預(yù)。然而,常規(guī)的基于字節(jié)碼埋點(diǎn)的 Agent 無(wú)法滿足隨時(shí)啟用的需求,因?yàn)樗蕾囉趹?yīng)用代碼的具體執(zhí)行路徑。只有當(dāng)執(zhí)行路徑觸及埋點(diǎn)時(shí),Agent 代碼才會(huì)被觸發(fā),從而從上下文中獲取對(duì)象并通過(guò)反射調(diào)用相關(guān)方法。然而,與注冊(cè)中心相關(guān)的埋點(diǎn)通常設(shè)置在程序啟動(dòng)初期,此時(shí)會(huì)執(zhí)行注冊(cè)中心初始化、服務(wù)注冊(cè)等操作,比較容易找到合適的埋點(diǎn)。在程序?qū)ν馓峁┓?wù)期間,程序主動(dòng)發(fā)起的注冊(cè)中心操作較少,因此很難找到合適的埋點(diǎn)來(lái)獲取預(yù)期的上下文。在需要隔離應(yīng)用流量時(shí),此時(shí)動(dòng)態(tài)掛入 Agent,由于執(zhí)行路徑中沒(méi)有能獲取注冊(cè)中心上下文的埋點(diǎn),Agent 代碼將無(wú)法生效。 因此,我們需要一個(gè)能夠主動(dòng)獲取對(duì)象并觸發(fā)對(duì)象方法的即開(kāi)即用的 Agent 工具。在這里,我們引入了 JVMTI 技術(shù)。JVMTI(JVM Tool Interface)是一種虛擬機(jī)提供的原生編程接口,允許開(kāi)發(fā)人員創(chuàng)建 Agent 以探查 JVM 內(nèi)部的運(yùn)行狀態(tài),甚至控制 JVM 應(yīng)用程序的執(zhí)行。JVMTI 能夠從 Java 堆中獲取特定類和對(duì)象信息,然后通過(guò)反射觸發(fā)方法,完美地滿足了我們的需求。 由于 JVMTI 是一套 JVM 原生編程接口,需要使用 C/C++ 進(jìn)行編寫(xiě)。編譯后的產(chǎn)物是動(dòng)態(tài)鏈接庫(kù)(.so 或 .dll 文件)。Java 運(yùn)行環(huán)境通過(guò) JNI(Java Native Interface)與 JVMTI 進(jìn)行交互。整體作為一個(gè) Java Agent,通過(guò) Attach API 動(dòng)態(tài)地掛載到目標(biāo) JVM 中。 得益于 JVMTI Agent 的強(qiáng)大功能,我們能夠在 Java 應(yīng)用內(nèi)相對(duì)簡(jiǎn)便地實(shí)施某些控制邏輯。為實(shí)現(xiàn) Dubbo 服務(wù)流量隔離,首先需要獲取 AbstractRegistryFactory 類的靜態(tài)屬性 REGISTRIES,它包含應(yīng)用當(dāng)前已注冊(cè)服務(wù)的服務(wù)列表以及相應(yīng)的注冊(cè)中心 Registry 實(shí)例。對(duì)于特定的微服務(wù),僅需調(diào)用其注冊(cè)中心 Registry 的 register/unregister 方法,便可實(shí)現(xiàn)服務(wù)的動(dòng)態(tài)摘除和恢復(fù)。這一方案直接在較高抽象層級(jí)上操作,而無(wú)需依賴具體的注冊(cè)中心 Registry 實(shí)現(xiàn)類,使其兼容所有注冊(cè)中心。 Spring CLoudSpring Cloud 服務(wù)流量隔離方法類似于 Dubbo,在了解 Spring Cloud 服務(wù)注冊(cè)原理后,獲得服務(wù)注冊(cè)/注銷方法路徑,然后通過(guò) JVMTI 干預(yù)應(yīng)用的服務(wù)注冊(cè)/注銷行為。 Spring Cloud 的服務(wù)注冊(cè)原理較為簡(jiǎn)單。在 Spring 容器啟動(dòng)時(shí),AbstractAutoServiceRegistration 監(jiān)聽(tīng)啟動(dòng)事件,并調(diào)用 ServiceRegistry 的 register 方法將 Registration(服務(wù)實(shí)例數(shù)據(jù))注冊(cè)到注冊(cè)中心。例如,Nacos服務(wù)注冊(cè)類 NacosServiceRegistry 實(shí)現(xiàn)了 ServiceRegistry 接口,通過(guò)重載 register/deregister 方法完成服務(wù)在注冊(cè)中心的注冊(cè)和注銷。
// 服務(wù)注冊(cè)類public abstract class AbstractAutoServiceRegistration...{      // 注冊(cè)中心實(shí)例private final ServiceRegistry serviceRegistry;// 服務(wù)注冊(cè)protected void register() {this.serviceRegistry.register(getRegistration());}// 服務(wù)注銷protected void deregister() {this.serviceRegistry.deregister(getRegistration());}}
在處理 Spring Cloud 服務(wù)流量隔離時(shí),首先獲取 AbstractAutoServiceRegistration 的服務(wù)注冊(cè)實(shí)例,然后調(diào)用 register/deregister 方法以在注冊(cè)中心上完成服務(wù)的注銷和重注冊(cè)。這種方法同樣不依賴于某個(gè)特定注冊(cè)中心的具體實(shí)現(xiàn)類,兼容所有注冊(cè)中心。

事件流量隔離

Aliware

應(yīng)用節(jié)點(diǎn)和中間件通常采用 client-server 模式通信,像 RocketMQ 和 SchedulerX 使用了 Netty 作為底層網(wǎng)絡(luò)框架完成客戶端和服務(wù)端通信 。在此,我們以 RocketMQ 為例,來(lái)說(shuō)明如何實(shí)現(xiàn)類似的事件驅(qū)動(dòng)中間件的流量隔離。 RocketMQ client 端的主要實(shí)現(xiàn)類是 NettyRemotingClient。如下圖所示,NettyRemotingClient 類中的屬性 channelTables 存儲(chǔ)了用于傳輸數(shù)據(jù)的 Channel,而 lockChannelTables 是用于控制 channelTables 更新的鎖。與此同時(shí),有幾個(gè) invoke 方法負(fù)責(zé)處理通信過(guò)程。 通信處理流程如下圖。首先,嘗試從 channelTables 中獲取用于通信的 Channel。如果沒(méi)有可用的 Channel,則重新連接 server 端以創(chuàng)建 Channel。為了保證線程間同步,新 Channel 更新到 channelTables 時(shí)需要獲得 lockChannelTables 鎖。如果在指定時(shí)間窗口內(nèi) lockChannelTables 一直被占用,將會(huì)拋出連接異常。 根據(jù)以上的原理分析,我們可以通過(guò)占用 lockChannelTables 鎖來(lái)阻止 Channel 的建立,再把現(xiàn)存的 Channel 關(guān)閉,則 client 端在 lockChannelTables 被釋放之前都無(wú)法與 server 端建立通信連接。若要恢復(fù)流量,僅需釋放 lockChannelTables 鎖,client 端將自動(dòng)重建 Channel 并恢復(fù)通信。由于這種管控是在網(wǎng)絡(luò)客戶端層進(jìn)行的,因此它不受應(yīng)用消息模型的影響,既適用于同步消息也適用于異步消息;同時(shí)也與 client 角色無(wú)關(guān),既適用于消費(fèi)者也適用于生產(chǎn)者。

結(jié)語(yǔ)

Aliware

關(guān)鍵詞:

責(zé)任編輯:

免責(zé)聲明

頭條新聞

精彩推送

新聞推送