简介
Redis缓存技术常备作为互联网架构中的热点技术,常用来提升系统的性能,常被应用在一些读多写少的场景。Jedis基于Java实现,通过Jedis底层的协议,我们很简单就可以访问Redis服务器,如Redis中的Set、keys、get等命令。本文将简单模拟Jedis的实现。
原理
Redis使用RESP协议,其描述如下:
Jedis协议地址
验证:
我们可以手写一个ServerSocket,让Jedis连接我们的ServerSocket,然后ServerSocket将受到的消息打印出来。
如我们使用Set命令,在RESP协议中,传输内容如下:
# set("iname", "zhanghaolin")
*3 # *代表数组个数
$3 # $表示下一个字符串的长度
SET # 命令
$5
iname
$11
zhanghaolin
代码实现
Jedis中将API分了三层,分别为传输层,协议层和API层。传输层主要使用Socket进行通信,协议层主要对要发送的对象进行包装,API层主要开放接口给调用者。在我的代码中分别对应connection、protocol、client。以上为Jedis的核心层,另外还可增加一些其他层,如安全编码等,如文中的safe。
1、Connection层
package org.redis.connection;
import org.redis.protocol.Protocol;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Connection {
private String host;
private int port;
private Socket socket;
private InputStream is;
private OutputStream os;
public Connection(String host, int port) {
this.host = host;
this.port = port;
}
private void connect(){
try {
if(!isConnected()){
socket = new Socket(host,port);
this.is = socket.getInputStream();
this.os = socket.getOutputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean isConnected(){
return this.socket != null && this.socket.isBound() && !this.socket.isClosed() && this.socket.isConnected();
}
public void sendCommand(Protocol.Command command,byte []... bytes){
connect();
Protocol.sendCommand(os,command,bytes);
}
public String replyCodeString(){
byte [] bytes = new byte[1024];
try {
is.read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return new String(bytes);
}
public void close(){
try {
if(os != null){
os.close();
}
if(is != null){
is.close();
}
if(socket != null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、Protocol层
package org.redis.protocol;
import java.io.IOException;
import java.io.OutputStream;
public class Protocol {
private static final String DOLLER_STRING = "$";
private static final String ALERSTIC_STRING = "*";
private static final String CTLF_STRING = "\r\n";
public static void sendCommand(OutputStream os,Command command,byte [] ... bytes){
StringBuilder builder = new StringBuilder();
builder.append(ALERSTIC_STRING).append(bytes.length + 1).append(CTLF_STRING);
builder.append(DOLLER_STRING).append(command.name().length()).append(CTLF_STRING);
builder.append(command.name()).append(CTLF_STRING);
for(byte [] arg:bytes){
builder.append(DOLLER_STRING).append(arg.length).append(CTLF_STRING);
builder.append(new String(arg)).append(CTLF_STRING);
}
try {
os.write(builder.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public static enum Command{
SET,GET,INCR,KEYS
}
}
3、client层
package org.redis.client;
import org.redis.connection.Connection;
import org.redis.protocol.Protocol;
public class Client {
private Connection connection;
public Client(String host,int port){
this.connection = new Connection(host,port);
}
public String set(String key, String value){
connection.sendCommand(Protocol.Command.SET,key.getBytes(),value.getBytes());
return connection.replyCodeString();
}
public String get(String key){
connection.sendCommand(Protocol.Command.GET,key.getBytes());
return connection.replyCodeString();
}
public String incr(){
return null;
}
public void close() {
if(connection != null) connection.close();
}
}
4、safe层
package org.redis.safe;
import java.io.UnsupportedEncodingException;
public class SaveEncoding {
public static byte [] encode(String string){
if(string == null){
throw new NullPointerException("encode字符串不能为空");
}
try {
return string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
总结
本文简要介绍了Jedis以及RESP协议,并提供了其Java版本的简单实现。
参考文献
https://blog.csdn.net/weixin_35586546/article/details/85256635