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

高并發(fā)服務(wù)遇redis瓶頸引發(fā)time-wait事故

開發(fā) 前端 Redis
元旦期間 訂單業(yè)務(wù)線 告知 推送系統(tǒng) 無法正常收發(fā)消息,作為推送系統(tǒng)維護者的我正外面瀟灑,無法第一時間回去,直接讓 ops 幫忙重啟服務(wù),一切好了起來,重啟果然是個大殺器。

摘要

元旦期間 訂單業(yè)務(wù)線 告知 推送系統(tǒng) 無法正常收發(fā)消息,作為推送系統(tǒng)維護者的我正外面瀟灑,無法第一時間回去,直接讓 ops 幫忙重啟服務(wù),一切好了起來,重啟果然是個大殺器。由于推送系統(tǒng)本身是分布式部署,消息有做各種的可靠性策略,所以重啟是不會丟失消息事件的。

事后通過日志分析有大量的 redis 的報錯,十分鐘內(nèi)有 16w 次的錯誤。日志的錯誤是 connect: cannot assign requested address 。該錯誤不是推送服務(wù)內(nèi)部及 redis 庫返回的 error,而是系統(tǒng)回饋的 errno 錯誤。

這個錯誤是由于無法申請可用地址引起的,也就是無法申請到可用的 socket。

話說,元旦當天在線數(shù)和訂單量確實大了不少,往常推送系統(tǒng)的長連接客戶端在 35w,這次峰值飆到 50w 左右, 集群共 6 個節(jié)點,其中有 4 個節(jié)點每個都抗了 9w+ 的長連接。另外,推送的消息量也隨之翻倍。

分析

下面是 kibana 日志的統(tǒng)計,出錯的時間區(qū)間里有近 16w 次的 redis 報錯。

下面是出問題節(jié)點的 TCP 連接狀況,可以看到 established 在 6w,而 time-wait 連接干到 2w 多個。

為什么會產(chǎn)生這么多 time-wait?誰主動關(guān)閉就就有 time-wait,但推送系統(tǒng)除了協(xié)議解析失敗之外,其余情況都不會主動 close 客戶端,哪怕是鑒權(quán)失敗和弱網(wǎng)絡(luò)客戶端寫緩沖爆滿,事后通過日志也確定了不是推送系統(tǒng)自身產(chǎn)生的 tw。

另外,linux 主機被 ops 交付時應(yīng)該有做內(nèi)核調(diào)優(yōu)初始化的,在開啟 tw_reuse 參數(shù)后,time-wait 是可以復(fù)用的。難道是沒開啟 reuse?

查看 sysctl.conf 的內(nèi)核參數(shù)得知,果然 tcp_tw_reuse 參數(shù)沒有打開,不能快速地復(fù)用還處在 time-wait 狀態(tài)的地址,只能等待 time-wait 的超時關(guān)閉,rfc 協(xié)議里規(guī)定等待 2 分鐘左右,開啟 tw_reuse可在 1s 后復(fù)用該地址。另外 ip_local_port_range 端口范圍也不大,縮短了可用的連接范圍。

  1. sysctl  -a|egrep "tw_reuse|timestamp|local_port" 
  2.  
  3. net.ipv4.ip_local_port_range = 35768    60999 
  4. net.ipv4.tcp_timestamps = 1 
  5. net.ipv4.tcp_tw_reuse = 0 

所以,由于沒有可用地址才爆出了 connect: cannot assign requested address 錯誤。

內(nèi)在問題

追究問題

上面是表象問題,來查查為什么會有這么多的 time-wait ?再說一遍,通常哪一端主動 close fd,哪一端就會產(chǎn)生 time-wait。事后通過 netstat 得知 time-wait 連接基本是來自 redis 主機。

下面是推送代碼中的連接池配置,空閑連接池只有 50,最大可以 new 的連接可以到 500 個。這代表當有大量請求時,企圖先從 size 為 50 的連接池里獲取連接,如果拿不到連接則 new 一個新連接,連接用完了后需要歸還連接池,如果這時候連接池已經(jīng)滿了,那么該連接會主動進行 close 關(guān)閉。

  1. MaxIdle   = 50 
  2. MaxActive = 500 
  3. Wait      = false 

除此之外,還發(fā)現(xiàn)一個問題。有幾處 redis 的處理邏輯是異步的,比如每次收到心跳包都會 go 一個協(xié)程去更新 redis, 這也加劇了連接池的搶奪,改為同步代碼。這樣在一個連接上下文中同時只對一個 redis 連接操作。

解決方法

調(diào)大 golang redis client 的 maxIdle 連接池大小,避免了高下無空閑連接而新建連接和池子爆滿又不能歸還連接的尷尬場面。當 pool wait 為 true 時,意味著如果空閑池中沒有可用的連接,且當前已建立連接的連接數(shù)大于 MaxActive 最大空閑數(shù),則一直阻塞等待其他人歸還連接。反之直接返回 “connection pool exhausted” 錯誤。

  1. MaxIdle   = 300 
  2. MaxActive = 400 
  3. Wait      = true 

redis 的 qps 性能瓶頸

redis 的性能一直是大家所稱贊的,在不使用 redis 6.0 multi io thread 下,QPS 一般可以在 13w 左右,如果使用多指令和 pipeline 的話,可以干到 40w 的 OPS 命令數(shù),當然 qps 還是在 12w-13w 左右。

