JedisConnectionException: java.net.SocketException: Broken pipe (Write failed) 問題排查

問題描述

筆者有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),程序捕獲異常後關閉客戶端連接,然後客戶端重建連接,在執行請求就恢復正常了。

參考資料

  1. Redis客戶端連接的空閑連接超時時間(timeout)的設置