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

從SPserver到BRPC

開發(fā) 后端
本文是之家廣告引擎團(tuán)隊(duì)結(jié)合工作中遇到的超時(shí)現(xiàn)象及問題分析過程,對服務(wù)框架升級背后的思考做出的一個(gè)總結(jié)與沉淀。

公眾號轉(zhuǎn)載自:汽車之家技術(shù)委員會

1.背景 

性能優(yōu)化是后端服務(wù)優(yōu)化的一個(gè)重要課題。尤其在廣告業(yè)務(wù)中,服務(wù)超時(shí)不但會引發(fā)廣告客戶的預(yù)算消耗顧慮,更會直接影響C端用戶的瀏覽體驗(yàn)。而一個(gè)服務(wù)程序的性能往往是覆蓋了編程語言特性、業(yè)務(wù)需求邏輯,甚至是操作系統(tǒng)底層原理等多方面因素的綜合性外在表現(xiàn)。面對超時(shí)問題,不論是對其進(jìn)行量化分解、問題復(fù)現(xiàn),還是異常監(jiān)控,以及后續(xù)的超時(shí)優(yōu)化,對后端開發(fā)同學(xué)而言都極富挑戰(zhàn)。如果變換思路,轉(zhuǎn)而升級后端服務(wù)框架或許會成為最為徹底的解決方案。

本文是之家廣告引擎團(tuán)隊(duì)結(jié)合工作中遇到的超時(shí)現(xiàn)象及問題分析過程,對服務(wù)框架升級背后的思考做出的一個(gè)總結(jié)與沉淀。

2.問題回顧 

之家廣告檢索服務(wù)從建立之初采用的一直是SPserver服務(wù)框架。Spserver是一個(gè)C++實(shí)現(xiàn)的半同步異步的網(wǎng)絡(luò)框架,名氣雖然不大,但在當(dāng)年并發(fā)編程尤其是協(xié)程概念尚不廣泛的年代,相比較代碼風(fēng)格各異、封裝獨(dú)立性參差不齊的諸多自研網(wǎng)絡(luò)框架,它確實(shí)不失為一個(gè)比較好的選擇。特別是在之家廣告系統(tǒng)搭建之初,其以良好的性能、穩(wěn)定性和簡便的使用方式,在引擎內(nèi)部曾得到廣泛應(yīng)用,可以說在推進(jìn)廣告業(yè)務(wù)發(fā)展上,SPserver立下了汗馬功勞。

隨著需求的不斷迭代和升級,廣告系統(tǒng)運(yùn)行中碰到的問題也越來越凸顯,包括:內(nèi)存消耗、并發(fā)、超時(shí)、生態(tài)融合等等,其中又以超時(shí)問題為重。

說起超時(shí),恐怕會讓人抓狂,因?yàn)樗鼇淼奶蝗欢規(guī)缀醪涣羧魏魏圹E。沒錯(cuò),監(jiān)控可以抓到它,但問題是通過分析全鏈路日志你可能不會有任何收獲,因?yàn)槟闼鶆澐值母鱾€(gè)業(yè)務(wù)邏輯階段的耗時(shí)并沒有超過預(yù)先設(shè)定的超時(shí)閾值,而且進(jìn)程的CPU利用率也不高(可以說很低),讓人詭異的是上游請求方的超時(shí)卻是實(shí)實(shí)在在發(fā)生了的。

3.問題分析 

應(yīng)用服務(wù)不是孤立的,它所發(fā)生的問題也應(yīng)該是有關(guān)聯(lián)的。

圖片

(圖1:來自網(wǎng)絡(luò))

通過圖1知道,一個(gè)業(yè)務(wù)應(yīng)用在處理網(wǎng)絡(luò)請求之前以及發(fā)送網(wǎng)絡(luò)應(yīng)答之后,數(shù)據(jù)會流經(jīng)網(wǎng)絡(luò)、系統(tǒng)內(nèi)核。那么,超時(shí)會不會是由它們引起的呢?為此,我們通過壓測環(huán)境復(fù)現(xiàn)了超時(shí)發(fā)生的系統(tǒng)上下文場景:

