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

浙公网安备 33010602011771号