欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

redis分布式Jedis類型轉(zhuǎn)換的異常深入研究

 更新時(shí)間:2022年03月24日 16:54:58   作者:乒乓狂魔  
這篇文章主要介紹了redis分布式Jedis類型轉(zhuǎn)換的異常深入研究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1 類型轉(zhuǎn)換異常場(chǎng)景

我們?cè)谑褂肑edis的時(shí)候,經(jīng)常會(huì)出現(xiàn)類型轉(zhuǎn)換異常,有如下情況:

多線程環(huán)境

Jedis是線程不安全的,如果存在多線程使用同一個(gè)Jedis,就會(huì)出現(xiàn)類型轉(zhuǎn)換異常網(wǎng)上也流傳著很多錯(cuò)誤的解釋,下面我們以一個(gè)案例來(lái)復(fù)現(xiàn)下這個(gè)問(wèn)題,這個(gè)很好理解。

單線程環(huán)境

即使在單線程的情況下,也是會(huì)出現(xiàn)類型轉(zhuǎn)換異常的,下面就針對(duì)此做一個(gè)案例分析

2 Jedis類型轉(zhuǎn)換異常案例

2.1 案例介紹

案例是從這里來(lái)的Jedis returnResource使用注意事項(xiàng)

代碼如下:

public static void main(String[] args) throws Exception{
	Jedis jedis = new Jedis("192.168.126.131", 6379);
	System.out.println("get name=" + jedis.get("name"));
	System.out.println("Make SocketTimeoutException");
	System.in.read(); //等待制造SocketTimeoutException
	try {
	    System.out.println(jedis.get("name"));
	} catch (Exception e) {
	    e.printStackTrace();
	}
	System.out.println("Recover from SocketTimeoutException");
	Thread.sleep(50000); // 繼續(xù)休眠一段時(shí)間 等待網(wǎng)絡(luò)完全恢復(fù)
	boolean isMember = jedis.sismember("urls", "baidu");
	System.out.println("isMember " + isMember);
	jedis.close();
}

以及包含2個(gè)阻斷和解除網(wǎng)絡(luò)通信的命令

阻斷網(wǎng)絡(luò)通信

sudo iptables -A INPUT -p tcp --dport 6379 -j DROP

解除網(wǎng)絡(luò)阻塞

sudo iptables -F

案例運(yùn)行過(guò)程描述:

  • 1 創(chuàng)建Jedis,發(fā)送get命令,啟動(dòng)與redis的連接,連接成功后獲取到響應(yīng)數(shù)據(jù)
  • 2 程序阻塞在System.in.read(),等待輸入,此時(shí)我們需要將網(wǎng)絡(luò)連接阻塞,執(zhí)行上述阻斷網(wǎng)絡(luò)命令
  • 3 輸入任意數(shù)據(jù),讓程序不再阻塞,繼續(xù)走下去,執(zhí)行g(shù)et命令,此時(shí)由于網(wǎng)絡(luò)不通,導(dǎo)致出現(xiàn)SocketTimeoutException異常
  • 4 打印出異常,繼續(xù)往下走,sleep 50s,此時(shí)我們需要解除網(wǎng)絡(luò)阻塞,執(zhí)行上述對(duì)應(yīng)命令
  • 5 50s過(guò)完,就會(huì)執(zhí)行jedis的sismember方法,此時(shí)就會(huì)出現(xiàn)類型轉(zhuǎn)換異常

2.2 Jedis原理介紹

Jedis內(nèi)部有一個(gè)Socket與redis服務(wù)器建立連接。在創(chuàng)建Jedis對(duì)象的時(shí)候,并沒(méi)有去建立連接,而是在執(zhí)行命令的時(shí)候才會(huì)先檢查是否已連接,未連接的話,才建立連接。

Socket一旦連接建立,就會(huì)獲取到Socket的OutputStream,并用RedisOutputStream進(jìn)行包裝,獲取到Socket的InputStream,并用RedisInputStream進(jìn)行包裝。RedisOutputStream內(nèi)部含有一個(gè)byte buf[]數(shù)組。