圖片

(圖2)

可以發(fā)現(xiàn),業(yè)務(wù)應(yīng)用與其上下游之間經(jīng)由多個(gè)tcp連接通信,而部分tcp連接在對應(yīng)socket的接收緩沖區(qū)和發(fā)送緩沖區(qū)卻存在著數(shù)據(jù)積壓的現(xiàn)象,且兩個(gè)隊(duì)列的積壓數(shù)據(jù)量分別固定在4xx和2xx。

不同于grpc倡導(dǎo)的單通道通信模式,spserver原生支持多tcp連接服務(wù),這點(diǎn)無可厚非?;趖cp協(xié)議的擁塞控制機(jī)制,網(wǎng)絡(luò)數(shù)據(jù)進(jìn)入應(yīng)用層前,會在操作系統(tǒng)內(nèi)核態(tài)的fd緩沖區(qū)稍作停留,因此短時(shí)間內(nèi)存在一定量的數(shù)據(jù)積壓也是正常的;既然是緩沖區(qū),自然有大小之分。那么,會不會是緩沖區(qū)太小或者網(wǎng)絡(luò)擁堵導(dǎo)致的超時(shí)呢?又該如何判斷呢?

圖片

(圖3)

可見,系統(tǒng)對socket收發(fā)緩沖區(qū)的默認(rèn)值都在80k以上,遠(yuǎn)大于圖2中的431字節(jié)。而且spserver源代碼中也沒有通過setsockopt系統(tǒng)調(diào)用對socket的收發(fā)緩沖區(qū)重新設(shè)值。所以,圖2中緩沖區(qū)大小是合理的,而其中接收隊(duì)列中的數(shù)據(jù)量431則可能是有問題的(實(shí)際上,431是壓測場景中單次請求的數(shù)據(jù)長度;在生產(chǎn)環(huán)境中,這個(gè)隊(duì)列長度會隨著C端請求的不同而呈現(xiàn)不同的值,毫無規(guī)律可言)。

連續(xù)刷新netstat,對比目標(biāo)tcp連接的發(fā)送緩沖隊(duì)列或接收隊(duì)列長度,發(fā)現(xiàn)此場景下收發(fā)隊(duì)列的長度并不能立即消失,也沒有減小,而是持續(xù)了1秒左右才有所變化。由此可以推測2種可能:1)網(wǎng)絡(luò)擁堵;2)服務(wù)端cpu繁忙,不能及時(shí)將數(shù)據(jù)從緩沖區(qū)讀走。第一種猜測通過網(wǎng)絡(luò)檢測工具很容易驗(yàn)證,是否定的;第二種猜測,也通過查看系統(tǒng)以及進(jìn)程cpu的利用率也很容易排除掉。分析排查到此,似乎走到了死胡同。真的是這樣嗎?

有一個(gè)細(xì)節(jié)似乎被忽略了:作為多線程服務(wù),進(jìn)程的cpu利用率≈線程cpu利用率之和,但進(jìn)程cpu利用率低并不意味著某些或某個(gè)線程cpu利用率也低。換句話說,個(gè)別線程所在cpu的高利用率同樣會使上面第二種猜測成立。

圖片

(圖4)

如圖4,反復(fù)施加不同量級的請求壓力,會發(fā)現(xiàn)除了18194號線程的cpu使用率保持在99%以上,其他線程的cpu使用率最大都不過40%左右。面對高壓力,服務(wù)不能平攤cpu壓力,這就是問題!

4.根本原因 

上帝總是吝嗇造就一個(gè)十全十美的東西,對待SPserver自然也不例外。其實(shí)排查走到線程的cpu利用率時(shí),已經(jīng)接近真實(shí)原因,但還不是真相。為此,擼了一遍spserver的源碼,得出如下線程模型示意圖。

