您现在的位置是:Instagram刷粉絲, Ins買粉絲自助下單平台, Ins買贊網站可微信支付寶付款 > 

03 Keep如何查看已經訂閱的(追蹤Redis Sentinel的CPU占有率長期接近100%的問題 二)

Instagram刷粉絲, Ins買粉絲自助下單平台, Ins買贊網站可微信支付寶付款2024-05-19 04:59:36【】2人已围观

简介server.maxclients,只要Sentinel不重啟,這個值就不會變化,這也就解釋了為什么Sentinel啟動之后再改變openfiles沒有效果的原因了。那什么時候發生了重寫呢?即“Gen

server.maxclients ,只要Sentinel不重啟,這個值就不會變化,這也就解釋了為什么Sentinel啟動之后再改變open files沒有效果的原因了。

那什么時候發生了重寫呢?即“Generated by CONFIG REWRITE”這句話什么時候會輸出?接著上面,我又在源碼里搜索了“Generated by CONFIG REWRITE”這句話,發現了常量REDIS_CONFIG_REWRITE_SIGNATURE,通過它繼而發現如下調用鏈:

上面的調用鏈中,*表示有很多地方會調用sentinelFlushConfig()。

什么時候調用sentinelFlushConfig()呢?經過查找,發現有很多條件都可以觸發sentinelFlushConfig函數的調用,包括Leader選舉、故障轉移、使用Sentinel set 設置命令、Sentinel處理info信息等等。

而sentinelFlushConfig()則會利用rewriteConfig(),針對具體的配置項,分別進行重寫,最終將Sentinel所有的狀態持久化到了配置文件中。如下所示,在rewriteConfig()中,可以看到非常多的重寫類型, 這些重寫類型都是與redis的各個配置選項一一對應的:

當然,我們只需要找到其中關于max clients的重寫即可,所以在該函數中,我們找到了調用:

可以看到,該函數傳入了Sentinel當前的server.maxclients(已經在啟動時調整過了,前面分析過),以及默認的REDIS_MAX_CLIENTS即10032。該函數作用就是將當前的server.maxclients的值重寫到配置文件中去。什么時候重寫呢,即當默認值與當前值不同的時候(也就是force==true的時候),具體可以查看其源碼,篇幅限制我們不做詳細介紹。

通過前面一大堆的分析,我們可以得出結論:

講到這里,還有一個問題就是,為什么Sentinel服務器會長期持有4000多個Established狀態的TCP連接而不釋放。按目前生產環境的規模,正常情況下業務客戶端使用的Jedis建立的TCP連接不應該有這么多。

經過查看,發現Sentinel上的很多連接在對應的客戶端中并沒有存在。如紅框所示IP10.X.X.74上:

總計有992個連接:

而實際上在10.X.X.74上,只有5個與Sentinel的連接長期存在:

也就是說,在Sentinel中有大量的連接是無效的,客戶端并沒有持有,Sentinel一直沒有釋放。這個問題, 就涉及到了TCP保活的相關知識。

我們首先要了解,操作系統通常會自身提供TCP的keepalive機制,如在linux默認配置下,運行sysctl -a |grep keep,會看到如下信息:

上面表示如果連接的空閑時間超過 7200 秒(2 小時),Linux 就發送保持活動的探測包。每隔75秒發一次,總共發9次,如果9次都失敗的話,表示連接失效。

TCP提供這種機制幫助我們判斷對端是否存活,當TCP檢測到對端不可用時,會出錯并通知上層進行處理。keepalive機制默認是關閉的,應用程序需要使用SO_KEEPALIVE進行啟用。

了解到這個知識之后,我們開始分析。在Redis的源碼中,發現有如下調用鏈:

還記得acceptTcpHandler嗎,acceptTcpHandler是TCP連接的事件處理器,當它為客戶端成功創建了TCP連接后,會通過調用createClient函數為每個連接(fd)創建一個redisClient 實例,這個redisClient 與客戶端是一一對應的。并且,還會設置一些TCP選項,如下所示。

如果用戶在Redis中沒有手動配置tcpkeepalive的話,server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE,默認為0。

由第x-x行我們可以明確,Redis服務器與客戶端的連接默認是關閉保活機制的,因為只有當server.tcpkeepalive不為0(修改配置文件或買粉絲nfig set)時,才能調用a買粉絲KeepAlive方法設置TCP的keepalive選項。

