偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

記一次 Redis 連接問題排查

數(shù)據(jù)庫
Redis 和業(yè)務(wù)應(yīng)用部署在同一個(gè) K8s 集群中,Redis Server 暴露了一個(gè) redis-service,指向到 master 節(jié)點(diǎn),業(yè)務(wù)應(yīng)用通過 redis-service 連接 Redis。

問題發(fā)現(xiàn)

客戶端:業(yè)務(wù)應(yīng)用使用 lettuce 客戶端

服務(wù)端:Redis server 部署架構(gòu)采用 1 主 + 1 從 + 3 哨兵

Redis 和業(yè)務(wù)應(yīng)用部署在同一個(gè) K8s 集群中,Redis Server 暴露了一個(gè) redis-service,指向到 master 節(jié)點(diǎn),業(yè)務(wù)應(yīng)用通過 redis-service 連接 Redis。

某個(gè)時(shí)刻起,開始發(fā)現(xiàn)業(yè)務(wù)報(bào)錯(cuò),稍加定位,發(fā)現(xiàn)是 Redis 訪問出了問題,搜索業(yè)務(wù)應(yīng)用日志,發(fā)現(xiàn)關(guān)鍵信息:

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: READONLY You can't write against a read only replica.

這是一個(gè) Redis 訪問的報(bào)錯(cuò),看起來跟 Redis 的讀寫配置有關(guān)。

問題定位

首先排查下業(yè)務(wù)應(yīng)用和 Redis 的連接情況

# netstat -ano | grep 6379
tcp 0 0 172.24.7.34:44602 10.96.113.219:6379 ESTABLISHED off (0.00/0/0)

其中 172.24.7.34 是業(yè)務(wù) pod 的 ip,10.96.113.219 是 redis 的 K8s service ip,連接是 ESTABLISHED 狀態(tài),說明連接沒有斷。

繼續(xù)排查 Redis 的 pod 是否正常:

redis-shareredis-0                           2/2     Running   0
redis-shareredis-1 2/2 Running 0
redis-shareredis-sentinel-5f7458cd89-7dwpz 2/2 Running 0
redis-shareredis-sentinel-5f7458cd89-rrfz7 2/2 Running 0
redis-shareredis-sentinel-5f7458cd89-xzpmb 2/2 Running 0

無論是讀寫節(jié)點(diǎn)還是哨兵節(jié)點(diǎn),都沒有重啟過。

既然報(bào)了只讀節(jié)點(diǎn)的異常,索性看下 redis 節(jié)點(diǎn)的讀寫角色情況。

root@redis-shareredis-0:/data# redis-cli -h 172.24.1.95 -a xxxx role
1) "slave"
2) "172.24.1.96"
3) (integer) 6379
4) "connected"
5) (integer) 6942040980
root@redis-shareredis-0:/data# redis-cli -h 172.24.1.96 -a xxxx role
1) "master"
2) (integer) 6942173072
3) 1) 1) "172.24.1.95"
2) "6379"
3) "6942173072"

可以看到此時(shí) redis-shareredis-0(172.24.1.95)是 slave 節(jié)點(diǎn),redis-shareredis-1(172.24.1.96)是 master 節(jié)點(diǎn)。

排查到這里,猜測是業(yè)務(wù) pod 實(shí)際通過 K8s service 連到了 slave 節(jié)點(diǎn)。進(jìn)入 slave 確認(rèn)這一信息,發(fā)現(xiàn)果然如此,并且 master 節(jié)點(diǎn)并沒有檢查到有該業(yè)務(wù) pod 的連接

root@redis-shareredis-0:/data# netstat -ano | grep 172.24.7.34:44602
tcp 0 0 172.24.1.95:6379 172.24.7.34:44602 ESTABLISHED keepalive (24.09/0/0)

懷疑是某個(gè)時(shí)刻開始,master 和 slave 角色發(fā)生了互換,而主從切換過程中由于 pod 沒有重啟,長連接會(huì)一直保留著,此時(shí)即使 Redis service 的 endpoint 被修正,也不會(huì)影響到已有的連接。

圖片

為了驗(yàn)證上述猜想,著手排查 Redis server 節(jié)點(diǎn)和 sentinel 節(jié)點(diǎn)。

查看 Redis 哨兵日志:

1:X 03 Feb 2023 06:21:41.357 * +slave slave 172.24.1.96:6379 172.24.1.96 6379 @ mymaster 172.24.1.95 6379
1:X 14 Feb 2023 06:53:27.683 # +reset-master master mymaster 172.24.1.96 6379
1:X 14 Feb 2023 06:53:28.692 * +slave slave 172.24.1.95:6379 172.24.1.95 6379 @ mymaster 172.24.1.96 6379
1:X 14 Feb 2023 06:53:33.271 # +reset-master master mymaster 172.24.1.96 6379

