自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

開發(fā) 架構(gòu)
新興的編程語(yǔ)言中,Rust以高性能、內(nèi)存安全為賣點(diǎn)在編程世界里廣受好評(píng)。除了比較繁瑣的語(yǔ)法,難于理解的變量所有權(quán)和生命周期,入門門檻比較高以外基本上沒有其他的缺點(diǎn)了。

新興的編程語(yǔ)言中,Rust以高性能、內(nèi)存安全為賣點(diǎn)在編程世界里廣受好評(píng)。除了比較繁瑣的語(yǔ)法,難于理解的變量所有權(quán)和生命周期,入門門檻比較高以外基本上沒有其他的缺點(diǎn)了?,F(xiàn)在處于編程語(yǔ)言百家爭(zhēng)鳴的時(shí)代,如何選擇合適的語(yǔ)言以及在合理時(shí)間解決問題成了一門學(xué)問。本文我們介紹一個(gè)案例為了解決瓶頸將將服務(wù)從Node.js遷移到Rust從而解決實(shí)際問題節(jié)省生產(chǎn)成本。過程中深入探討了導(dǎo)致需要更改語(yǔ)言的一些細(xì)節(jié),以及該過程中如何決策的,希望能給予大家一些啟發(fā)。

概述

案例涉及的是一個(gè)企業(yè)的業(yè)務(wù)監(jiān)控系統(tǒng),該系統(tǒng)用來以幫助開發(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)鍵部分:日志提取和日志處理。

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

起初的系統(tǒng)中是通過Node.js構(gòu)建提取服務(wù)。Node.js接收日志,與elixir服務(wù)進(jì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ǔ)的使用來收費(fèi)的,存儲(chǔ)的越多,需要支付的費(fèi)用就越多。

為了解決費(fèi)用的問題,于是就有一個(gè)救援計(jì)劃。

Kinesis救援和災(zāi)難

為了解決昂貴的CloudWatch存儲(chǔ)費(fèi)用問題,在將日志傳送到CloudWatch之前,使用了Kinesis Firehose前置處理。Kinesis Firehose可能熟悉少,但是知道kafka的人可能多,那么Kinesis Firehose就是AWS云中的Kafka。使用Kinesis Firehose前置處理,可以用可靠的方式將數(shù)據(jù)流傳遞到多個(gè)目的地。只需對(duì)日志處理程序進(jìn)行很少的更新,就可以從CloudWatch和Kinesis Firehose提取日志。通過該架構(gòu)的更改,可以將日成本下降到之前的千分之六。

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

新架構(gòu)中系統(tǒng)將日志數(shù)據(jù)通過Kinesis傳遞到s3中,從而觸發(fā)日志處理程序。新架構(gòu)運(yùn)行后,一切都o(jì)k。但是過幾天出現(xiàn)了異常。。。監(jiān)控儀表板上的一些異常情況。系統(tǒng)在收集垃圾,很多垃圾!

垃圾回收(GC)是某些編程語(yǔ)言自動(dòng)釋放不再使用內(nèi)存的一種方式。發(fā)生這種情況時(shí),程序?qū)?huì)暫停。這稱為GC暫停。對(duì)內(nèi)存進(jìn)行的寫操作越多,需要進(jìn)行的垃圾回收就越多,因此暫停時(shí)間會(huì)增加。對(duì)于系統(tǒng)服務(wù),這些暫停的速度越來越快,足以導(dǎo)致服務(wù)器重新啟動(dòng)并給CPU造成壓力。發(fā)生這種情況時(shí),它看起來就像是服務(wù)器已關(guān)閉(因?yàn)樗鼤簳r(shí)處于關(guān)閉狀態(tài)),并且在客戶端會(huì)有大量的5xx錯(cuò)誤,而代理所嘗試提取的日志中大約有6%出現(xiàn)了這個(gè)錯(cuò)誤。

下面圖顯示了垃圾回收的暫停時(shí)間和暫停頻率:

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

在某些情況下,暫停時(shí)間超過了4秒(如左圖所示),并且每分鐘最多有400次暫停(如右圖所示)。

經(jīng)過更多研究分析后,似乎成為AWS Javascript SDK中內(nèi)存泄漏的導(dǎo)致的該問題的發(fā)生。嘗試將資源分配增加到極限,例如減小縮放閾值到1000 req/min自動(dòng)縮放,但是沒有問題仍沒有解決。

可能的解決方案

由于不能能使用上面的kninesis方案,因此需要新的解決方案來解決問題??蛇x的方案有以下幾種。

Elixir

如前的架構(gòu)介紹,系統(tǒng)使用Elixir服務(wù)檢查客戶訪問權(quán)限。該服務(wù)是私有的,只能從虛擬私有云(VPC)中訪問。由于從未遇到過該服務(wù)的任何可擴(kuò)展性問題,并且大多數(shù)邏輯已經(jīng)存在。所以可選擇簡(jiǎn)單地從該服務(wù)中將日志發(fā)送到Kinesis,而跳過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ì)遇到類似的問題??紤]到這些限制,系統(tǒng)需要一個(gè)更好的選擇。

以Rust為核心進(jìn)行重新架構(gòu)

在系統(tǒng)最初的實(shí)現(xiàn)和備份中,核心問題都是相同的:垃圾回收。解決方案是使用一種具有內(nèi)存管理更好的并且沒有垃圾回收的語(yǔ)言。那么可選擇的語(yǔ)言就到了Rust。

