1. 程式人生 > >誰是效能殺手?Kafka多Topic下啟用SSL時延增大問題分析

誰是效能殺手?Kafka多Topic下啟用SSL時延增大問題分析

問題背景

專案中將Kafka介面進行RESTful封裝,在使用RESTful介面進行效能測試時,發現Topic數增多後,開啟SSL與非SSL進行測試,發現開啟SSL後效能下降得厲害。例如600個Topic總數每個Topic3分割槽3副本的場景下,使用1200個執行緒只發送10個Topic,開啟SSL的TPS只有3100,但是不開啟SSL效能達到11000。

 

其中測試客戶端會啟動多個執行緒,每個執行緒採用同步傳送的方式呼叫RESTful API傳送,即每次傳送成功一條後才傳送下一條。 客戶端會根據傳送執行緒在Topic數之間進行均分,例如1200個執行緒傳送10個Topic,則每個Topic同時有120個執行緒進行傳送。

 

定位與分析過程

1.SSL效能下降

1.定位分析

開啟SSL是會導致效能下降的, 主要來自於CPU的耗時與JVM的具體實現,參見Kafka官網的解釋:

 

從我們之前測試的結果來看,高可靠場景SSL效能下降並沒有太厲害(從2.3W TPS下降到2.1W TPS)。應該是觸發了某些其他問題。通過JStack檢視啟動SSL下的堆疊,發現存在一些傳送執行緒被Block住:

 

這個堆疊裡面做的事情,是來自於java.security.SecureRandom要產生隨機數,採用”SHA1PRNG”演算法。在sun/oracle的jdk裡,這個隨機演算法的的實現在底層依賴到作業系統提供的隨機資料,預設用的是/dev/random,在讀取時,/dev/random裝置會返回小於熵池噪聲總數的隨機位元組。/dev/random可生成高隨機性的公鑰或一次性密碼本。若熵池空了,對/dev/random的讀操作將會被阻塞,直到收集到了足夠的環境噪聲為止。這個問題在網上也查到,主要是JDK提供的SecureRandom函式存在1個全域性的鎖,在熵源不足且SSL執行緒多的時候很容易碰到這個問題,具體見:

https://github.com/netty/netty/issues/3639

http://bugs.java.com/view_bug.do?bug_id=6521844

 

2.解決措施

措施一:更新JDK

目前這個問題是在OpenJDK 1.8中解決了,可以通過升級JDK到使用OpenJDK,但是這個方案不太好替換,並且OpenJDK和原來有什麼不相容還不清楚。

措施二:採用非阻塞的熵源: /dev/urandom

通過設定-Djava.security.egd=file:/dev/./urandom巨集,隨機數時選擇/dev/urandom,它會重複地使用熵池中的資料以產生偽隨機資料避免阻塞,不過隨機安全性會降低。

 

2.Topic多情況下效能下降

1.定位分析

發現在Topic600個情況下,非SSL與SSL的時延其實差距並沒有原先發現的問題那麼大,以下是我們用SDK介面測試的時延資料:

600個 Topic總量下,400個執行緒同時傳送10個Topic,非SSL與SSL時延對比:

可以看出時延差距在20%之內,主要的時延增加來自於Topic增多導致的。 

為什麼Topic增多會導致時延增多?針對這個問題通過在程式進行打點測試,以下是在不同的Topic數量情況下,針對10個Topic,總髮送5000條訊息的場景下,非SSL時延對比:

其中總時延 = 訊息的待發送佇列等待時延 + 服務端處理平均時延 + 網路傳送與響應時延。

 

從上面的表格可以看出基本上每個處理環節上都增加了時延4~5倍。為什麼會出現這種情況?分析如下可能點:

1、磁碟的寫速度變慢

2、Server由於Topic多需要過濾資訊變慢

3、複製處理在多Topic下變慢。即使無資料,多Topic下複製執行緒也會一直髮送空請求

4、Topic多資源佔用大

 

通過逐一分析、排除與測試,主要原因還是在第三點:服務端在複製處理在Topic數量多的情況下變慢導致的。

 

例如10個Topic的時候,如果用10個複製執行緒(目前效能測試就是配置10)用於副本複製,則每個複製執行緒會分配到1個Topic;而當Topic有600個的時候,如果還是10個複製執行緒用於副本複製,則每個複製執行緒會分配到60個Topic。 如果此時只發送前10個Topic的時候,很有可能只有1個複製執行緒在工作,其他的複製執行緒由於分配到的Topic沒有資料,基本處於空閒狀態。

 

2.解決措施

既然複製執行緒變慢,我們可以通過繼續增加複製執行緒的方式提高效能,在600個Topic場景只發送10個Topic場景下,我們把複製執行緒提升到60個,這樣10個Topic能儘可能分配到不同的複製執行緒中,提高了複製的速度。以下是實際測試結果:

可以看到增加到60個fetch執行緒後,時延變為100ms左右。同時原來的環境下,通過增加複製執行緒(修改配置num.replica.fetchers=60),在原環境下1200個傳送執行緒即使啟動SSL,效能也能達到11000+。

 

效能提升措施總結

RESTful API是同步介面,但是內部使用的SDK介面是非同步傳送。根據高可靠場景下非同步傳送的能力能達到2W+ TPS來看,主要還是同步介面的併發壓力上不去導致的,可以通過以下措施來改進:

1、增加請求等待時間linger.ms

通過在客戶端增加引數linger.ms,使得每個請求回等待指定的時間後再發送,使得每個請求可以傳送更多的資料,即增加合包率。

2、增加同步傳送對同1個Topic的併發數量

3、減少Topic的分割槽數

因為目前RESTful API並沒有用盡服務端的能力(1個分割槽的能力瓶頸還沒達到),預設的3個分割槽是浪費資源,並且會導致合包率降低,如果採用1個分割槽,則同樣的壓力下,合包率能提升3倍,這樣效能也能提升。這個措施還可以支援更多的Topic數。

4、增加複製執行緒

5、考慮提供非同步傳送SDK介面