也就是說(shuō)在jedis在向OutputStream寫入命令的時(shí)候,會(huì)先寫入到上述buf數(shù)組中,然后在讀取的時(shí)候,才會(huì)flush上述數(shù)據(jù),將數(shù)據(jù)寫入到Socket的OutputStream中,并調(diào)用flush,以Jedis的get方法為例

public String get(final String key) {
	checkIsInMulti();
	client.sendCommand(Protocol.Command.GET, key);
	return client.getBulkReply();
}

client.sendCommand方法會(huì)將數(shù)據(jù)寫入到RedisOutputStream內(nèi)部的buf中 client.getBulkReply方法會(huì)首先執(zhí)行一次flush,即將buf中數(shù)據(jù)寫入到Socket的OutputStream中,并調(diào)用Socket的OutputStream的flush。

2.3 類型轉(zhuǎn)換異常的原因

網(wǎng)上很多人說(shuō)造成上述場(chǎng)景的類型轉(zhuǎn)換異常是因?yàn)椋?/p>

出現(xiàn)SocketTimeoutException異常后,RedisOutputStream的buf中殘留上次命令,沒(méi)做清理處理,導(dǎo)致再執(zhí)行其他命令時(shí)連同之前的命令一起發(fā)送過(guò)去了。

經(jīng)過(guò)查看RedisOutputStream的源碼,buf中確實(shí)不會(huì)去主動(dòng)清除原有數(shù)據(jù),而是每次都是直接覆蓋,有count指針來(lái)標(biāo)記,但是這也不會(huì)造成上述所說(shuō)的影響,RedisOutputStream是OK的。

首先我們要明白什么是SocketTimeoutException異常: 上述Jedis的Socket在發(fā)送完成數(shù)據(jù)后,就會(huì)去執(zhí)行讀取數(shù)據(jù),即讀取Socket的InputStream中的數(shù)據(jù),并且又一定的阻塞時(shí)間,如果redis服務(wù)器遲遲不返回?cái)?shù)據(jù),一旦超過(guò)SO_TIMEOUT(即Socket的讀取超時(shí)時(shí)間),客戶端就會(huì)拋出一個(gè)SocketTimeoutException異常。

造成這種異常的原因有很多:

  • 網(wǎng)絡(luò)閃斷(會(huì)TCP重傳):上述案例情景就是網(wǎng)絡(luò)斷開,數(shù)據(jù)包發(fā)送失敗,會(huì)TCP重傳
  • 網(wǎng)絡(luò)沒(méi)有斷,但是傳輸比較慢,或者redis服務(wù)器處理很慢

上述原因都會(huì)造成客戶端讀取超時(shí)。一旦超時(shí),我們的Jedis程序拋出異常,繼續(xù)往下走,如果此時(shí)再次執(zhí)行其他命令的話,仍然會(huì)讀取服務(wù)器端響應(yīng),此時(shí)讀到的響應(yīng)就是上次請(qǐng)求的響應(yīng)了,所以會(huì)導(dǎo)致類型轉(zhuǎn)換異常。如果與上次請(qǐng)求的類型一致,那就更可怕了,錯(cuò)誤就會(huì)被深深的掩蓋過(guò)去了。

3 Jedis類型轉(zhuǎn)換異常的解決辦法

上述問(wèn)題就是:我們沒(méi)有正確對(duì)待這個(gè)SocketTimeoutException異常,即一旦出現(xiàn)SocketTimeoutException異常,我們是必須要廢棄掉這個(gè)Jedis的。所以對(duì)于單線程環(huán)境下的Jedis來(lái)說(shuō),一旦出現(xiàn)這種異常,我們需要重新new一個(gè)新的Jedis來(lái)使用。

Jedis在內(nèi)部執(zhí)行出現(xiàn)異常,如SocketTimeoutException異常的時(shí)候,會(huì)標(biāo)記一個(gè)boolean broken=true,即意味著該連接已經(jīng)廢棄了。

