jedis不是線程安全的:
public class RedisLockTest { private Integer inventory = 1000; private int num = 1000; private int corePoolsize = 100; private int maximumPoolSize = 1000; private long keepAliveTime = 10000; private LinkedBlockingDeque linkedBlockingDeque = new LinkedBlockingDeque(); /** * jedis--redis客戶端,在多線程操作時會報異常 * @throws InterruptedException */ @Test public void redismethodError() throws InterruptedException { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolsize,maximumPoolSize,keepAliveTime, java.util.concurrent.TimeUnit.SECONDS,linkedBlockingDeque); num =20; CountDownLatch countDownLatch = new CountDownLatch(num); Jedis jedis = new Jedis("localhost",6379); jedis.auth("root123456"); for (int i = 0; i < num; i++) { threadPoolExecutor.execute(new Runnable() { @Override public void run() { try{ jedis.set("aaa","value"); }catch (Exception e){ System.out.println(e); }finally { countDownLatch.countDown(); } } }); } countDownLatch.await(); threadPoolExecutor.shutdown(); } }
該程序可能會報異常:
1、redis.clients.jedis.exceptions.JedisDataException: ERR Protocol error: expected '$', got ' '
2、redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset
3、redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Connection reset by peer: socket write error
4、 redis.clients.jedis.exceptions.JedisDataException: RR Protocol error: invalid bulk length
打印出錯誤棧:
Exception in thread "pool-1-thread-7" Exception in thread "pool-1-thread-10" redis.clients.jedis.exceptions.JedisDataException: RR Protocol error: invalid bulk length at redis.clients.jedis.Protocol.processError(Protocol.java:127) at redis.clients.jedis.Protocol.process(Protocol.java:161) at redis.clients.jedis.Protocol.read(Protocol.java:215) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239) at redis.clients.jedis.Jedis.set(Jedis.java:121) at com.yhq.redis.RedisLockTest$2.run(RedisLockTest.java:76) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:748)
這種報錯是因為客戶端向redis發送的命令,redis發現接收的命令不滿足RESP協議(Redis服務器與客戶端通過RESP(REdis Serialization Protocol)協議通信)返回以字節 "-"開頭的字節流,客戶端jedis接收到后,拋出異常。
在 RESP 中, 一些數據的類型通過它的第一個字節進行判斷:
單行回復:回復的第一個字節是 "+"
錯誤信息:回復的第一個字節是 "-"
整形數字:回復的第一個字節是 ":"
多行字符串:回復的第一個字節是 "$"
數組:回復的第一個字節是 "*"
public final class Protocol { public static final byte DOLLAR_BYTE = '$'; public static final byte ASTERISK_BYTE = '*'; public static final byte PLUS_BYTE = '+'; public static final byte MINUS_BYTE = '-'; public static final byte COLON_BYTE = ':'; }
分析命令不滿足RESP協議的原因:
jedis.set("aaa","value"); ----> client.set(key, value); ----->
public class Connection implements Closeable { private RedisOutputStream outputStream; private RedisInputStream inputStream; protected Connection sendCommand(final Command cmd, final byte[]... args) { try { //獲取連接 connect(); //發送命令(向outputStream寫入拼接的命令,但是沒有執行輸出流刷新,redis還接收不到命令) Protocol.sendCommand(outputStream, cmd, args); pipelinedCommands++; return this; } catch (JedisConnectionException ex) { //...................... } broken = true; throw ex; } }
------>
public final class Protocol { private static void sendCommand(final RedisOutputStream os, final byte[] command, final byte[]... args) { try { os.write(ASTERISK_BYTE); os.writeIntCrLf(args.length + 1); os.write(DOLLAR_BYTE); os.writeIntCrLf(command.length); os.write(command); os.writeCrLf(); // 如果一個線程剛執行到這里,另一個線程執行到 outputStream.flush();操作,那么就報該異常 for (final byte[] arg : args) { os.write(DOLLAR_BYTE); os.writeIntCrLf(arg.length); os.write(arg); os.writeCrLf(); } } catch (IOException e) { throw new JedisConnectionException(e); } } }