偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如何調用一個只支持batch_call的服務?

開發(fā) 后端
如果非得使用同步調用的方式進行調用,建議模仿Nagle算法的形式,攢一批數(shù)據(jù)再發(fā)起請求,這樣既可以增大batch,同時減少并發(fā),真·一舉兩得,親測有效。

我們先來說下標題是什么意思。

為了更好的理解我說的是啥,我們來舉個例子。

假設你現(xiàn)在在做一個類似B站的系統(tǒng),里面放了各種視頻。

圖片

用戶每天在里頭上傳各種視頻。

按理說每個視頻都要去審查一下有沒有搞顏色,但總不能人眼挨個看吧。

畢竟唐老哥表示這玩意看多了,看太陽都是綠色的,所以會有專門訓練過的算法服務去做檢測。

但也不能上來就整個視頻每一幀都拿去做審查吧,所以會在每個視頻里根據(jù)時長和視頻類型隨機抽出好幾張圖片去做審查,比如視頻標簽是美女的,算法愛看,那多抽幾張。標簽是編程的,狗都不看,就少抽幾張。

將這些抽出來的圖片,送去審查。

為了實現(xiàn)這個功能,我們會以視頻為維度去做審核,而每個視頻里都會有N張數(shù)量不定的圖片,下游服務是個使用GPU去檢測圖片的算法服務。

現(xiàn)在問題來了,下游服務的算法開發(fā)告訴你,這些個下游服務,它不支持很高的并發(fā),但請求傳參里給你加了個數(shù)組,你可以批量(batch)傳入一個比較大的圖片數(shù)組,通過這個方式可以提升點圖片處理量。

于是,我們的場景就變成。

上游服務的入?yún)⑹且粋€視頻和它的N張圖片,出參是這個視頻是否審核通過。

下游服務的入?yún)⑹荖張圖片的,出參是這個視頻是否審核通過。

圖片batch_call上下游

現(xiàn)在我們想要用上游服務接入下游服務。該怎么辦?

看上去挺好辦的,一把梭不就完事了嗎?

當一個視頻進來,就拿著視頻的十多張圖片作為一個batch去進行調用。

有幾個視頻進來,就開幾個這樣的并發(fā)。

這么做的結果就是,當并發(fā)大一點時,你會發(fā)現(xiàn)性能很差,并且性能非常不穩(wěn)定,比如像下面的監(jiān)控圖一樣一會3qps,一會15qps。處理的圖片也只支持20qps左右。

狗看了都得搖頭。

圖片

圖1-直接調用時qps很低

圖片

這可如何是好?

為什么下游需要batch call

本著先問是不是,再問為什么的精神,我們先看看為啥下游的要求會如此別致。

為什么同樣都是處理多張圖片,下游不搞成支持并發(fā)而要搞成批量調用(batch call)?

這個設定有點奇怪?

其實不奇怪,在算法服務中甚至很常見,舉個例子你就明白了。

同樣是處理多張圖片,為了簡單,我就假設是三張吧。如果是用單個cpu去處理的話。那不管是并發(fā)還是batch進來,由于cpu內部的計算單元有限,所以你可以簡單理解為,這三張圖片,就是串行去計算的。

圖片

cpu處理圖片時的流程

我計算第一張圖片是否能審核通過,跟第二張圖片是否能審核通過,這兩者沒有邏輯關聯(lián),因此按道理兩張圖片是可以并行計算。

奈何我CPU計算單元有限啊,做不到啊。

但是。

如果我打破計算單元有限的這個條件,給CPU加入超多計算單元,并且弱化一些對于計算沒啥用處的組件,比如cache和控制單元。那我們就有足夠的算力可以讓這些圖片的計算并行起來了。

圖片

并行處理圖片

是的,把CPU這么一整,它其實就變成了GPU。

圖片

GPU和CPU的區(qū)別

上面的講解只是為了方便理解,實際上,gpu會以更細的粒度去做并發(fā)計算,比如可以細到圖片里的像素級別。

這也是為什么如果我們跑一些3d游戲的時候,需要用到顯卡,因為它可以快速的并行計算畫面里每個地方的光影,遠近效果啥的,然后渲染出畫面。

回到為什么要搞成batch call的問題中。

其實一次算法服務調用中,在數(shù)據(jù)真正進入GPU前,其實也使用了CPU做一些前置處理。

