成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

優化 Golang 分布式行情推送的性能瓶頸

開發 前端 分布式
最近一直在優化行情推送系統,有不少優化心得跟大家分享下。性能方面提升最明顯的是時延,在單節點8萬客戶端時,時延從1500ms優化到40ms,這里是內網mock客戶端的得到的壓測數據。

[[409249]]

本文轉載自微信公眾號「碼農桃花源」,作者峰云就她了 。轉載本文請聯系碼農桃花源公眾號。

最近一直在優化行情推送系統,有不少優化心得跟大家分享下。性能方面提升最明顯的是時延,在單節點8萬客戶端時,時延從1500ms優化到40ms,這里是內網mock客戶端的得到的壓測數據。

對于訂閱客戶端數沒有太執著量級的測試,弱網絡下單機8w客戶端是沒問題的。當前采用的是kubenetes部署方案,可靈活地擴展擴容。

架構圖

push-gateway是推送的網關,有這么幾個功能:第一點是為了做鑒權;第二點是為了做接入多協議,我們這里實現了websocket, grpc, grpc-web,sse的支持;第三點是為了實現策略調度及親和綁定等。

push-server 是推送服務,這里維護了訂閱關系及監聽mq的新消息,繼而推送到網關。

問題一:并發操作map帶來的鎖競爭及時延

推送的服務需要維護訂閱關系,一般是用嵌套的map結構來表示,這樣造成map并發競爭下帶來的鎖競爭和時延高的問題。

  1. // xiaorui.cc  
  2. {"topic1": {"uuid1": client1, "uuid2": client2}, "topic2": {"uuid3": client3,  "uuid4": client4}   ... }  

已經根據業務拆分了4個map,但是該訂閱關系是嵌套的,直接上鎖會讓其他協程都阻塞,阻塞就會造成時延高。

加鎖操作map本應該很快,為什么會阻塞?上面我們有說過該map是用來存topic和客戶端列表的訂閱關系,當我進行推送時,必然是需要拿到該topic的所有客戶端,然后進行一個個的send通知。(這里的send不是io.send,而是chan send,每個客戶端都綁定了緩沖的chan)

解決方法:在每個業務里劃分256個map和讀寫鎖,這樣鎖的粒度降低到1/256。除了該方法,開始有嘗試過把客戶端列表放到一個新的slice里返回,但造成了 GC 的壓力,經過測試不可取。

  1. // xiaorui.cc 
  2.  
  3. sync.RWMutex 
  4. map[string]map[string]client 
  5.  
  6. 改成這樣 
  7.  
  8. m *shardMap.shardMap 

分段map的庫已經推到github[1]了,有興趣的可以看看。

問題二:串行消息通知改成并發模式

簡單說,我們在推送服務維護了某個topic和1w個客戶端chan的映射,當從mq收到該topic消息后,再通知給這1w個客戶端chan。

客戶端的chan本身是有大buffer,另外發送的函數也使用 select default 來避免阻塞。但事實上這樣串行發送chan耗時不小。對于channel底層來說,需要goready等待channel的goroutine,推送到runq里。

下面是我寫的benchmark[2],可以對比串行和并發的耗時對比。在mac下效果不是太明顯,因為mac cpu頻率較高,在服務器里效果明顯。

