問題介紹
最近查看生產(chǎn)日志讲婚,在日志中發(fā)現(xiàn)一些“Unexpect end of stream”的報錯棚点,這個異常之前也出現(xiàn)過,但之前出現(xiàn)的次數(shù)都比較少杈女,沒太引起注意点待,最近出現(xiàn)的此類錯誤的次數(shù)越來越多搔耕,于是引起了重視矮嫉。
環(huán)境介紹
Jedis的版本是:2.7.3
redis的版本是:2.6.0
jedis的配置如下:
<bean id="jedisConfig" class="org.apache.commons.pool2.impl.GenericObjectPoolConfig">
<property name="maxToal" value="100" />
<property name="maxIdle" value="100" />
<property name="minIdle" value="10" />
<property name="testWhileIdle" value="true" />
<property name="minEvictableIdleTimeMillis" value="160000" />
</bean>
解決過程
- 首先通過異常提示,可以確定的是服務(wù)端主動關(guān)閉了連接蚪拦,導(dǎo)致客戶端拿到一個已經(jīng)被服務(wù)端關(guān)閉的連接在執(zhí)行命令越除,導(dǎo)致拋出異常。
- 在網(wǎng)上查詢資料外盯,看大家是否也遇到過此類問題摘盆,結(jié)合自己的經(jīng)驗來看,沒能很好地解決這個問題饱苟。
- 找DBA拿了一下redis的配置孩擂,發(fā)現(xiàn)redis的超時配置是300s,maxclients采用的是默認(rèn)值10000箱熬,client-output-buffer-limit normal 512M 256 60类垦。
- 猜測狈邑。maxclients的設(shè)置成10000,這個參數(shù)應(yīng)該不會導(dǎo)致redis主動關(guān)閉連接的情況蚤认,client-output-buffer-limit被設(shè)置成一個相對比較大的數(shù)值也不會導(dǎo)致redis主動關(guān)閉連接的情況米苹,于是就把目標(biāo)放在了timeout參數(shù)上。我們知道在配置了timeout參數(shù)的情況下砰琢,redis會每隔一段時間將連接到redis的空閑連接關(guān)閉掉蘸嘶,這個空閑的時間就是通過timeout設(shè)置的。
- 翻閱了JedisPool的源碼陪汽,發(fā)現(xiàn)在配置了timeBetweenEvictionRunsMillis參數(shù)的情況下(該參數(shù)值大于0)训唱,JedisPool會啟動一個Timer定時關(guān)閉空閑連接(連接是否空閑的判斷依據(jù)是連接的空閑時間大于minEvictableIdleTimeMillis則連接空閑),每次關(guān)閉的空閑連接數(shù)是由參數(shù)numTestsPerEvictionRun設(shè)置的挚冤,該參數(shù)的默認(rèn)值是3况增,結(jié)合我們對JedisPool的配置,也就是說JedisPool每160s才關(guān)閉三個空閑連接训挡,而redis的配置的timeout參數(shù)是300s澳骤,所以就有可能導(dǎo)致拿到被關(guān)閉的空閑連接的情況導(dǎo)致該異常。
- 驗證澜薄。重新設(shè)置JedisPool參數(shù)后为肮,并做測試之后未出現(xiàn)此類錯誤。
后記
為啥Jedis在拋出該錯誤的時機(jī)是讀取redis返回結(jié)果的時候拋出異常呢表悬?而不是在寫redis命令到redis的時候報異常呢?具體參考RedisInputStream
類丧靡。一開始我也不解蟆沫,為了重現(xiàn)此類場景我模擬了客戶端與服務(wù)端的通信,并讓服務(wù)端主動關(guān)閉連接温治,結(jié)果與此現(xiàn)象一致饭庞。
原因是tcp的四次揮手機(jī)制,服務(wù)端關(guān)閉連接后熬荆,客戶端會馬上返回一個ack舟山,使服務(wù)端從fin-wait-1 -> fin-wait-2狀態(tài),客戶端處于close-wait狀態(tài)卤恳,我們知道處于close-wait狀態(tài)的一端可以繼續(xù)往另一端發(fā)送數(shù)據(jù)而不能讀取數(shù)據(jù)累盗。