因此,我們可以簡單的將一次調用的時間理解成做了下面這些事情。

圖片

GPU處理圖片時的流程

服務由CPU邏輯和GPU處理邏輯組成,調用進入服務后,會有一些前置邏輯,它需要CPU來完成,然后才使用GPU去進行并行計算,將結果返回后又有一些后置的CPU處理邏輯。中間的GPU部分,管是計算1張圖,還是計算100張圖,只要算力支持,那它們都是并行計算的,耗時都差不多。

如果把這多張圖片拆開,并發(fā)去調用這個算法服務,那就有 N組這樣的CPU+GPU的消耗,而中間的并行計算,其實沒有利用到位。

并且還會多了前置和后置的CPU邏輯部分,算法服務一般都是python服務,主流的一些web框架幾乎都是以多進程,而不是多線程的方式去處理外部請求,這就有可能導致額外的進程間切換消耗。

當并發(fā)的請求多了,請求處理不過來,后邊來的請求就需要等前邊的處理完才能被處理,后面的請求耗時看起來就會變得特別大。這也是上面圖1里,接口延時(latency)像過山車那樣往上漲的原因。

圖片

還是上面的圖1的截圖,一張圖用兩次哈哈

按理說減少并發(fā),增大每次調用時的圖片數(shù)量,就可以解決這個問題。

這就是推薦batch call的原因。

但問題又來了。

每次調用,上游服務輸入的是一個視頻以及它的幾張圖片,調用下游時,batch的數(shù)量按道理就只能是這幾張圖片的數(shù)量,怎么才能增大batch的數(shù)量呢?

這里的調用,就需要分為同步調用和異步調用了。

同步調用和異步調用的區(qū)別

同步調用,意思是上游發(fā)起請求后,阻塞等待,下游處理邏輯后返回結果給上游。常見的形式就像我們平時做的http調用一樣。

圖片同步調用

異步調用,意思是上游發(fā)起請求后立馬返回,下游收到消息后慢慢處理,處理完之后再通過某個形式通知上游。常見的形式是使用消息隊列,也就是mq。將消息發(fā)給mq后,下游消費mq消息,觸發(fā)處理邏輯,然后再把處理結果發(fā)到mq,上游消費mq的結果。

圖片

異步調用

異步調用的形式接入

圖片

異步調用的實現(xiàn)方式

回到我們文章開頭提到的例子,當上游服務收到一個請求(一個視頻和它對應的圖片),這時候上游服務作為生產(chǎn)者將這個數(shù)據(jù)寫入到mq中,請求返回。然后新造一個C服務,負責批量消費mq里的消息。這時候服務C就可以根據(jù)下游服務的性能控制自己的消費速度,比如一次性消費10條數(shù)據(jù)(視頻),每個數(shù)據(jù)下面掛了10個圖片,那我一次batch的圖片數(shù)量就是10*10=100張,原來的10次請求就變?yōu)榱?次請求。這對下游就相當?shù)挠押昧恕?/p>

下游返回結果后,服務C將結果寫入到mq的另外一個topic下,由上游去做消費,這樣就結束了整個調用流程。

當然上面的方案,如果你把mq換成數(shù)據(jù)庫,一樣是ok的,這時候服務C就可以不斷的定時輪詢數(shù)據(jù)庫表,看下哪些請求沒處理,把沒處理的請求批量撈出來再batch call下游。不管是mq還是數(shù)據(jù)庫,它們的作用無非就是作為中轉,暫存數(shù)據(jù),讓服務C根據(jù)下游的消費能力,去消費這些數(shù)據(jù)。

這樣不管后續(xù)要加入多少個新服務,它們都可以在原來的基礎上做擴展,如果是mq,加topic,如果是數(shù)據(jù)庫,則加數(shù)據(jù)表,每個新服務都可以根據(jù)自己的消費能力去調整消費速度。

圖片

mq串聯(lián)多個不同性能的服務

其實對于這種上下游服務處理性能不一致的場景,最適合用的就是異步調用。而且涉及到的服務性能差距越大,服務個數(shù)越多,這個方案的優(yōu)勢就越明顯。

同步調用的方式接入

雖然異步調用在這種場景下的優(yōu)勢很明顯,但也有個缺點,就是它需要最上游的調用方能接受用異步的方式去消費結果。其實涉及到算法的服務調用鏈,都是比較耗時的,用異步接口非常合理。但合理歸合理,有些最上游他不一定聽你的,就是不能接受異步調用。

