您好,登錄后才能下訂單哦!
這篇文章給大家介紹Rust中怎么重構(gòu)業(yè)務(wù)架構(gòu),內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
概述
案例涉及的是一個(gè)企業(yè)的業(yè)務(wù)監(jiān)控系統(tǒng),該系統(tǒng)用來(lái)以幫助開(kāi)發(fā)人員監(jiān)控業(yè)務(wù)API。當(dāng)客戶的應(yīng)用程序調(diào)用API時(shí),會(huì)向系統(tǒng)發(fā)送日志,系統(tǒng)對(duì)發(fā)送的日志中進(jìn)行監(jiān)控和分析。
系統(tǒng)數(shù)據(jù)流為平均每分鐘處理30k 的API調(diào)用。每個(gè)客戶都會(huì)進(jìn)行很多個(gè)API的調(diào)用。系統(tǒng)的處理分為兩個(gè)關(guān)鍵部分:日志提取和日志處理。
起初的系統(tǒng)中是通過(guò)Node.js構(gòu)建提取服務(wù)。Node.js接收日志,與elixir服務(wù)進(jìn)行通信檢查用戶的訪問(wèn)權(quán)限,用Redis檢查速率限制,然后將日志發(fā)送到CloudWatch。CloudWatch部署了觸發(fā)器,觸發(fā)事件通知數(shù)據(jù)處理程序處理。
系統(tǒng)提取有關(guān)API調(diào)用的信息,包括從用戶應(yīng)用程序發(fā)送的每個(gè)調(diào)用的有效負(fù)載(請(qǐng)求和響應(yīng))。這些文件的大小被限制為1MB,但是仍然涉及大量的數(shù)據(jù)需要處理。處理程序以異步的形式發(fā)送和處理所有內(nèi)容,目標(biāo)是使信息盡快提供給最終用戶。
所有內(nèi)容都托管在亞馬遜云AWS Fargate上,并對(duì)其設(shè)置為在4000 req/min閾值觸發(fā)自動(dòng)縮放。
整個(gè)流程都運(yùn)行的很好,但是費(fèi)用卻非常昂貴。由于AWS是按照CloudWatch存儲(chǔ)的使用來(lái)收費(fèi)的,存儲(chǔ)的越多,需要支付的費(fèi)用就越多。
為了解決費(fèi)用的問(wèn)題,于是就有一個(gè)救援計(jì)劃。
Kinesis救援和災(zāi)難
為了解決昂貴的CloudWatch存儲(chǔ)費(fèi)用問(wèn)題,在將日志傳送到CloudWatch之前,使用了Kinesis Firehose前置處理。Kinesis Firehose可能熟悉少,但是知道kafka的人可能多,那么Kinesis Firehose就是AWS云中的Kafka。使用Kinesis Firehose前置處理,可以用可靠的方式將數(shù)據(jù)流傳遞到多個(gè)目的地。只需對(duì)日志處理程序進(jìn)行很少的更新,就可以從CloudWatch和Kinesis Firehose提取日志。通過(guò)該架構(gòu)的更改,可以將日成本下降到之前的千分之六。
新架構(gòu)中系統(tǒng)將日志數(shù)據(jù)通過(guò)Kinesis傳遞到s3中,從而觸發(fā)日志處理程序。新架構(gòu)運(yùn)行后,一切都o(jì)k。但是過(guò)幾天出現(xiàn)了異常。。。監(jiān)控儀表板上的一些異常情況。系統(tǒng)在收集垃圾,很多垃圾!
垃圾回收(GC)是某些編程語(yǔ)言自動(dòng)釋放不再使用內(nèi)存的一種方式。發(fā)生這種情況時(shí),程序?qū)?huì)暫停。這稱為GC暫停。對(duì)內(nèi)存進(jìn)行的寫(xiě)操作越多,需要進(jìn)行的垃圾回收就越多,因此暫停時(shí)間會(huì)增加。對(duì)于系統(tǒng)服務(wù),這些暫停的速度越來(lái)越快,足以導(dǎo)致服務(wù)器重新啟動(dòng)并給CPU造成壓力。發(fā)生這種情況時(shí),它看起來(lái)就像是服務(wù)器已關(guān)閉(因?yàn)樗鼤簳r(shí)處于關(guān)閉狀態(tài)),并且在客戶端會(huì)有大量的5xx錯(cuò)誤,而代理所嘗試提取的日志中大約有6%出現(xiàn)了這個(gè)錯(cuò)誤。
下面圖顯示了垃圾回收的暫停時(shí)間和暫停頻率:
在某些情況下,暫停時(shí)間超過(guò)了4秒(如左圖所示),并且每分鐘最多有400次暫停(如右圖所示)。
經(jīng)過(guò)更多研究分析后,似乎成為AWS Javascript SDK中內(nèi)存泄漏的導(dǎo)致的該問(wèn)題的發(fā)生。嘗試將資源分配增加到極限,例如減小縮放閾值到1000 req/min自動(dòng)縮放,但是沒(méi)有問(wèn)題仍沒(méi)有解決。
可能的解決方案
由于不能能使用上面的kninesis方案,因此需要新的解決方案來(lái)解決問(wèn)題。可選的方案有以下幾種。
Elixir
如前的架構(gòu)介紹,系統(tǒng)使用Elixir服務(wù)檢查客戶訪問(wèn)權(quán)限。該服務(wù)是私有的,只能從虛擬私有云(VPC)中訪問(wèn)。由于從未遇到過(guò)該服務(wù)的任何可擴(kuò)展性問(wèn)題,并且大多數(shù)邏輯已經(jīng)存在。所以可選擇簡(jiǎn)單地從該服務(wù)中將日志發(fā)送到Kinesis,而跳過(guò)Node.js服務(wù)層。這是一個(gè)值得嘗試的方案。
做了一番改進(jìn)后,系統(tǒng)進(jìn)行了測(cè)試。效果會(huì)好一點(diǎn),但仍然不是很佳。系統(tǒng)的基準(zhǔn)測(cè)試表明,GC垃圾收集的水平仍然很高,并且在使用日志時(shí)仍會(huì)有5xx的日志返回給用戶。
Golang
系統(tǒng)也考慮到Golang。這是一個(gè)很好的選擇方案,但是,畢竟Golang也是一種垃圾收集語(yǔ)言。雖然可能可以實(shí)現(xiàn)比上述更高效,但隨著規(guī)模的擴(kuò)展,很可能還會(huì)遇到類(lèi)似的問(wèn)題??紤]到這些限制,系統(tǒng)需要一個(gè)更好的選擇。
以Rust為核心進(jìn)行重新架構(gòu)
在系統(tǒng)最初的實(shí)現(xiàn)和備份中,核心問(wèn)題都是相同的:垃圾回收。解決方案是使用一種具有內(nèi)存管理更好的并且沒(méi)有垃圾回收的語(yǔ)言。那么可選擇的語(yǔ)言就到了Rust。
Rust
Rust不是垃圾收集的語(yǔ)言。Rust依賴于稱為變量生命周期和所有權(quán)的概念。所有權(quán)是Rust的最獨(dú)特功能,它使Rust無(wú)需垃圾收集器即可保證內(nèi)存安全。
所有權(quán)是一個(gè)經(jīng)常使Rust難以學(xué)習(xí)和編寫(xiě)的概念,但又使它非常適合像這個(gè)項(xiàng)目遇到的情況。Rust中的每個(gè)值都有一個(gè)所有者變量,因此在內(nèi)存中有一個(gè)分配點(diǎn)。一旦該變量超出范圍,內(nèi)存將會(huì)立即釋放。
由于提取日志所需的代碼很小,應(yīng)該非常值得嘗試。為了對(duì)此進(jìn)行測(cè)試,通過(guò)問(wèn)題的瓶頸:向Kinesis發(fā)送大量數(shù)據(jù)。第一個(gè)基準(zhǔn)測(cè)試非常成功。
所以Rust最終成了救世主,最后決定將原型充實(shí)并在生產(chǎn)系統(tǒng)的部署。
在這些實(shí)驗(yàn)過(guò)程中,并沒(méi)有直接使用Rust直接替換原始的Node.js服務(wù),而是重構(gòu)了日志提取的大部分架構(gòu)。新服務(wù)的核心是通過(guò)Envoy代理,在其中Rust應(yīng)用程序作為輔助工具。
新架構(gòu)流程
當(dāng)用戶應(yīng)用程序中Agent將日志數(shù)據(jù)發(fā)送到系統(tǒng)時(shí),它將首先進(jìn)入Envoy代理。Envoy查看請(qǐng)求并與Redis通信以檢查速率限制,授權(quán)詳細(xì)信息和使用配額之類(lèi)的內(nèi)容。接下來(lái),與Envoy一起運(yùn)行的Rust應(yīng)用程序準(zhǔn)備日志數(shù)據(jù),并將其通過(guò)Kinesis傳遞到s3存儲(chǔ)桶中進(jìn)行存儲(chǔ)。然后,S3觸發(fā)日志處理程序處理,Elastic Search開(kāi)始對(duì)其進(jìn)行索引。這樣,最終用戶就可以訪問(wèn)儀表板中的數(shù)據(jù)。
性能和資源對(duì)比
新架構(gòu)中使用了更少(更小)的服務(wù)器,但是可以處理更多數(shù)據(jù),而不會(huì)出現(xiàn)任何之前的gc 5xx問(wèn)題。
對(duì)比新舊架構(gòu)的服務(wù)延遲。在舊的Node.js架構(gòu)下服務(wù)的延遲數(shù)如下圖,可以看到平均響應(yīng)時(shí)間接近1700ms的峰值:
通過(guò)Rust服務(wù)的實(shí)施,新架構(gòu)中,即使在最高峰期間,延遲也降至90ms以下,平均響應(yīng)時(shí)間保持在40ms以下。
舊架構(gòu)下Node.js應(yīng)用程序在任何給定時(shí)間都會(huì)使用約1.5GB的內(nèi)存,CPU的負(fù)載約為150%。
新架構(gòu)下Rust服務(wù)使用了大約100MB的內(nèi)存,而僅占用了2.5%的CPU負(fù)載。
關(guān)于Rust中怎么重構(gòu)業(yè)務(wù)架構(gòu)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。