圖片

(圖5)

不出所料,SPserver采用的是單線程reactor網(wǎng)絡(luò)模型,即單線程負(fù)責(zé)事件監(jiān)聽、socket讀寫,多線程負(fù)責(zé)業(yè)務(wù)邏輯處理。單線程io的優(yōu)劣顯而易見,可以很好的利用thread local加速,也沒有cpu cache bounding的問題;但問題是一旦某個(gè)socket上待讀取或待發(fā)送的數(shù)據(jù)量較大時(shí),就會阻塞其他socket上數(shù)據(jù)的收發(fā),這就比較致命。退一步講,即便每個(gè)socket fd上的數(shù)據(jù)量大小均勻,在上述單線程的cpu吃滿時(shí),整個(gè)框架的數(shù)據(jù)收發(fā)效率同樣會成為所在服務(wù)的性能瓶頸。

真相大白了,原來SPserver框架的設(shè)計(jì)機(jī)制決定了它不適合高并發(fā)、高吞吐業(yè)務(wù)場景的事實(shí)。但如果是在業(yè)務(wù)初期又或是業(yè)務(wù)流量比較小,個(gè)人覺得它仍可視為一個(gè)不錯(cuò)的選擇(SPserver框架的c++代碼風(fēng)格還是很不錯(cuò)的,很簡潔,封裝性也很好,值得借鑒)。

5.選擇BRPC 

如今rpc框架林立,我們的選擇是百度研發(fā)的brpc框架,主要是基于以下考慮:

1)高并發(fā)高性能

個(gè)人理解后面會著重介紹一下。這里我只貼一個(gè)brpc和其他rpc框架性能的對比圖:

圖片

(圖6:來自brpc官網(wǎng))

是的,你沒有看錯(cuò),在跨機(jī)多client請求單server的場景下,brpc框架的性能已經(jīng)絕對領(lǐng)先國外知名的rpc框架,尤其是grpc,用碾壓一詞來形容也不為過。

2)文檔資料豐富

brpc有著豐富的中英文文檔,豐富程度讓人有點(diǎn)難以置信,曾一度有人認(rèn)為是百度內(nèi)部的技術(shù)資料無意中被公開了,呵呵。

3)apache 的頂級項(xiàng)目,目前有數(shù)千個(gè)企業(yè)級應(yīng)用

說直白點(diǎn),已經(jīng)在生產(chǎn)中經(jīng)歷過千錘百煉了,質(zhì)量有保證。

當(dāng)然,brpc還有諸多特性,這里不再贅述。詳見brpc官網(wǎng)或移步到github incubator-brpc項(xiàng)目。

6.性能初探 

經(jīng)歷了spserver框架,免不了要有一番對比。在介紹brpc的線程模型前,先了解一個(gè)bprc中的概念:bthread。我們看一下官方的解釋:

“bthread是brpc使用的M:N線程庫,目的是在提高程序的并發(fā)度的同時(shí),降低編碼難度,并在核數(shù)日益增多的CPU上提供更好的scalability和cache locality?!盡:N“是指M個(gè)bthread會映射至N個(gè)pthread,一般M遠(yuǎn)大于N。由于linux當(dāng)下的pthread實(shí)現(xiàn)[NPTL]是1:1的,M個(gè)bthread也相當(dāng)于映射至N個(gè)[LWP]。bthread的前身是Distributed Process(DP)中的fiber,一個(gè)N:1的合作式線程庫,等價(jià)于event-loop庫,但寫的是同步代碼?!?/p>