這就需要采用同步調用的方案,但怎么才能把同步接口改造得更適合這種調用場景,這也是這篇文章的重點。

限流

如果直接將請求打到下游算法服務,下游根本吃不消,因此首先需要做的就是給在上游調用下游的地方,加入一個速率限制(rate limit)。

這樣的組件一般也不需要你自己寫,幾乎任何一個語言里都會有現(xiàn)成的。

比如golang里可以用golang.org/x/time/rate庫,它其實是用令牌桶算法實現(xiàn)的限流器。如果不知道令牌桶是啥也沒關系,不影響理解。

圖片

限流器邏輯

當然,這個限制的是當前這個服務調用下游的qps,也就是所謂的單節(jié)點限流。如果是多個服務的話,網(wǎng)上也有不少現(xiàn)成的分布式限流框架。但是,還是那句話,夠用就好。

限流只能保證下游算法服務不被壓垮,并不能提升單次調用batch的圖片數(shù)量,有沒有什么辦法可以解決這個問題呢?

參考Nagle算法的做法

我們熟悉的TCP協(xié)議里,有個算法叫Nagle算法,設計它的目的,就是為了避免一次傳過少數(shù)據(jù),提高數(shù)據(jù)包的有效數(shù)據(jù)負載。

當我們想要發(fā)送一些數(shù)據(jù)包時,數(shù)據(jù)包會被放入到一個緩沖區(qū)中,不立刻發(fā)送,那什么時候會發(fā)送呢?

數(shù)據(jù)包會在以下兩個情況被發(fā)送:

  • 緩沖區(qū)的數(shù)據(jù)包長度達到某個長度(MSS)時。
  • 或者等待超時(一般為200ms)。在超時之前,來的那么多個數(shù)據(jù)包,就是湊不齊MSS長度,現(xiàn)在超時了,不等了,立即發(fā)送。

這個思路就非常值得我們參考。我們完全可以自己在代碼層實現(xiàn)一波,實現(xiàn)也非常簡單。

1.我們定義一個帶鎖的全局隊列(鏈表)。

2.當上游服務輸入一個視頻和它對應的N張圖片時,就加鎖將這N張圖片數(shù)據(jù)和一個用來存放返回結果的結構體放入到全局隊列中。然后死循環(huán)讀這個結構體,直到它有結果。就有點像阻塞等待了。

3.同時在服務啟動時就起一個線程A專門用于收集這個全局隊列的圖片數(shù)據(jù)。線程A負責發(fā)起調用下游服務的請求,但只有在下面兩個情況下會發(fā)起請求

  • 當收集的圖片數(shù)量達到xx張的時候
  • 距離上次發(fā)起請求過了xx毫秒(超時)

4.調用下游結束后,再根據(jù)一開始傳入的數(shù)據(jù),將調用結果拆開來,送回到剛剛提到的用于存放結果的結構體中。

5.第2步里的死循環(huán)因為存放返回結果的結構體,有值了,就可以跳出死循環(huán),繼續(xù)執(zhí)行后面的邏輯。

圖片

batch_call同步調用改造

這就像公交車站一樣,公交車站不可能每來一個顧客就發(fā)一輛公交車,當然是希望車里顧客越多越好。上游每來一個請求,就把請求里的圖片,也就是乘客,塞到公交車里,公交車要么到點發(fā)車(向下游服務發(fā)起請求),要么車滿了,也沒必要等了,直接發(fā)車。這樣就保證了每次發(fā)車的時候公交車里的顧客數(shù)量足夠多,發(fā)車的次數(shù)盡量少。

大體思路就跟上面一樣,如果是用go來實現(xiàn)的話,就會更加簡單。

比如第1步里的加鎖全局隊列可以改成有緩沖長度的channel。第2步里的"用來存放結果的結構體",也可以改成另一個無緩沖channel。執(zhí)行 res := <-ch, 就可以做到阻塞等待的效果。

而核心的仿Nagle的代碼也大概長下面這樣。當然不看也沒關系,反正你已經(jīng)知道思路了。

