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); } } }