個(gè)人理解bthread其實(shí)是一個(gè)運(yùn)行在系統(tǒng)pthread之上的可以低成本、靈活調(diào)度的任務(wù)(隊(duì)列),而這個(gè)任務(wù)載有自身運(yùn)行時(shí)的上下文信息(比如:棧、寄存器、signal等),使它能夠隨意切換在不同的pthread上運(yùn)行。其低成本體現(xiàn)在幾個(gè)方面:1)bthread實(shí)現(xiàn)了多種同步原語,可以和系統(tǒng)線程實(shí)現(xiàn)相互等待,我們可以像使用pthread一樣使用bthread;2)納秒級建立bthread,耗時(shí)遠(yuǎn)低于pthread;3)幾乎沒有上下文切換,且將cpu cache locality和thread local發(fā)揮到了極致,這部分后面會有實(shí)踐說明。

下面再來看看它的線程模型示意圖:

圖片

(圖7)

由圖7可見:1)在brpc框中系統(tǒng)級線程池pthread是高效運(yùn)行的基礎(chǔ)設(shè)施,不再與具體的業(yè)務(wù)邏輯直接綁定,取而代之的是bthread;2)bthread按不同責(zé)任分成不同類型,且不同類型的bthread有著不同的數(shù)量。比如:網(wǎng)絡(luò)事件的監(jiān)聽和驅(qū)動由一個(gè)bthread專職處理,當(dāng)然也可以通過命令行啟動服務(wù)時(shí)配置,或者服務(wù)啟動后通過web入口更新;而處理具體請求的bthread數(shù)量則是動態(tài)計(jì)算的;3)brpc支持不忙的線程“偷”繁忙線程的bthread任務(wù)來提升系統(tǒng)整體效能。

那么問題來了,某一個(gè)socket fd上大量數(shù)據(jù)的接收或發(fā)送,會發(fā)生類似spserver那樣的阻塞嗎?答案是不會。首先,每個(gè)fd有兩個(gè)bthread,分別負(fù)責(zé)接收和發(fā)送,這就能保證收發(fā)互不影響;其次,bthread作為被調(diào)度的任務(wù),會被分派給不同系統(tǒng)線程(也就是pthread),而一個(gè)系統(tǒng)線程同一時(shí)刻只能執(zhí)行一個(gè)bthread任務(wù),加上bthread 支持的stealing機(jī)制,就保證了進(jìn)程中所有線程都是有事可做的,不會存在空閑的pthread(除非請求量非常小,不足以給pthread平分)。因此,基本上是不可能發(fā)生“一處阻塞處處阻塞”的情況的。

使用更大量的壓測請求對brpc服務(wù)發(fā)壓,得到各pthread的cpu消耗如下:

圖片

(圖8)

由圖8可見,系統(tǒng)的多核cpu得到充分的調(diào)動,且隨著壓力的增大cpu的擴(kuò)展性表現(xiàn)良好;再來看一下網(wǎng)絡(luò)隊(duì)列:

圖片

(圖9)

可見,(同機(jī)grpc單通道壓測,僅有一個(gè)tcp連接)tcp fd的收發(fā)緩沖區(qū)得到了充分的利用,且隊(duì)列長度很快能減少至0。

Socket緩沖區(qū)不再是擺設(shè),整個(gè)系統(tǒng)活了起來!

另外,值得一提的是線程間的上下文切換。因?yàn)檫^多的上下文切換,會把cpu時(shí)間消耗在寄存器、線程棧的保存和恢復(fù)上,從而降低服務(wù)的整體性能。brpc框架的m:n線程庫,在這方面做的比較好,它使用固定的系統(tǒng)線程調(diào)度運(yùn)行大量用戶態(tài)的bthread,將所有的切換基本上都限制在了用戶態(tài),這就避免了內(nèi)核態(tài)和用戶態(tài)的數(shù)據(jù)交換(用戶態(tài)之間切換耗時(shí)在100~200ns,而內(nèi)核態(tài)和用戶態(tài)切換則在微秒級)。通過命令也可以證明這一點(diǎn):

圖片

(圖10)

