선요약: Jedis는 Socket을 통해 InputStream,OutputStream을 이용해 통신
우선 Jedis는 DB연결과 비슷하게 Pool을 이용해서 Redis와 연결된 객체들을 관리한다. 사용자들은 Pool을 통해 Jedis객체를 가져와서 사용한다.
//jedis의 Pool클래스의 Jedis를 받아오는 메소드
public T getResource() {
try {
return internalPool.borrowObject();
} catch (NoSuchElementException nse) {
throw new JedisException("Could not get a resource from the pool", nse);
} catch (Exception e) {
throw new JedisConnectionException("Could not get a resource from the pool", e);
}
}
이렇게 가져온 Jedis를 통해 Redis의 get, set, hset, expire등의 명령어를 수행하는데, 이때 각 Redis명령어를 수행하는 것은 Jedis클래스에서 처리한다. Jedis클래스는 BinartJedis라는 클래스를 상속받고 있는데 Jedis, BinaryJedis에서 Redis명령어 수행에 필요한 메소드 들이 구현되어있다.
//Jedis
// Redis의 set명령어를 수행한다.
public String set(final String key, final String value, final String nxxx, final String expx,
final long time) {
checkIsInMultiOrPipeline();
client.set(key, value, nxxx, expx, time);
return client.getStatusCodeReply();
}
//BinaryJedis
//현재 Jedis가 multi상태인지 체크를 한다
protected void checkIsInMultiOrPipeline() {
if (client.isInMulti()) {
throw new JedisDataException(
"Cannot use Jedis when in Multi. Please use Transation or reset jedis state.");
} else if (pipeline != null && pipeline.hasPipelinedResponse()) {
throw new JedisDataException(
"Cannot use Jedis when in Pipeline. Please use Pipeline or reset jedis state .");
}
}
set메소드안에 client 부분이 Redis-Client에 붙어서 호출을 하는 역할이다.
이 client도 BinaryJedis라는 클래스를 상속받고 있고, BinaryJedis는 Connect라는 클래스를 상속받고 있다.
protected Connection sendCommand(final Command cmd, final byte[]... args) {
try {
connect();
Protocol.sendCommand(outputStream, cmd, args);
pipelinedCommands++;
return this;
} catch (JedisConnectionException ex) {
/*
* When client send request which formed by invalid protocol, Redis send back error message
* before close connection. We try to read it to provide reason of failure.
*/
try {
String errorMessage = Protocol.readErrorLineIfPossible(inputStream);
if (errorMessage != null && errorMessage.length() > 0) {
ex = new JedisConnectionException(errorMessage, ex.getCause());
}
} catch (Exception e) {
/*
* Catch any IOException or JedisConnectionException occurred from InputStream#read and just
* ignore. This approach is safe because reading error message is optional and connection
* will eventually be closed.
*/
}
// Any other exceptions related to connection?
broken = true;
throw ex;
}
}
위 코드는 Connect에서 실제로 Redis-Client에 메세지를 전달하는 부분이다. 저안의 connect메소드에서 현재 Connect가 Redis-Client와 소캣 연결이 되어있는지 확인한다. 그리고 Socket연결로 OutputStream,InputStream을 이용해서 통신한다.
public void connect() {
if (!isConnected()) {
try {
socket = new Socket();
// ->@wjw_add
socket.setReuseAddress(true);
socket.setKeepAlive(true); // Will monitor the TCP connection is
// valid
socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to
// ensure timely delivery of data
socket.setSoLinger(true, 0); // Control calls close () method,
// the underlying socket is closed
// immediately
// <-@wjw_add
socket.connect(new InetSocketAddress(host, port), connectionTimeout);
socket.setSoTimeout(soTimeout);
outputStream = new RedisOutputStream(socket.getOutputStream());
inputStream = new RedisInputStream(socket.getInputStream());
} catch (IOException ex) {
broken = true;
throw new JedisConnectionException(ex);
}
}
}
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();
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);
}
}