重要的大坑在這里,我們通常使用JedisPool來(lái)應(yīng)對(duì)多線程環(huán)境下Jedis的使用,一般使用方式如下:

Jedis jedis = null;//從pool中獲取資源  
try{
	jedis = pool.getResource();
    jedis.set("k1", "v1");  
}catch(Exception e){  
    e.printStackTrace();
}finally{
	if(jedis != null){
		pool.returnResource(jedis);//向連接池“歸還”資源,千萬(wàn)不要忘記。
	}
}

而對(duì)于JedisPool,我們會(huì)使用returnResource方法來(lái)向pool中釋放回Jedis,而這個(gè)returnResource卻忽視了上述boolean broken屬性,直接將一個(gè)標(biāo)記廢棄的連接放回到了pool中,下次別人取的時(shí)候,必然出問(wèn)題。

所以針對(duì)JedisPool這種情況,解決辦法如下:

1 在上述catch中捕獲SocketTimeoutException異常,調(diào)用pool的returnBrokenResource方法來(lái)釋放Jedis(該方法會(huì)將Jedis實(shí)例標(biāo)記為下線,無(wú)法被他人獲取到了),但是不推薦這種,還要考慮其他異常等等

2 另一個(gè)就是直接調(diào)用Jedis的close方法,最新版2.9.0(其他版本沒(méi)驗(yàn)證)中close方法對(duì)上述boolean broken標(biāo)記進(jìn)行了處理,并且將returnResource標(biāo)記成廢棄了,處理如下

public void close() {
	if (dataSource != null) {
	  if (client.isBroken()) {
	    this.dataSource.returnBrokenResource(this);
	  } else {
	    this.dataSource.returnResource(this);
	  }
	} else {
	  client.close();
	}
}

上述this.dataSource可以理解為JedisPool。 即一旦是broken,則調(diào)用pool的returnBrokenResource方法,否則調(diào)用pool的returnResource方法。

所以最終寫法應(yīng)該如下:

Jedis jedis = null;//從pool中獲取資源  
try{
	jedis = pool.getResource();
    jedis.set("k1", "v1");  
}finally{
	if(jedis != null){
		jedis.close();
	}
}

4 問(wèn)題深思

可以想到2方面的問(wèn)題:

問(wèn)題1:jedis為什么要暴漏這么個(gè)危險(xiǎn)的API給用戶使用

即要求用戶自覺(jué)的close,不自覺(jué)后果自負(fù)

如果是我們?cè)陂_發(fā)框架給被人使用,那就要盡量避免這種API的設(shè)計(jì),把close自動(dòng)隱藏在框架內(nèi)部,避免了使用人員的誤使用,同時(shí)減少了代碼的復(fù)雜度,即使是上述最終的寫法也是很丑陋的,要完成一個(gè)set功能,要關(guān)注太多地方了,這部分完全可以框架底層包裝起來(lái),只給用戶一個(gè)set方法即可。

問(wèn)題2:請(qǐng)求和響應(yīng)的不匹配問(wèn)題

這種不匹配的問(wèn)題在同步和異步的時(shí)候分別怎么處理?

同步通信:

在設(shè)計(jì)的時(shí)候,必須發(fā)送一次請(qǐng)求就要讀取一次響應(yīng),通過(guò)這種方式來(lái)匹配。然而在某些情況下,讀取響應(yīng)有一定的超時(shí)時(shí)間,一旦超時(shí),就拋出SocketTimeoutException異常,從而結(jié)束本次讀取,而響應(yīng)可能后來(lái)又到達(dá)了,這種情況就會(huì)造成不匹配的現(xiàn)象。要避免這種情況,就必須要廢棄掉這個(gè)Socket了,所以如果客戶端設(shè)計(jì)成同步通信的時(shí)候,一旦遇到這種異常,則就需要廢棄了,重新建立連接了。

異步通信:

在設(shè)計(jì)的時(shí)候一般會(huì)為每個(gè)請(qǐng)求分配一個(gè)請(qǐng)求id,服務(wù)器端在處理請(qǐng)求后,會(huì)把這個(gè)請(qǐng)求id返回給客戶端,客戶端根據(jù)返回的請(qǐng)求id來(lái)匹配是那一次的請(qǐng)求對(duì)應(yīng)的響應(yīng),就不會(huì)出現(xiàn)上述那種匹配錯(cuò)亂的問(wèn)題。異步通信在讀取數(shù)據(jù)的時(shí)候也通常是有數(shù)據(jù)可讀才會(huì)去執(zhí)行讀操作,可以減少同步通信中因網(wǎng)絡(luò)擁堵或其他原因造成的SocketTimeoutException問(wèn)題。異步通信好處的代價(jià)就是比同步通信復(fù)雜。

所以如果我們?cè)谠O(shè)計(jì)的時(shí)候,就需要去考慮這樣的問(wèn)題,避免造出一個(gè)大坑來(lái)。

以上就是redis分布式Jedis類型轉(zhuǎn)換的異常深入研究的詳細(xì)內(nèi)容,更多關(guān)于redis分布式Jedis類型轉(zhuǎn)換異常的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談redis的maxmemory設(shè)置以及淘汰策略

    淺談redis的maxmemory設(shè)置以及淘汰策略

    下面小編就為大家?guī)?lái)一篇淺談redis的maxmemory設(shè)置以及淘汰策略。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • Redis?Brpop?命令作用詳解

    Redis?Brpop?命令作用詳解

    BRPOP?是一個(gè)阻塞的列表彈出原語(yǔ),該命令會(huì)按照給出的?key?順序查看?list,并在找到的第一個(gè)非空?list?的尾部彈出一個(gè)元素,今天通過(guò)本文給大家介紹Redis?Brpop?命令相關(guān)知識(shí),感興趣的朋友一起看看吧
    2023-07-07
  • Redis字符串原理的深入理解

    Redis字符串原理的深入理解

    這篇文章主要給大家介紹了關(guān)于Redis字符串原理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Redis哨兵監(jiān)控的使用

    Redis哨兵監(jiān)控的使用

    在Redis集群模式中,哨兵模式是一種常用的方案,本文主要介紹了Redis哨兵監(jiān)控的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • 利用Redis的有序集合實(shí)現(xiàn)排行榜功能實(shí)例代碼

    利用Redis的有序集合實(shí)現(xiàn)排行榜功能實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于如何利用Redis的有序集合實(shí)現(xiàn)排行榜功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Spring?Boot實(shí)戰(zhàn)解決高并發(fā)數(shù)據(jù)入庫(kù)之?Redis?緩存+MySQL?批量入庫(kù)問(wèn)題

    Spring?Boot實(shí)戰(zhàn)解決高并發(fā)數(shù)據(jù)入庫(kù)之?Redis?緩存+MySQL?批量入庫(kù)問(wèn)題

    這篇文章主要介紹了Spring?Boot實(shí)戰(zhàn)解決高并發(fā)數(shù)據(jù)入庫(kù)之?Redis?緩存+MySQL?批量入庫(kù)問(wèn)題,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • spring?boot整合redis中間件與熱部署實(shí)現(xiàn)代碼

    spring?boot整合redis中間件與熱部署實(shí)現(xiàn)代碼

    spring?boot整合redis最常用的有三個(gè)工具庫(kù)Jedis,Redisson,Lettuce,本文重點(diǎn)介紹spring?boot整合redis中間件與熱部署實(shí)現(xiàn),需要的朋友可以參考下
    2023-01-01
  • CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實(shí)例(詳解)

    CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實(shí)例(詳解)

    這篇文章主要介紹了CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實(shí)例,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Redis連接超時(shí)異常的處理方法

    Redis連接超時(shí)異常的處理方法

    這篇文章主要給大家介紹了關(guān)于Redis連接超時(shí)異常的處理方法,文中通過(guò)示例代碼以及圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Redis中鍵值過(guò)期操作示例詳解

    Redis中鍵值過(guò)期操作示例詳解

    這篇文章主要給大家介紹了關(guān)于Redis中鍵值過(guò)期操作的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論