JedisConnectionException: java.net.SocketException: Broken pipe (Write failed) 問題排查
- 2022 年 8 月 19 日
- 筆記
- broken pipe, Redis
問題描述
筆者有2個應用會不定時請求redis,其中一個應用大約每分鐘請求一次,可以正常請求,但是另一個大約每小時請求一次的應用,經常出現Broken pipe (Write failed)
報錯,具體報錯資訊如下:
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe (Write failed)
at redis.clients.jedis.Connection.flush(Connection.java:282)
at redis.clients.jedis.Connection.getBinaryMultiBulkReply(Connection.java:222)
at redis.clients.jedis.Jedis.hgetAll(Jedis.java:780)
...
Caused by: java.net.SocketException: Broken pipe (Write failed)
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
at redis.clients.util.RedisOutputStream.flushBuffer(RedisOutputStream.java:31)
at redis.clients.util.RedisOutputStream.flush(RedisOutputStream.java:213)
at redis.clients.jedis.Connection.flush(Connection.java:279)
... 26 more
問題定位
定位到redis服務端配置了3600秒的超時時間,即如果客戶端連接超過3600秒空閑,那麼就會被redis服務端主動清理掉。筆者大約每小時請求一次redis的應用,若請求時間間隔超過1小時,會被redis服務端認為連接是空閑的,然後把連接清理掉,此時客戶端在去請求redis,就會出現報錯: Broken pipe (Write failed)
。
解決方案
筆者首先嘗試調大客戶端socketTimout的超時時間,讓客戶端socketTimout的時間超過3600秒,如:7200秒。調整後發現,大約每小時請求一次redis的應用,仍然會出現 Broken pipe (Write failed)
報錯。由此可見,當客戶端和服務端都包含超時的配置時,redis會以服務端為準。
既然調整客戶端的超時配置沒有效果,服務端仍然會清理空閑的連接。那麼客戶端是否可以把服務端清理的連接,主動關閉掉呢?然後在需要時在重建連接。按照這個思路,筆者在每次請求redis前,會先執行ping操作。若ping成功了,說明socket連接還沒被清理,可以直接請求redis;若ping 不成功,那麼會拋出異常,捕獲異常後在關閉客戶端連接,後面在執行請求redis的命令時,會重新建立socket連接。以下是捕獲異常後關閉連接的相關程式碼。
try {
String result = jedis.ping();
LOGGER.debug("ping redis: {}", result);
} catch (Exception e){
LOGGER.warn("ping failed, close jedis and reconnect, {}", e.getMessage());
try {
jedis.close();
LOGGER.info("jedis closed");
} catch (Exception e1){
LOGGER.warn("close jedis failed, {}", e1.getMessage());
}
}
通過上述方案,客戶端空閑超過1小時後,向redis請求會拋出異常 Broken pipe (Write failed)
,程式捕獲異常後關閉客戶端連接,然後客戶端重建連接,在執行請求就恢復正常了。