我們知道,Sentinel是特殊模式的Redis,我們無法使用買粉絲nfig set命令去修改其配置,包括tcpkeepalive 參數。所以,當Sentinel啟動后,Sentinel也使用默認的tcpkeepalive ==0這個設置,不會啟用tcpkeepalive ,與客戶端的TCP連接都沒有保活機制。也就是說,Sentinel不會主動去釋放連接,哪怕是失效連接。

但是,TCP連接是雙向的,Sentinel無法處理失效連接,那Jedis客戶端呢?它是否可以主動斷掉連接?我們定位到了Jedis建立連接的函數買粉絲nnect(),如下所示:

由第x行可以看到,Jedis啟用了TCP的keepalive機制,并且沒有設置其他keepalive相關選項。也就是說,Jedis客戶端會采用linux默認的TCP keepalive機制,每隔7200秒去探測連接的情況。這樣,即使與Sentinel的連接出問題,Jedis客戶端也能主動釋放掉,雖然時間有點久。

但是,實際上,如前面所示,Sentinel服務器上有很多失效連接持續保持,為什么會有這種現象?

對于上面的問題,能想到的原因就是,在Jedis去主動釋放掉TCP連接前,該連接被強制斷掉,沒有進行完整的四次揮手的過程。而Sentinel卻因為沒有保活機制,沒有感知到這個動作,導致其一直保持這個連接。

能干掉連接的元兇,馬上想到了防火墻,于是我又詢問了運維,結果,他們告知了我一個噩耗:

目前,生產環境上防火墻的設置是主動斷掉超過10分鐘沒有數據交換的TCP連接。

好吧,繞了一大圈,至此,問題已經很清楚了。

終于,我們得出了結論:

有了前面的分析,其實解決辦法很簡單:

關于“追蹤Redis Sentinel的CPU占有率長期接近100%的問題”到此就結束了,在寫這兩篇博文的時候,我收貨了很多自己沒有掌握的知識和技巧。現在覺得,寫博文真的是一件值得堅持和認真對待的事情,早應該開始。不要問我為什么,當你嘗試之后,也就和我一樣明白了。

這兩篇文章的分析過程肯定有疏漏和不足之處,個人能力有限,希望大家能夠理解,并多多指教,非常感謝!我會繼續進步!

前面提到過,在每個客戶端上,都可以發現5個正常的TCP連接,他們是什么呢?讓我們重新回到Jedis。

在《追蹤Redis Sentinel的CPU占有率長期接近100%的問題 一》中,我們提到Jedis SentinelPool會為每一個Sentinel建立一個MasterListener線程,該線程用來監聽主從切換,保證客戶端的Jedis句柄始終對應在Master上。在這里,即會有5個MasterListener來對應5個Sentinel。

其實,MasterListener的監聽功能根據Redis的pub sub功能實現的。MasterListener線程會去訂閱+switch-master消息,該消息會在master節點地址改變時產生,一旦產生,MasterListener就重新初始化連接池,保證客戶端使用的jedis句柄始終關聯到Master上。

如下所示為MasterListener的線程函數,它會在一個無限循環中不斷的創建Jedis句柄,利用該句柄去訂閱+switch-master消息,只要發生了主從切換,就會觸發onMessage。

如何實現訂閱功能呢,我們需要查看subscribe函數的底層實現,它實際使用client.setTimeoutInfinite()->買粉絲nnect建立了一個TCP連接,然后使用JedisPubSub的proceed方法去訂閱頻道,并且無限循環的讀取訂閱的信息。

在procee的方法中,實際先通過subscribe訂閱頻道,然后調用process方法讀取訂閱信息。

其實,subscribe函數就是簡單的向服務器發送了一個SUBSCRIBE命令。

而process函數,篇幅較長,此處省略,其主要功能就是以無限循環的方式不斷地讀取訂閱信息

綜上,MasterListener線程會向Sentinel創建+switch-master頻道的TCP訂閱連接,并且會do while循環讀取該頻道信息。如果訂閱或讀取過程中出現Tcp連接異常,則釋放Jedis句柄,然后等待5000ms 后重新創建Jedis句柄進行訂閱。當然,這個過程會在一個循環之中。

至此,也就解釋了為何每個業務客戶端服務器和Sentinel服務器上,都有5個長期保持的、狀態正常的TCP連接的原因了。

很赞哦!(32)

Instagram刷粉絲, Ins買粉絲自助下單平台, Ins買贊網站可微信支付寶付款的名片

职业:程序员,设计师

现居:四川内江市中区

工作室:小组

Email:[email protected]