Redis学习记录(六)redis客户端jedis多线程操作时报异常

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

  

 

posted @ 2020-04-24 16:07  yaohuiqin  阅读(3357)  评论(1编辑  收藏  举报