Redis QPS 高低跟 redis 版本和 cpu hz、cache 存在正比關(guān)系

根據(jù)我的經(jīng)驗,在內(nèi)網(wǎng)環(huán)境下且已實例化連接對象,單條 redis 指令請求耗時通常在 0.2ms 左右,200us 已經(jīng)夠快了,但為什么還會有大量因 redis client 連接池無空閑連接而建立新連接的情況?

通過 grafana 監(jiān)控分析 redis 集群,發(fā)現(xiàn)有幾個節(jié)點 QPS 已經(jīng)到了 Redis 單實例性能瓶頸,QPS 干到了近 15w 左右。難怪不能快速處理來自業(yè)務(wù)的 redis 請求。這個瓶頸必然會影響請求的時延。請求的時延都高了,連接池不能及時返回連接池,所以就造成了文章開頭說的問題。總之,業(yè)務(wù)流量的暴增引起了一系列問題。

發(fā)現(xiàn)問題,那么就要解決問題,redis 的 qps 優(yōu)化方案有兩步:

  • 擴容 redis 節(jié)點,遷移 slot 使其分擔流量
  • 盡量把程序中 redis 的請求改成批量模式

增加節(jié)點容易,批量也容易。起初在優(yōu)化推送系統(tǒng)時,已經(jīng)把同一個邏輯中的 redis 操作改為批量模式了。但問題來了,很多的 redis 操作在不同的邏輯塊里面,沒法合成一個 pipeline。

然后做了進一步的優(yōu)化,把不同邏輯中的 redis 請求合并到一個 pipeline 里,優(yōu)點在于提高了 redis 的吞吐,減少了 socket 系統(tǒng)調(diào)用、網(wǎng)絡(luò)中斷開銷,缺點是增加了邏輯復(fù)雜度,使用 channal 管道做隊列及通知增加了 runtime 調(diào)度開銷,pipeline worker 觸發(fā)條件是滿足 3 個 command 或 5ms 超時,定時器采用分段的時間輪。

對比優(yōu)化修改前,cpu開銷減少了 3% 左右,壓測下redis qps平均降了 3w 左右差值,最多可以降到 7w 左右,當然概率上消息的時延會高了幾個ms。

實現(xiàn)的邏輯參考下圖,調(diào)用方把redis command和接收結(jié)果的chan推送到任務(wù)隊列中,然后由一個worker去消費,worker組裝多個redis cmd為pipeline,向redis發(fā)起請求并拿回結(jié)果,拆解結(jié)果集后,給每個命令對應(yīng)的結(jié)果chan推送結(jié)果。調(diào)用方在推送任務(wù)到隊列后,就一直監(jiān)聽傳輸結(jié)果的chan。

這個方案來自我在上家公司做推送系統(tǒng)的經(jīng)驗,有興趣的朋友可以看看 PPT,內(nèi)涵不少golang高并發(fā)經(jīng)驗。

總結(jié)

推送系統(tǒng)設(shè)計之初是預(yù)計 20w 的長連接數(shù),穩(wěn)定后再無優(yōu)化調(diào)整,也一直穩(wěn)定跑在線上。后面隨著業(yè)務(wù)的暴漲,長連接數(shù)也一直跟著暴漲,現(xiàn)在日常穩(wěn)定在 35w,出問題時暴到 50w,我們沒有因為業(yè)務(wù)暴增進行整條鏈路壓測及優(yōu)化。

話說,如果對推送系統(tǒng)平時多上點心也不至于出這個問題。我曾經(jīng)開發(fā)過相對高規(guī)格的推送系統(tǒng),而現(xiàn)在公司的推送系統(tǒng)我是后接手的,由于它的架子一般,但業(yè)務(wù)性又太強,看著腦仁疼,所以就沒有推倒來重構(gòu)。一直是在這個架子上添添補補,做了一些常規(guī)的性能優(yōu)化。嗯,看來不能掉以輕心,免得績效離我遠去。

責任編輯:張燕妮 來源: 峰云就她了
相關(guān)推薦

2020-05-07 11:00:24

Go亂碼框架

2019-12-27 11:13:24

高并發(fā)服務(wù)器邏輯

2023-02-16 08:55:13

2021-08-26 05:04:38

TCP網(wǎng)絡(luò)HTTP

2023-09-03 22:44:28

I/O高并發(fā)

2020-11-11 10:00:13

NAT性能內(nèi)核

2023-07-18 09:24:04

MySQL線程

2018-02-05 09:30:23

高性能高并發(fā)服務(wù)

2024-02-02 11:24:00

I/O高并發(fā)場景

2021-10-08 08:55:23

FacebookBGP工具

2020-12-09 08:59:59

MongoDB復(fù)合索事故

2017-06-09 10:16:40

2022-10-25 18:00:00

Redis事務(wù)生產(chǎn)事故

2016-12-28 14:16:25

京東高并發(fā)系統(tǒng)設(shè)計

2021-07-12 09:09:54

Go 連接池緩存

2021-09-30 14:23:23

服務(wù)器開發(fā)工具

2015-03-27 10:18:25

TCP協(xié)議CLOSE_WAIT狀服務(wù)器異常

2022-05-17 11:46:48

高并發(fā)服務(wù)數(shù)據(jù)庫

2019-10-30 16:54:08

golangredis數(shù)據(jù)庫

2024-08-20 21:27:04

docker部署容器
點贊
收藏

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