可以看到在 2023/2/14 14:53 (時(shí)區(qū)+8)時(shí)發(fā)生了主從切換。

嘗試排查主從切換的原因,進(jìn)到 redis-0 查看日志:

1:M 14 Feb 2023 14:53:27.343 # Connection with replica 172.24.1.96:6379 lost.
1:S 14 Feb 2023 14:53:27.616 * Before turning into a replica, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
1:S 14 Feb 2023 14:53:27.616 * REPLICAOF 172.24.1.96:6379 enabled (user request from 'id=1238496 addr=172.24.1.91:49388 fd=7 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=45 qbuf-free=32723 obl=0 oll=0 omem=0 events=r cmd=slaveof')
1:S 14 Feb 2023 14:53:27.646 * REPLICAOF would result into synchronization with the master we are already connected with. No operation performed.
1:S 14 Feb 2023 14:53:27.670 * REPLICAOF would result into synchronization with the master we are already connected with. No operation performed.
1:S 14 Feb 2023 14:53:28.076 * Connecting to MASTER 172.24.1.96:6379
1:S 14 Feb 2023 14:53:28.076 * MASTER <-> REPLICA sync started
1:S 14 Feb 2023 14:53:28.076 * Non blocking connect for SYNC fired the event.
1:S 14 Feb 2023 14:53:28.076 * Master replied to PING, replication can continue...
1:S 14 Feb 2023 14:53:28.077 * Trying a partial resynchronization (request 816c44412b9008e6969b2fef6401a6cef85fff87:6901666283).
1:S 14 Feb 2023 14:53:28.081 * Full resync from master: 86aa2f4759f73114594586e2e7d2cfbdd1ed2b69:6901664978
1:S 14 Feb 2023 14:53:28.081 * Discarding previously cached master state.
1:S 14 Feb 2023 14:53:28.140 * MASTER <-> REPLICA sync: receiving 1117094 bytes from master
1:S 14 Feb 2023 14:53:28.144 * MASTER <-> REPLICA sync: Flushing old data
1:S 14 Feb 2023 14:53:28.157 * MASTER <-> REPLICA sync: Loading DB in memory
1:S 14 Feb 2023 14:53:28.234 * MASTER <-> REPLICA sync: Finished with success

從日志分析是主從同步時(shí)出現(xiàn)了網(wǎng)絡(luò)分區(qū),導(dǎo)致哨兵進(jìn)行重新選主,但為什么出現(xiàn)網(wǎng)絡(luò)分區(qū),就無從得知了,K8s 中兩個(gè) pod 之間的通信都能出現(xiàn) Connection lost 的確挺詭異的。

到這里,問題的根源基本定位清楚了。

問題復(fù)盤

無論 Redis 的主從切換是故意的還是不小心,都應(yīng)當(dāng)被當(dāng)做是一個(gè)常態(tài),程序需要兼容這類場景。反映出兩個(gè)問題:

  • 問題一,Redis 使用了哨兵機(jī)制,程序應(yīng)當(dāng)首選通過哨兵連接 Redis
  • 問題二,Lettuce 客戶端沒有自動(dòng)斷開錯(cuò)誤的連接

那么改進(jìn)思路自然是有兩種,一是改用哨兵連接 Redis,二是替換掉 Lettuce。對于本文遇到的問題,方案一可能可以,但不能確保沒有其他極端情況導(dǎo)致其他連接問題,所以我實(shí)際采用的是方案二,使用 Jedis 替換掉 Lettuce。

項(xiàng)目一開始采用 Lettuce,主要是因?yàn)?spring-boot-data-redis 默認(rèn)采用了 Lettuce 的實(shí)現(xiàn),盡管我一開始已經(jīng)留意到搜索引擎中諸多關(guān)于 Lettuce 的問題,但實(shí)際測試發(fā)現(xiàn),高版本 Lettuce 基本均已修復(fù)了這些問題,忽略了特殊場景下其可能存在的風(fēng)險(xiǎn)。簡單對比下 Jedis 和 Lettuce:

  • Lettuce:
  • Lettuce 客戶端沒有連接?;钐綔y,錯(cuò)誤連接存在連接池中會(huì)造成請求超時(shí)報(bào)錯(cuò)。
  • Lettuce 客戶端未實(shí)現(xiàn) testOnBorrow 等連接池檢測方法,無法在使用連接之前進(jìn)行連接校驗(yàn)。
  • Jedis:

  • Jedis 客戶端實(shí)現(xiàn)了 testOnBorrow、testWhileIdle、testOnReturn 等連接池校驗(yàn)配置。

    開啟 testOnBorrow 在每次借用連接前都會(huì)進(jìn)行連接校驗(yàn),可靠性最高,但是會(huì)影響性能(每次 Redis 請求前會(huì)進(jìn)行探測)。



  • testWhileIdle 可以在連接空閑時(shí)進(jìn)行連接檢測,合理配置閾值可以及時(shí)剔除連接池中的異常連接,防止使用異常連接造成業(yè)務(wù)報(bào)錯(cuò)。



  • 在空閑連接檢測之前,連接出現(xiàn)問題,可能會(huì)造成使用該連接的業(yè)務(wù)報(bào)錯(cuò),此處可以通過參數(shù)控制檢測間隔(timeBetweenEvictionRunsMillis)。