如圖10,我們的服務(wù)在使用brpc后上下文切換頻次基本保持在每秒1次。再回頭來看看spserver框架,由于沒有用戶態(tài)任務(wù)的概念,只是單純依賴系統(tǒng)級的線程池,就不可避免的使cpu不斷地游離于多線程調(diào)度和任務(wù)執(zhí)行上,內(nèi)核態(tài)和用戶態(tài)間的上下文切換開銷肯定少不了:

圖片

(圖11)

用戶態(tài)線程切換的另一個(gè)好處是,可以將內(nèi)核態(tài)線程與cpu核心很好的綁定到了一起,這就能夠盡量避免cpu不同核心間cacheline的數(shù)據(jù)同步,從而提升性能,這也是brpc框架高性能的一個(gè)原因。

7.應(yīng)用實(shí)踐 

brpc的編譯安裝及基本使用,在官方文檔都有較為詳細(xì)的說明,比較簡單。這里再分享一下我們在brpc應(yīng)用過程中遇到過的幾個(gè)值得注意的地方:

1)thread local.

它是多線程程序常用的加速手段。比如tcmalloc就充分利用了這個(gè)技術(shù),通過在線程內(nèi)部設(shè)置局部緩存來加速小額空間的申請效率。之家廣告引擎服務(wù)毫無例外的也使用了這項(xiàng)技術(shù)。但需要注意的是,在引入brpc框架后,原有的pthread id可能將不再有效,如果你執(zhí)意為之,就可能會在程序運(yùn)行期間遇到莫名其妙的段錯(cuò)誤。這是因?yàn)槲覀兊臉I(yè)務(wù)代碼是托管在bthread中的,而bthread是在系統(tǒng)pthread之間隨機(jī)游走的,使用pthread1的標(biāo)識信息到pthreadN的線程棧中讀取緩存數(shù)據(jù)自然是讀不到的。我們的臨時(shí)解決辦法是,暫時(shí)剝離掉thread local緩存,在控制鎖粒度的前提下改為全局cache,暴力、簡單、有效。

2)cpu profiler

顧名思義,你的程序可能會因此得以優(yōu)化并加速運(yùn)行。但事實(shí)并非完全如此。如果你們業(yè)務(wù)程序的CMakeList來自某個(gè)demo或網(wǎng)絡(luò)程序,則最好要注意這個(gè)編譯選項(xiàng)。它的原理是通過調(diào)用對應(yīng)的庫函數(shù)采集活躍線程中的線程函數(shù)信息,并根據(jù)棧體現(xiàn)的函數(shù)調(diào)用關(guān)系生成調(diào)用圖,進(jìn)而進(jìn)行調(diào)用優(yōu)化。所以,它會加速但不是立即,因?yàn)樗炔杉瘮?shù)據(jù)、再分析,最后才能優(yōu)化運(yùn)行。我們實(shí)踐初期,肉眼可見其性能相比較不加此編譯選項(xiàng)要低10%以上,所以,對待cpu profiler要慎用。

3)關(guān)于grpc

前面提到過,gprc默認(rèn)是基于單通道的通信方式,這是既是google官方的建議也是微軟的實(shí)踐建議。下面截圖來自微軟的” Performance best practices with gRPC”一文:

圖片

(圖12:截自微軟官網(wǎng))

不能總是人云亦云。結(jié)合具體業(yè)務(wù)場景,我們的實(shí)踐結(jié)論是:多通道數(shù)據(jù)傳輸效率要優(yōu)于單通道。受限于tcp速率限制,單通道(連接)情況下,一旦遇到高吞吐的數(shù)據(jù)傳輸業(yè)務(wù)場景,會明顯阻塞此網(wǎng)絡(luò)連接。特別對于廣告業(yè)務(wù)來說,我們允許有大數(shù)據(jù)塊傳輸,但不允許大數(shù)據(jù)塊的傳輸影響其他正常的廣告數(shù)據(jù)響應(yīng)。

因此,我們的建議是:要么使用多通道grpc或者其他協(xié)議方式,要么就放棄grpc吧(事實(shí)上,grpc在生產(chǎn)中還有其他問題)。