func CallAPI() error {
size := 100
// 這個數(shù)組用于收集視頻里的圖片,每個 IVideoInfo 下都有N張圖片
videoInfos := make([]IVideoInfo, 0, size)
// 設置一個200ms定時器
tick := time.NewTicker(200 * time.Microsecond)
defer tick.Stop()
// 死循環(huán)
for {
select {
// 由于定時器,每200ms,都會執(zhí)行到這一行
case <-tick.C:
if len(videoInfos) > 0 {
// 200ms超時,去請求下游
limitStartFunc(videoInfos, true)
// 請求結束后把之前收集的數(shù)據(jù)清空,重新開始收集。
videoInfos = make([]IVideoInfo, 0, size)
}
// AddChan就是所謂的全局隊列
case videoInfo, ok := <-AddChan:
if !ok {
// 通道關閉時,如果還有數(shù)據(jù)沒有去發(fā)起請求,就請求一波下游服務
limitStartFunc(videoInfos, false)
videoInfos = make([]IVideoInfo, 0, size)
return nil
} else {
videoInfos = append(videoInfos, videoInfo)
if videoInfos 內的圖片滿足xx數(shù)量 {
limitStartFunc(videoInfos, false)
videoInfos = make([]IVideoInfo, 0, size)
// 重置定時器
tick.Reset(200 * time.Microsecond)
}
}
}
}
return nil
}

通過這一操作,上游每來一個請求,都會將視頻里的圖片收集起來,堆到一定張數(shù)的時候再統(tǒng)一請求,大大提升了每次batch call的圖片數(shù)量,同時也減少了調用下游服務的次數(shù)。真·一舉兩得。

優(yōu)化的效果也比較明顯,上游服務支持的qps從原來不穩(wěn)定的3q~15q變成穩(wěn)定的90q。下游的接口耗時也變得穩(wěn)定多了,從原來的過山車似的飆到15s變成穩(wěn)定的500ms左右。處理的圖片的速度也從原來20qps提升到350qps。

到這里就已經(jīng)大大超過業(yè)務需求的預期(40qps)了,夠用就好,多一個qps都是浪費。

可以了,下班吧。

圖片

圖片

總結

  • 為了充分利用GPU并行計算的能力,不少算法服務會希望上游通過加大batch的同時減少并發(fā)的方式進行接口調用。
  • 對于上下游性能差距明顯的服務,建議配合mq采用異步調用的方式將服務串聯(lián)起來。
  • 如果非得使用同步調用的方式進行調用,建議模仿Nagle算法的形式,攢一批數(shù)據(jù)再發(fā)起請求,這樣既可以增大batch,同時減少并發(fā),真·一舉兩得,親測有效。

最后

講了那么多可以提升性能的方式,現(xiàn)在需求來了,如果你資源充足,但時間不充足,那還是直接同步調用一把梭吧。

性能不夠?下游加機器,gpu卡,買!

然后下個季度再提起一個技術優(yōu)化,性能提升xx%,cpu,gpu減少xx%。

有沒有聞到?

這是KPI的味道。

責任編輯:武曉燕 來源: 小白debug
相關推薦

2024-03-15 15:20:10

并發(fā)服務IP

2014-04-14 15:54:00

print()Web服務器

2017-11-13 13:33:09

MySQL全備份恢復表

2022-04-06 08:47:03

Dubbo服務協(xié)議

2013-08-15 10:00:07

產(chǎn)品產(chǎn)品經(jīng)理優(yōu)秀的產(chǎn)品

2009-06-26 15:48:23

Windows Mob

2022-09-13 08:01:58

短鏈服務哈希算法字符串

2021-05-20 13:22:31

架構運維技術

2025-05-20 08:00:00

鏈式調用異步

2015-10-10 11:09:48

NFVNFVI網(wǎng)絡虛擬化

2022-11-08 08:35:53

架構微服務移動

2017-04-11 16:16:48

HTTPS互聯(lián)網(wǎng)服務端

2022-05-22 13:55:30

Go 語言

2021-07-28 14:59:08

鴻蒙HarmonyOS應用

2024-05-24 08:31:49

服務器聯(lián)網(wǎng)SSH

2010-07-22 12:15:59

Batch Telne

2025-02-11 08:20:00

DeepseekAIOPS人工智能

2021-04-26 18:13:37

微服務模式數(shù)據(jù)庫

2019-11-13 14:00:48

Java架構微服務

2023-09-11 10:53:32

點贊
收藏

51CTO技術棧公眾號