記錄一次并發(fā)情況下的redis導(dǎo)致服務(wù)假死的問(wèn)題解決
問(wèn)題描述
最近項(xiàng)目在做性能壓測(cè),框架使用的是 spring boot 2.1.2 + jedis 2.9.1,80個(gè)并發(fā)持續(xù)壓測(cè)4-5分鐘服務(wù)就假死,所有的請(qǐng)求就pending,查看服務(wù)日志沒有任何異常,查看其它沒有使用redis的接口都能正常請(qǐng)求。
查找問(wèn)題思路
查看了一下redis的連接配置,都是正常夠用的
再使用jstack看一下堆棧信息
發(fā)現(xiàn)很多WAITING的線程,再往下看都是redis的getResource方法導(dǎo)致的等待。
查看redis的源碼
Jedis.java
@Override public void close() { if (dataSource != null) {//1 if (client.isBroken()) { this.dataSource.returnBrokenResource(this); } else { this.dataSource. returnResource(this); // 2 } this.dataSource = null; // 3 } else { super.close(); } }
分析一下這個(gè)代碼,
client.isBroken()
這里默認(rèn)值是false,連接釋放的時(shí)候先釋放resource 然后再將dataSource置為null,那如果是并發(fā)的情況下話,那有可能再下一個(gè)線程進(jìn)來(lái)的時(shí)候dataSource已經(jīng)就是null了,在執(zhí)行第2步的時(shí)候dataSource剛好被置為null,那這個(gè)時(shí)候就無(wú)法釋放連接了。這個(gè)時(shí)候我們?cè)倏聪芦@取連接的方法。JedisSentinelPool.java
@Override public Jedis getResource() { while (true) { Jedis jedis = super.getResource(); jedis.setDataSource(this); // <-- This line // get a reference because it can change concurrently final HostAndPort master = currentHostMaster; final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient() .getPort()); if (master.equals(connection)) { // connected to the correct master return jedis; } else { returnBrokenResource(jedis); } } }
這里
jedis.setDataSource(this);
設(shè)置的則為null。
問(wèn)題解決方案
我查看了一下jedis的github,在issue上有找到有人曾經(jīng)提出過(guò)這樣的問(wèn)題,https://github.com/redis/jedis/issues/1920
,給出的解決方案是升級(jí)jedis的jar包到2.10.2版本以上,換成高版本的以后問(wèn)題果然就解決了。
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.10.2</version> </dependency>
這里要注意一下的是jedis的版本跟spring-data-redis的版本是有一個(gè)對(duì)應(yīng)關(guān)系的。
spring-data-redis 2.1.x 對(duì)應(yīng)的jedis版本是2.x.x 版本
spring-data-redis 2.2.x 對(duì)應(yīng)的jedis 版本就是3.x版本了
分析升級(jí)版本以后的改動(dòng)
還是看那兩個(gè)類,看看新版本做了什么改動(dòng)
public void close() { if (this.dataSource != null) { Pool<Jedis> pool = this.dataSource; this.dataSource = null; if (this.client.isBroken()) { pool.returnBrokenResource(this); } else { pool.returnResource(this); } } else { super.close(); } }
這里很明顯的處理方式就是講dataSource復(fù)制給pool,然后用pool去釋放資源,這個(gè)時(shí)候設(shè)置dataSource與這個(gè)就沒有關(guān)系了,就不存在釋放資源釋放不了的情況了。
到此這篇關(guān)于記錄一次并發(fā)情況下的redis導(dǎo)致服務(wù)假死的問(wèn)題解決的文章就介紹到這了,更多相關(guān)redis導(dǎo)致服務(wù)假死內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis 哨兵機(jī)制及配置實(shí)現(xiàn)
本文主要介紹了Redis 哨兵機(jī)制及配置實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03基于Redis的List實(shí)現(xiàn)特價(jià)商品列表功能
本文通過(guò)場(chǎng)景分析給大家介紹了基于Redis的List實(shí)現(xiàn)特價(jià)商品列表,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08基于Redis有序集合實(shí)現(xiàn)滑動(dòng)窗口限流的步驟
滑動(dòng)窗口算法是一種基于時(shí)間窗口的限流算法,通過(guò)動(dòng)態(tài)地滑動(dòng)窗口,可以動(dòng)態(tài)調(diào)整限流的速率,Redis有序集合可以用來(lái)實(shí)現(xiàn)滑動(dòng)窗口限流,本文介紹基于Redis有序集合實(shí)現(xiàn)滑動(dòng)窗口限流,感興趣的朋友一起看看吧2024-12-12Redis 多規(guī)則限流和防重復(fù)提交方案實(shí)現(xiàn)小結(jié)
本文主要介紹了Redis 多規(guī)則限流和防重復(fù)提交方案實(shí)現(xiàn)小結(jié),包括使用String結(jié)構(gòu)和Zset結(jié)構(gòu)來(lái)記錄用戶IP的訪問(wèn)次數(shù),具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02Redis數(shù)據(jù)結(jié)構(gòu)之listpack和quicklist使用學(xué)習(xí)
這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)之listpack和quicklist的使用學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Redis連接池監(jiān)控(連接池是否已滿)與優(yōu)化方法
本文詳細(xì)講解了如何在Linux系統(tǒng)中監(jiān)控Redis連接池的使用情況,以及如何通過(guò)連接池參數(shù)配置、系統(tǒng)資源使用情況、Redis命令監(jiān)控、外部監(jiān)控工具等多種方法進(jìn)行檢測(cè)和優(yōu)化,以確保系統(tǒng)在高并發(fā)場(chǎng)景下的性能和穩(wěn)定性,討論了連接池的概念、工作原理、參數(shù)配置,以及優(yōu)化策略等內(nèi)容2024-09-09