Rust

Rust不是垃圾收集的語(yǔ)言。Rust依賴于稱為變量生命周期和所有權(quán)的概念。所有權(quán)是Rust的最獨(dú)特功能,它使Rust無需垃圾收集器即可保證內(nèi)存安全。

所有權(quán)是一個(gè)經(jīng)常使Rust難以學(xué)習(xí)和編寫的概念,但又使它非常適合像這個(gè)項(xiàng)目遇到的情況。Rust中的每個(gè)值都有一個(gè)所有者變量,因此在內(nèi)存中有一個(gè)分配點(diǎn)。一旦該變量超出范圍,內(nèi)存將會(huì)立即釋放。

由于提取日志所需的代碼很小,應(yīng)該非常值得嘗試。為了對(duì)此進(jìn)行測(cè)試,通過問題的瓶頸:向Kinesis發(fā)送大量數(shù)據(jù)。第一個(gè)基準(zhǔn)測(cè)試非常成功。

所以Rust最終成了救世主,最后決定將原型充實(shí)并在生產(chǎn)系統(tǒng)的部署。

在這些實(shí)驗(yàn)過程中,并沒有直接使用Rust直接替換原始的Node.js服務(wù),而是重構(gòu)了日志提取的大部分架構(gòu)。新服務(wù)的核心是通過Envoy代理,在其中Rust應(yīng)用程序作為輔助工具。

新架構(gòu)流程

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

當(dāng)用戶應(yīng)用程序中Agent將日志數(shù)據(jù)發(fā)送到系統(tǒng)時(shí),它將首先進(jìn)入Envoy代理。Envoy查看請(qǐng)求并與Redis通信以檢查速率限制,授權(quán)詳細(xì)信息和使用配額之類的內(nèi)容。接下來,與Envoy一起運(yùn)行的Rust應(yīng)用程序準(zhǔn)備日志數(shù)據(jù),并將其通過Kinesis傳遞到s3存儲(chǔ)桶中進(jìn)行存儲(chǔ)。然后,S3觸發(fā)​​日志處理程序處理,Elastic Search開始對(duì)其進(jìn)行索引。這樣,最終用戶就可以訪問儀表板中的數(shù)據(jù)。

性能和資源對(duì)比

新架構(gòu)中使用了更少(更小)的服務(wù)器,但是可以處理更多數(shù)據(jù),而不會(huì)出現(xiàn)任何之前的gc 5xx問題。

對(duì)比新舊架構(gòu)的服務(wù)延遲。在舊的Node.js架構(gòu)下服務(wù)的延遲數(shù)如下圖,可以看到平均響應(yīng)時(shí)間接近1700ms的峰值:

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

通過Rust服務(wù)的實(shí)施,新架構(gòu)中,即使在最高峰期間,延遲也降至90ms以下,平均響應(yīng)時(shí)間保持在40ms以下。

 

基于Rust實(shí)現(xiàn)業(yè)務(wù)架構(gòu)的重構(gòu)

 

舊架構(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ù)載。

結(jié)論

大多數(shù)初創(chuàng)公司都一樣,會(huì)遭遇業(yè)務(wù)迸發(fā)的階段。這時(shí)候當(dāng)初最好的解決方案并非永遠(yuǎn)都不再是最好的解決方案了。

該案例中的Node.js的架構(gòu)就是這種情況。它使企業(yè)能夠前進(jìn),但是隨著業(yè)務(wù)的飛速成長(zhǎng),最終業(yè)務(wù)超過了它。這時(shí)簡(jiǎn)單的資源縮放會(huì)帶來昂貴的不可接受的成本。這時(shí)候就需要優(yōu)化基礎(chǔ)架構(gòu),以滿足新的需求。本案例中,雖然僅用Rust替換Node.js就完成了架構(gòu)的升級(jí)和優(yōu)化,并完美解決了業(yè)務(wù)瓶頸。

 

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2013-12-22 15:51:00

IT基礎(chǔ)架構(gòu)關(guān)鍵業(yè)務(wù)英特爾

2023-09-26 21:55:29

2013-04-12 10:17:56

重構(gòu)業(yè)務(wù)邏輯

2024-12-23 06:10:00

RustRigAI Agent

2009-11-05 13:42:44

BSM

2018-07-10 10:00:15

Android架構(gòu)MVC

2021-07-08 06:08:54

架構(gòu)重構(gòu)開發(fā)

2022-09-16 07:40:17

CloudWeGo開源Rust

2021-01-16 16:07:51

RustAndroid Nat內(nèi)存

2022-08-08 13:24:28

整潔架構(gòu)架構(gòu)前端

2023-12-06 09:33:54

Reactor網(wǎng)絡(luò)

2023-03-10 10:29:19

前端邏輯拆分

2017-07-05 16:10:40

Fluent FetcJavaScrip 網(wǎng)絡(luò)請(qǐng)求庫(kù)

2022-05-09 10:36:05

PythonPyScript開發(fā)者

2022-03-10 17:02:51

Rust單鏈表數(shù)據(jù)結(jié)構(gòu)

2022-11-24 10:43:33

2025-01-26 10:10:30

2024-01-09 07:34:28

Rust架構(gòu)語(yǔ)言

2019-05-28 10:30:16

Java架構(gòu)微服務(wù)

2022-05-05 07:49:54

業(yè)務(wù)冪MySQL索引
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)