因此,Jedis 客戶端在面對連接異常,網(wǎng)絡(luò)抖動(dòng)等場景下的異常處理和檢測能力明顯強(qiáng)于 Lettuce,可靠性更強(qiáng)。

參數(shù)

配置介紹

配置建議

maxTotal

最大連接,單位:個(gè)

根據(jù)Web容器的Http線程數(shù)來進(jìn)行配置,估算單個(gè)Http請求中可能會(huì)并行進(jìn)行的Redis調(diào)用次數(shù),例如:Tomcat中的Connector內(nèi)的maxConnections配置為150,每個(gè)Http請求可能會(huì)并行執(zhí)行2個(gè)Redis請求,在此之上進(jìn)行部分預(yù)留,則建議配置至少為:150 x 2 + 100= 400限制條件:單個(gè)Redis實(shí)例的最大連接數(shù)。maxTotal和客戶端節(jié)點(diǎn)數(shù)(CCE容器或業(yè)務(wù)VM數(shù)量)數(shù)值的乘積要小于單個(gè)Redis實(shí)例的最大連接數(shù)。例如:Redis主備實(shí)例配置maxClients為10000,單個(gè)客戶端maxTotal配置為500,則最大客戶端節(jié)點(diǎn)數(shù)量為20個(gè)。

maxIdle

最大空閑連接,單位:個(gè)

建議配置為maxTotal一致。

minIdle

最小空閑連接,單位:個(gè)

一般來說建議配置為maxTotal的X分之一,例如此處常規(guī)配置建議為:100。對于性能敏感的場景,防止經(jīng)常連接數(shù)量抖動(dòng)造成影響,也可以配置為與maxIdle一致,例如:400。

maxWaitMillis

最大獲取連接等待時(shí)間,單位:毫秒

獲取連接時(shí)最大的連接池等待時(shí)間,根據(jù)單次業(yè)務(wù)最長容忍的失敗時(shí)間減去執(zhí)行命令的超時(shí)時(shí)間得到建議值。例如:Http最大容忍超時(shí)時(shí)間為15s,Redis請求的timeout設(shè)置為10s,則此處可以配置為5s。

timeout

命令執(zhí)行超時(shí)時(shí)間,單位:毫秒

單次執(zhí)行Redis命令最大可容忍的超時(shí)時(shí)間,根據(jù)業(yè)務(wù)程序的邏輯進(jìn)行選擇,一般來說處于對網(wǎng)絡(luò)容錯(cuò)等考慮至少建議配置為210ms以上。特殊的探測邏輯或者環(huán)境異常檢測等,可以適當(dāng)調(diào)整達(dá)到秒級。

minEvictableIdleTimeMillis

空閑連接逐出時(shí)間,大于該值的空閑連接一直未被使用則會(huì)被釋放,單位:毫秒

如果希望系統(tǒng)不會(huì)經(jīng)常對連接進(jìn)行斷鏈重建,此處可以配置一個(gè)較大值(xx分鐘),或者此處配置為-1并且搭配空閑連接檢測進(jìn)行定期檢測。

timeBetweenEvictionRunsMillis

空閑連接探測時(shí)間間隔,單位:毫秒

根據(jù)系統(tǒng)的空閑連接數(shù)量進(jìn)行估算,例如系統(tǒng)的空閑連接探測時(shí)間配置為30s,則代表每隔30s會(huì)對連接進(jìn)行探測,如果30s內(nèi)發(fā)生異常的連接,經(jīng)過探測后會(huì)進(jìn)行連接排除。根據(jù)連接數(shù)的多少進(jìn)行配置,如果連接數(shù)太大,配置時(shí)間太短,會(huì)造成請求資源浪費(fèi)。對于幾百級別的連接,常規(guī)來說建議配置為30s,可以根據(jù)系統(tǒng)需要進(jìn)行動(dòng)態(tài)調(diào)整。

testOnBorrow

向資源池借用連接時(shí)是否做連接有效性檢測(ping),檢測到的無效連接將會(huì)被移除。

對于業(yè)務(wù)連接極端敏感的,并且性能可以接受的情況下,可以配置為True,一般來說建議配置為False,啟用連接空閑檢測。

testWhileIdle

是否在空閑資源監(jiān)測時(shí)通過ping命令監(jiān)測連接有效性,無效連接將被銷毀。

