現(xiàn)象
某系統(tǒng)的tomcat薇正,收不到上游系統(tǒng)請(qǐng)求很澄,日志停止不滾動(dòng),進(jìn)程還存活(俗稱的tomcat假死)筹麸。
分析排查
應(yīng)用排查
- 登錄應(yīng)用jstack -l pid > jstack.txt枫夺,查看堆棧信息将宪,發(fā)現(xiàn)大量的連redis集群的BLOCKED信息:
"http-nio-9080-exec-200" #287 daemon prio=5 os_prio=0 tid=0x00002aaad4716800 nid=0x47db waiting for monitor entry [0x000000005287e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at redis.clients.jedis.JedisClusterInfoCache.getSlotPool(JedisClusterInfoCache.java:151)
- waiting to lock <0x0000000648ecf770> (a redis.clients.jedis.JedisClusterInfoCache)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:54)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:47)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:32)
at redis.clients.jedis.JedisCluster.get(JedisCluster.java:97)
- 登錄監(jiān)控平臺(tái),發(fā)現(xiàn)一個(gè)redis節(jié)點(diǎn)已經(jīng)監(jiān)控不到。在應(yīng)用上telnet該節(jié)點(diǎn)较坛,返回超過最大連接數(shù)印蔗。于是基本定位故障:某redis節(jié)點(diǎn)超過最大連接數(shù),導(dǎo)致無法訪問丑勤。
redis監(jiān)控.png
分析問題
應(yīng)用程序
- 應(yīng)用程序在生成流水號(hào)時(shí)华嘹,強(qiáng)依賴redis,沒有做降級(jí)方案法竞。
- 應(yīng)用端jedispool設(shè)置了連接超時(shí)時(shí)間耙厚,但是過大,造成請(qǐng)求阻塞岔霸。
- 此redis集群為公用集群薛躬,至少有60個(gè)子系統(tǒng)在使用,每個(gè)子系統(tǒng)都有設(shè)置了最大連接數(shù)呆细,理論上型宝,是不會(huì)超出10000的。
redis集群排查
- 登錄redis-cli(redis版本3.0.6)絮爷,list連接清單趴酣,發(fā)現(xiàn)了某應(yīng)用集群有大量的連接,并處于establish狀態(tài)坑夯,斷定來源IP為問題應(yīng)用岖寞,并且應(yīng)用使用了redis的發(fā)布訂閱功能:subscribe。
連接信息.png
- 登錄問題應(yīng)用服務(wù)器柜蜈,使用netstat查看未發(fā)現(xiàn)到redis服務(wù)器的連接仗谆。由此定位問題為redis連接未及時(shí)釋放。
長連接問題排查
- 查看網(wǎng)絡(luò)拓?fù)錇閼?yīng)用->防火墻->redis跨释。登錄防火墻胸私,連接數(shù)依然為0-1個(gè)厌处,確認(rèn)問題為redis未及時(shí)回收連接鳖谈。
- 查看操作系統(tǒng)tcp keepalive設(shè)置為7200s(2小時(shí)),未生效阔涉,推測此配置被redis配置覆蓋缆娃。(在測試環(huán)境使用相同配置,進(jìn)行tcpdump抓包測試瑰排,確認(rèn)無誤贯要。)
- 查看redis配置tcp_keepalive為0,代表關(guān)閉tcp連接狀態(tài)檢查椭住,與現(xiàn)象一致崇渗。
- 查看防火墻長連接配置為30分鐘無流量主動(dòng)斷開連接,與現(xiàn)象一致。
結(jié)論
- 應(yīng)用與redis之間有jupiter防火墻宅广,防火墻30分鐘無數(shù)據(jù)通信葫掉,會(huì)拆連接,拆的時(shí)候不會(huì)通知兩端回收連接跟狱。
- redis-server設(shè)置了keepalive=0俭厚,此配置覆蓋了操作系統(tǒng)的keepalive=7200s,導(dǎo)致redis-server不主動(dòng)檢測連接狀態(tài)驶臊,所以不會(huì)主動(dòng)回收連接挪挤。
- 客戶端應(yīng)該是jvm有默認(rèn)設(shè)置,或者走了操作系統(tǒng)的配置(待驗(yàn)證)关翎,所以客戶端機(jī)器上可以正晨该牛回收連接。
解決方案
- redis設(shè)置tcp_keepalive=60s
- 應(yīng)用程序jedispool設(shè)置連接檢測纵寝。
核心技術(shù)原理與知識(shí)點(diǎn)
- TCP連接報(bào)活機(jī)制
ftp://ftp.wayne.edu/ldp/en/TCP-Keepalive-HOWTO/TCP-Keepalive-HOWTO.pdf - jvm thread stack
- redis server配置
- jedispool配置
相關(guān)場景建議
- 所有redis集群對(duì)tcp_keepalive配置進(jìn)行review尖飞,保證連接可主動(dòng)回收
- 所有使用tcp長連接的通信方式(Oracle,Mysql,Redis,MQ等),CS兩端應(yīng)配置合理tcp keepalive時(shí)間店雅,嚴(yán)謹(jǐn)配置為0政基,C端應(yīng)提供重連機(jī)制。
- 發(fā)布訂閱的功能闹啦,建議使用rabbitmq等實(shí)現(xiàn)沮明,redis還是搞緩存吧