4)并發(fā)

早在spserver時(shí)期,我們曾在其中實(shí)現(xiàn)了并發(fā)線程庫(確切說是一個(gè)并發(fā)線程類),但效果并未達(dá)到預(yù)期,因?yàn)樗欢ǔ潭壬霞又亓硕嗑€程調(diào)度成本。而如今的brpc則直接提供了較為簡單的并發(fā)線程 api,我們可以直接使用,無需造輪。

然而,會面臨新的選擇:用bthread_start_background,還是用bthread_start_urgent。使用后者啟動bthread后會在當(dāng)前pthread立即執(zhí)行任務(wù),而前者則會讓新生成的bthread任務(wù)排隊(duì)等待調(diào)度。在我們的廣告檢索過濾場景中,適合使用后者;而在執(zhí)行http請求時(shí),則更適合使用前者。建議brpc開發(fā)者,一定要根據(jù)自己業(yè)務(wù)的實(shí)際情況再做決定。

8.最后 

通過從SPserver框架升級到BRPC框架,在相同的業(yè)務(wù)場景下,之家廣告服務(wù)qps從5w+提升到了10w左右,服務(wù)實(shí)例數(shù)也因此下降了一半以上,收益明顯。

另外,Brpc提供了相對豐富的內(nèi)置服務(wù),這里貼兩個(gè)具有代表性功能的web界面,都比較實(shí)用,推薦大家嘗試。

圖13:我們可以看到服務(wù)的qps、latency分布等數(shù)據(jù),方便把握服務(wù)的運(yùn)行時(shí)信息。

圖片

(圖13)

而從下圖,可以看到服務(wù)運(yùn)行期間在等待鎖上所消耗的時(shí)間及發(fā)生等待的函數(shù),從而支持我們有針對性的開展性能優(yōu)化。

圖片

(圖14)

note

限于作者水平,難免會有理解和描述上的疏漏或者錯(cuò)誤,歡迎共同交流、指正。文章供于學(xué)習(xí)交流,轉(zhuǎn)載注明出處,謝謝!

作者簡介

圖片

汽車之家

主機(jī)廠事業(yè)部-技術(shù)部

楊明哲

2018年加入汽車之家,目前任職于主機(jī)廠事業(yè)部-技術(shù)部-廣告技術(shù)及系統(tǒng)團(tuán)隊(duì),負(fù)責(zé)之家廣告引擎架構(gòu)的設(shè)計(jì)與研發(fā)等相關(guān)工作。

圖片

責(zé)任編輯:武曉燕 來源: 汽車之家技術(shù)委員會
相關(guān)推薦

2023-11-16 21:20:13

ListWatchkube

2021-03-03 08:18:54

Service組件

2022-05-09 08:35:43

面試產(chǎn)品互聯(lián)網(wǎng)

2015-09-17 13:09:48

預(yù)裝軟件毒瘤國產(chǎn)手機(jī)

2013-06-06 13:42:48

OSPF入門配置

2023-10-12 15:38:50

FreeDOS命令

2017-06-26 09:15:39

SQL數(shù)據(jù)庫基礎(chǔ)

2013-04-07 10:10:23

2022-09-04 21:46:12

數(shù)據(jù)信息風(fēng)險(xiǎn)

2025-02-05 09:55:29

2010-02-06 15:31:18

ibmdwAndroid

2009-07-22 14:55:16

ibmdwAndroid

2023-12-27 06:48:49

KubernetesDevOpsHTTP

2021-02-21 22:53:01

CanvasHTML5JavaScript

2020-10-27 06:39:14

智慧城市智慧旅游物聯(lián)網(wǎng)

2016-11-28 16:23:23

戴爾

2010-12-22 12:00:48

軟件保護(hù)軟件授權(quán)

2019-07-02 14:17:18

API網(wǎng)關(guān)網(wǎng)關(guān)流量

2018-01-18 04:52:07

點(diǎn)贊
收藏

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