True

testOnReturn

向資源池歸還連接時(shí)是否做連接有效性檢測(ping),檢測到無效連接將會(huì)被移除。

False

maxAttempts

在JedisCluster模式下,您可以配置maxAttempts參數(shù)來定義失敗時(shí)的重試次數(shù)。

建議配置3-5之間,默認(rèn)配置為5。根據(jù)業(yè)務(wù)接口最大超時(shí)時(shí)間和單次請求的timeout綜合配置,最大配置不建議超過10,否則會(huì)造成單次請求處理時(shí)間過長,接口請求阻塞。

再次回到本次案例,如果使用了 Jedis,并且配置了合理的連接池策略,可能仍然會(huì)存在問題,因?yàn)?Jedis 底層檢測連接是否可用,使用的是 ping 命令,當(dāng)連接到只讀節(jié)點(diǎn),ping 命令仍然可以工作,所以實(shí)際上連接檢查機(jī)制并不能解決本案例的問題。

但 Jedis 提供了一個(gè) minEvictableIdleTimeMillis 參數(shù),該參數(shù)表示一個(gè)連接至少停留在 idle 狀態(tài)的最短時(shí)間,然后才能被 idle object evitor 掃描并驅(qū)逐,該參數(shù)會(huì)受到 minIdle 的影響,驅(qū)逐到 minIdle 的數(shù)量。也就意味著:默認(rèn)配置 minEvictableIdleTimeMillis=60s,minIdle=0 下,連接在空閑時(shí)間達(dá)到 60s 時(shí),將會(huì)被釋放。由于實(shí)際的業(yè)務(wù)場景 Redis 讀寫空閑達(dá)到 60s 的場景是很常見的,所以該方案勉強(qiáng)可以達(dá)到在主從切換之后,在較短時(shí)間內(nèi)恢復(fù)。但如果 minIdle > 0,這些連接依舊會(huì)有問題。而 Lettuce 默認(rèn)配置下,連接會(huì)一直存在。

出于一些不可描述的原因,我無法將應(yīng)用連接 Redis 的模式切換成哨兵模式,所以最終采取了切換到 Jedis 客戶端,并且配置 minIdle=0、minEvictableIdleTimeMillis=60s 的方案。

問題總結(jié)

當(dāng)使用域名/K8s Service 連接 Redis 集群時(shí),需要考慮主從切換時(shí)可能存在的問題。Redis 通常使用長連接通信,主從切換時(shí)如果連接不斷開,會(huì)導(dǎo)致無法進(jìn)行寫入操作??梢栽诳蛻舳?、服務(wù)端兩個(gè)層面規(guī)避這一問題,以下是一些行之有效的方案:

  • 客戶端連接哨兵集群,哨兵會(huì)感知到主從切換,并推送給客戶端這一變化
  • 客戶端配置 minIdle=0,及時(shí)斷開空閑的連接,可以一定程度規(guī)避連接已經(jīng)不可用但健康檢測又檢查不出來的場景。(即本文的場景)
  • 服務(wù)端主從切換時(shí)斷開所有已有的連接,依靠客戶端的健康檢測以及重連等機(jī)制,確保連接到正確的節(jié)點(diǎn)。

Redis 客戶端推薦使用 Jedis 客戶端,其在面對連接異常,網(wǎng)絡(luò)抖動(dòng)等場景下的異常處理和檢測能力明顯強(qiáng)于 Lettuce。

責(zé)任編輯:武曉燕 來源: Kirito的技術(shù)分享
相關(guān)推薦

2021-05-13 08:51:20

GC問題排查

2021-03-29 12:35:04

Kubernetes環(huán)境TCP

2021-11-23 21:21:07

線上排查服務(wù)

2022-02-08 17:17:27

內(nèi)存泄漏排查

2017-12-19 14:00:16

數(shù)據(jù)庫MySQL死鎖排查

2019-03-15 16:20:45

MySQL死鎖排查命令

2023-01-04 18:32:31

線上服務(wù)代碼

2023-10-11 22:24:00

DubboRedis服務(wù)器

2024-04-10 08:48:31

MySQLSQL語句

2021-04-13 08:54:28

dubbo線程池事故排查

2020-08-24 07:34:39

網(wǎng)絡(luò)超時(shí)請求

2023-01-05 11:44:43

性能HTTPS

2022-11-03 16:10:29

groovyfullGC

2022-11-16 08:00:00

雪花算法原理

2021-11-11 16:14:04

Kubernetes

2018-07-20 08:44:21

Redis內(nèi)存排查

2018-01-19 11:12:11

HTTP問題排查

2021-05-31 10:08:44

工具腳本主機(jī)

2019-09-10 10:31:10

JVM排查解決

2011-08-12 09:30:02

MongoDB
點(diǎn)贊
收藏

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