訊息佇列為了提高效能使用了哪些細節?

2021-10-12 05:00:39 字數 2710 閱讀 4186

假設使用同步實現,偽裝**如下:

transfer(accountfrom, accountto, amount)

上面的偽**首先從 accountfrom 的賬戶中減去相應的錢數,再把減去的錢數加到 accountto 的賬戶中,這種同步實現是一種很自然方式,簡單直接。那麼效能表現如何呢?假設微服務 add 的平均響應時延是 50ms,那麼可以計算出我們實現的微服務 transfer 的平均響應時延大約等於執行 2 次 add 的時延,也就是 100ms。那隨著呼叫transfer 服務的請求越來越多,會出現什麼情況呢?

在這種實現中,每處理乙個請求需要耗時 100ms,並在這 100ms 過程中是需要獨佔乙個執行緒的:每個執行緒每秒鐘最多可以處理 10 個請求。每台計算機上的執行緒資源並不是無限的,假設我們使用的伺服器同時開啟的執行緒數量上限是 10,000,可以計算出這台伺服器每秒鐘可以處理的請求上限是: 10,000(個執行緒)* 10(次請求每秒) =100,000 次每秒。如果請求速度超過這個值,那麼請求就不能被馬上處理,只能阻塞或者排隊,這時候 transfer 服務的響應時延由 100ms 延長到了:排隊的等待時延 + 處理時延 (100ms)。也就是說,在大量請求的情況下,我們的微服務的平均響應時延變長了。

這是不是已經到了這台伺服器所能承受的極限了呢?其實遠遠沒有,如果我們監測一下伺服器的各項指標,會發現無論是 cpu、記憶體,還是網絡卡流量或者是磁碟的 io 都空閒的很,那我們 transfer 服務中的那 10,000 個執行緒在幹什麼呢?對,絕大部分執行緒都在等待 add 服務返回結果。

採用非同步處理

transferasync(accountfrom, accountto, amount, oncomplete()) 

// 扣減賬戶 accountfrom 完成後呼叫

ondebit(accountto, amount, onalldone(oncomplete()))

// 轉入賬戶 accountto 完成後呼叫

onalldone(oncomplete())

由於沒有了執行緒的數量的限制,總體吞吐量上限會大大超過同步實現,並且在伺服器 cpu、網路頻寬資源達到極限之前,響應時延不會隨著請求數量增加而顯著公升高,可以一直保持約 100ms 的平均響應時延。

比如我們要序列化乙個 user 物件,它包含 3 個屬性,姓名 zhangsan,年齡:23,婚姻狀況:已婚。

user:

name: "zhangsan"

age: 23

married: true

使用 json 序列化後:

實現高效能的序列化,對於同樣的 user 物件,我們可以把它序列化成這樣:

03   | 08 7a 68 61 6e 67 73 61 6e | 17 | 01

user | z h a n g s a n | 23 | true

03 表示這是乙個 user 型別的物件。可以約定按照 name、age、married 這個固定順序來序列化這三個屬性。按照順序,第乙個欄位是 name,不存欄位名,直接存字段值「zhangsan」就可以了,由於名字的長度不固定,用第乙個位元組 08 表示這個名字的長度是 8 個位元組,後面的 8 個位元組就是 zhangsan。第二個欄位是年齡,直接用乙個位元組表示就可以了,23 的 16 進製是 17 。最後乙個欄位是婚姻狀態,用乙個位元組來表示,01 表示已婚,00 表示未婚,這裡面儲存乙個 01。

可以看到,同樣的乙個 user 物件,json 序列化後需要 47 個位元組,這裡只要 12 個位元組就夠了。

pagecache 是現代作業系統都具有的一項基本特性。通俗地說,pagecache 就是作業系統在記憶體中給磁碟上的檔案建立的快取。無論我們使用什麼語言編寫的程式,在呼叫系統的 api 讀寫檔案的時候,並不會直接去讀寫磁碟上的檔案,應用程式實際操作的都是 pagecache,也就是檔案在記憶體中快取的副本。應用程式在寫入檔案的時候,作業系統會先把資料寫入到記憶體中的 pagecache,然後再一批一批地寫到磁碟上。讀取檔案的時候,也是從 pagecache 中來讀取資料,這時候會出現兩種可能情況。

一種是 pagecache 中有資料,那就直接讀取,這樣就節省了從磁碟上讀取資料的時間;另一種情況是,pagecache 中沒有資料,這時候作業系統會引發乙個缺頁中斷,應用程式的讀取執行緒會被阻塞,作業系統把資料從檔案中複製到 pagecache 中,然後應用程式再從 pagecache 中繼續把資料讀出來,這時會真正讀一次磁碟上的檔案,這個讀的過程就會比較慢。使用者的應用程式在使用完某塊 pagecache 後,作業系統並不會立刻就清除這個 pagecache,而是盡可能地利用空閒的物理記憶體儲存這些 pagecache,除非系統記憶體不夠用,作業系統才會清理掉一部分 pagecache。清理的策略一般是 lru 或它的變種演算法,這個演算法我們不展開講,它保留 pagecache 的邏輯是:優先保留最近一段時間最常使用的那些 pagecache。

例如kafka 在讀寫訊息檔案的時候,充分利用了 pagecache 的特性。一般來說,訊息剛剛寫入到服務端就會被消費,按照 lru 的「優先清除最近最少使用的頁」這種策略,讀取的時候,對於這種剛剛寫入的 pagecache,命中的機率會非常高。也就是說,大部分情況下,消費讀訊息都會命中 pagecache,帶來的好處有兩個:乙個是讀取的速度會非常快,另外乙個是,給寫入訊息讓出磁碟的 io 資源,間接也提公升了寫入的效能。 

為了提高效能,怎樣動態載入JS檔案

可是有些檔案是在開啟某些型別的 時才須要載入。比如,僅僅有當開啟甘特圖 時,才須要載入gantetu.js檔案。那麼問題來了,為了降低js檔案數量和請求,怎樣在須要時自己主動載入js檔案呢?辦法例如以下。假設就這樣了,還不完美。由於這是動態非同步載入的。超級 程式中。在執行這種方法後立即會執行js檔...

訊息佇列選型 Kafka 如何實現高效能?

訊息佇列中應用最廣泛的 kafka 和 rocketmq 不知道你有沒有了解過自己電腦的配置?我們一般會認為高效能是和高配置聯絡在一起的,比如大記憶體比小記憶體快,8 核的機器比 4 核的機器快。我身邊也有一些朋友是攢機愛好者,對各種硬體配置如數家珍。對於伺服器來說,家用電腦的效能與配置的關係也同樣...

SQL中使用WITH AS提高效能

1.案例起因公司門店應用程式每天都要出乙份報表,用來統計所有商品當天的期初庫存數量 入庫數量 出庫數量 及當天的期末庫存數量。執行半年以後,這份報表執行越來越慢,到現在,每次執行該報表顯示當天資料時需要近20秒的時間。於是開發人員找到我,希望我看看,是不是可以使該報表執行的時間更短。該報表就是一段s...