1. 程式人生 > >edis.clients.jedis.exceptions.JedisConnectionException: java.Net.SocketTimeoutException: Read timed

edis.clients.jedis.exceptions.JedisConnectionException: java.Net.SocketTimeoutException: Read timed

當我們獲取連線後對redis進行操作時,丟擲redis.clients.jedis.exceptions.JedisConnectionException: java.NET.SocketTimeoutException: Read timed out異常。

異常程式碼如下:

redis.clients.jedis.exceptions.JedisConnectionException: java.Net.SocketTimeoutException: Read timed out

at redis.clients.jedis.Protocol.process(Protocol.java:79)
at redis.clients.jedis.Protocol.read(Protocol.java:131)
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:188)
at redis.clients.jedis.Jedis.sismember(Jedis.java:1266)


這是一個比較麻煩的異常,困擾了我一天的時間。我們都知道Redis是對記憶體進行操作,速度應該都在毫秒級,這是我們通常的認識,所以當對Redis操作出現幾秒的超時時間,你能想象嗎?
我們還是先分析一下Jedis的原始碼吧,以sadd操作為例:
  1. public Long sadd(final String key, final String... members) {
  2. checkIsInMulti();
  3. client.sadd(key, members);
  4. return client.getIntegerReply();
  5. }
client是redis.clients.jedis.Client.java的例項,繼承關係如下:

public class Client extends BinaryClient implements Commands;

public class BinaryClient extends Connection;

Connection包裝了對Redis server的socket操作,命令寫操作通過socket.getOutputStream()輸出流將命令資訊傳送到redis server,當寫完命令後要通過socket.getInputStream()的到的輸入流將
命令執行結果返回,這中間必然會有一個命令執行到結果返回的延時時間,這就是一個Jedis呼叫redis命令操作所用的時間。

需要說明的是,Redis server是單執行緒執行所有連線傳送過來的命令的,也就是說不管併發中有多少個client在傳送命令,redis-server端是單執行緒處理的,並按照預設的FIFO方式處理請求,

這個可在redis.conf配置檔案中配置。關於redis server的詳細執行機制參見:http://redis.io/documentation

所以client.sadd(key, members);呼叫完後只是將命令資訊傳送到了redis server端,具體有沒有執行要看redis server的負載情況。然後,通過client.getIntegerReply();等待(time out)返回結果。
Connection初始化socket時有多種選擇,其中設定socket time out 的方法如下:
  1. public void rollbackTimeout() {
  2.           try {
  3.             socket.setSoTimeout(timeout);
  4.             socket.setKeepAlive(false);
  5.           } catch (SocketException ex) {
  6.             throw new JedisException(ex);
  7.           }
  8.       }
由redis.clients.jedis.Protocol.DEFAULT_TIMEOUT = 2000 我們知道預設的超時時間是2秒,這個時間相對於redis操作記憶體毫秒級的速度來說已經很長,那我們為什麼還會遇到
ava.net.SocketTimeoutException: Read timed out異常呢?redis操作記憶體雖然平均毫秒級的,但當資料量很大時未必都如此快速。在我的開發過程中就遇到過一個集合到了

千萬級資料量,一次操作超時時間在秒級是很正常的,而且機器效能很好的情況下已經如此,更何況我們本機開發的機器相對於生產伺服器來說速度會更慢了。所以在初始化JedisPool時應該根據實際

情況通過redis.clients.jedis.JedisPoolConfig合理設定連線池引數,通過edisPool構造方法,合理設定socket讀取輸入InputStream的超時時間。

  1. pool = new JedisPool(config, host, port, 100000);

注意第四個引數time out,設定成我們能容忍的超時時間,單位是毫秒。但不知道為什麼既然單位是毫秒,為什麼引數型別是int而不是long。

設定第四個引數後,我在四千萬資料量集合上操作最多一次大概超時5秒,問題基本解決。