串行通知,拿到所有客戶端的chan,然后進行send發送。

  1. for _, notifier := range notifiers { 
  2.     s.directSendMesg(notifier, mesg) 

并發send,這里使用協程池來規避morestack的消耗,另外使用sync.waitgroup里實現異步下的等待。

  1. // xiaorui.cc 
  2.  
  3. notifiers := []*mapping.StreamNotifier{} 
  4. // conv slice 
  5. for _, notifier := range notifierMap { 
  6.     notifiers = append(notifiers, notifier) 
  7.  
  8.  
  9. // optimize: direct map struct 
  10. taskChunks := b.splitChunks(notifiers, batchChunkSize) 
  11.  
  12.  
  13. // concurrent send chan 
  14. wg := sync.WaitGroup{} 
  15. for _, chunk := range taskChunks { 
  16.     chunkCopy := chunk // slice replica 
  17.     wg.Add(1) 
  18.     b.SubmitBlock( 
  19.         func() { 
  20.             for _, notifier := range chunkCopy { 
  21.                 b.directSendMesg(notifier, mesg) 
  22.             } 
  23.             wg.Done() 
  24.         }, 
  25.     ) 
  26. wg.Wait() 

按線上的監控表現來看,時延從200ms降到30ms。這里可以做一個更深入的優化,對于少于5000的客戶端,可直接串行調用,反之可并發調用。

問題三:過多的定時器造成cpu開銷加大

行情推送里有大量的心跳檢測,及任務時間控速,這些都依賴于定時器。go在1.9之后把單個timerproc改成多個timerproc,減少了鎖競爭,但四叉堆數據結構的時間復雜度依舊復雜,高精度引起的樹和鎖的操作也依然頻繁。

所以,這里改用時間輪解決上述的問題。數據結構改用簡單的循環數組和map,時間的精度弱化到秒的級別,業務上對于時間差是可以接受的。

Golang時間輪的代碼已經推到github[3]了,時間輪很多方法都兼容了golang time原生庫。有興趣的可以看下。

問題四:多協程讀寫chan會出現send closed panic的問題

解決的方法很簡單,就是不要直接使用channel,而是封裝一個觸發器,當客戶端關閉時,不主動去close chan,而是關閉觸發器里的ctx,然后直接刪除topic跟觸發器的映射。

  1. // xiaorui.cc 
  2.  
  3. // 觸發器的結構 
  4. type StreamNotifier struct { 
  5.     Guid  string 
  6.     Queue chan interface{} 
  7.  
  8.  
  9.     closed int32 
  10.     ctx    context.Context 
  11.     cancel context.CancelFunc 
  12.  
  13.  
  14. func (sc *StreamNotifier) IsClosed() bool { 
  15.     if sc.ctx.Err() == nil { 
  16.         return false 
  17.     } 
  18.     return true 
  19.  
  20. ... 

問題五:提高grpc的吞吐性能

grpc是基于http2協議來實現的,http2本身實現流的多路復用。通常來說,內網的兩個節點使用單連接就可以跑滿網絡帶寬,無性能問題。但在golang里實現的grpc會有各種鎖競爭的問題。

如何優化?多開grpc客戶端,規避鎖競爭的沖突概率。測試下來qps提升很明顯,從8w可以提到20w左右。

可參考以前寫過的grpc性能測試[4]。

問題六:減少協程數量

有朋友認為等待事件的協程多了無所謂,只是占內存,協程拿不到調度,不會對runtime性能產生消耗。這個說法是錯誤的。雖然拿不到調度,看起來只是占內存,但是會對 GC 有很大的開銷。所以,不要開太多的空閑的協程,比如協程池開的很大。

在推送的架構里,push-gateway到push-server不僅幾個連接就可以,且幾十個stream就可以。我們自己實現大量消息在十幾個stream里跑,然后調度通知。在golang grpc streaming的實現里,每個streaming請求都需要一個協程去等待事件。所以,共享stream通道也能減少協程的數量。

問題七:GC 問題

對于頻繁創建的結構體采用sync.Pool進行緩存。有些業務的緩存先前使用list鏈表來存儲,在不斷更新新數據時,會不斷的創建新對象,對 GC 造成影響,所以改用可復用的循環數組來實現熱緩存。

后記

有坑不怕,填上就可以了。

參考資料

[1]github: https://github.com/rfyiamcool/ccmap/blob/master/syncmap.go

[2]benchmark: https://github.com/rfyiamcool/go-benchmark/tree/master/batch_notify_channel

[3]github: https://github.com/rfyiamcool/go-timewheel

[4]測試: https://github.com/rfyiamcool/grpc_batch_test

 

責任編輯:武曉燕 來源: 碼農桃花源
相關推薦

2022-08-16 09:23:54

分布式系統

2023-11-19 23:24:21

Golang開發

2010-07-06 09:39:20

SQL Server分

2019-06-19 15:40:06

分布式鎖RedisJava

2024-05-20 09:14:20

2019-10-10 09:16:34

Zookeeper架構分布式

2023-05-12 08:23:03

分布式系統網絡

2017-09-01 05:35:58

分布式計算存儲

2023-05-29 14:07:00

Zuul網關系統

2022-12-08 08:13:11

分布式數據庫CAP

2022-06-08 07:36:03

LocustKubernete微服務

2018-07-19 14:53:23

秒殺websocket異步

2023-02-11 00:04:17

分布式系統安全

2017-10-27 08:40:44

分布式存儲剪枝系統

2024-05-23 10:19:57

2023-10-26 18:10:43

分布式并行技術系統

2024-01-10 08:02:03

分布式技術令牌,

2018-07-17 08:14:22

分布式分布式鎖方位

2024-03-01 09:53:34

2014-02-19 11:37:57

分布式對象存儲Sheepdog
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久草免费在线视频 | 亚洲二区视频 | 国产精品国产精品国产专区不片 | av福利网 | 男人天堂av网| 国产成人免费视频网站视频社区 | 精品欧美一区二区在线观看视频 | 最新国产视频 | 狠狠操狠狠干 | 免费在线a视频 | 99精品久久久国产一区二区三 | 日本一区精品 | 国产亚洲精品一区二区三区 | 国产精品中文字幕在线 | 日本高清中文字幕 | 性高朝久久久久久久3小时 av一区二区三区四区 | 国产精品一区二区欧美黑人喷潮水 | 国产一区不卡在线观看 | 亚洲精品中文字幕在线观看 | 国产精品不卡 | 中文字幕在线播放不卡 | 亚洲精品久久久久中文字幕欢迎你 | 男女网站视频 | 国产一区二区日韩 | 亚洲成人av| 中文字幕一区二区三区精彩视频 | 久久美女网 | 国产在线精品一区二区 | 久久精品亚洲欧美日韩精品中文字幕 | 操久久| 国产高清视频在线播放 | 国产电影一区二区 | 国产成人精品一区二区三区四区 | 欧美精品一区在线 | 日韩av福利在线观看 | 精品久久九九 | 欧美精品一区二区免费视频 | 久久久久国产一区二区三区四区 | 免费成人av | 婷婷综合色 | 国产在线a|