如何用Java Socket实现一个简单的Redis客户端
Redis是最常见的缓存服务中间件,在java开发中,一般使用 jedis 来实现。
如果不想依赖第三方组件,自己实现一个简单的redis客户端工具,该如何实现呢?本文就是介绍这样一种方法。
Redis的协议非常简单,而且输入数据和输出数据都遵循统一的协议,具体规则参考这里:
http://redisdoc.com/topic/protocol.html
Redis的命令协议:
$参数数量n
$参数1的值的字节数组长度
$参数1的值的字符串表示
$参数2的值的字节数组长度
$参数2的值的字符串表示
…
$参数n的值的字节数组长度
$参数n的值的字符串表示
Redis的返回协议:
1、状态回复(status reply)的第一个字节是 “+”,单行字符串;
2、错误回复(error reply)的第一个字节是 “-“;
3、整数回复(integer reply)的第一个字节是 “:”;
4、批量回复(bulk reply)的第一个字节是 “$”;
5、多条批量回复(multi bulk reply)的第一个字节是 “*”;
6、所有的命令都是以
结尾。
Java代码说明
针对上述规则,我们用两个类来实现:
1、SimpleRedisClient类,主要用于发送请求,并读取响应结果(字符串);
整体比较简单,稍微复杂点的地方就是读取流数据,遇到两种情况就该结束循环,一是返回长度为-1,二是返回字符串以
结尾。
如果处理不当,可能会导致 read 阻塞,Socket卡住。
2、SimpleRedisData类,用于解析响应结果,把redis统一协议的字符串,解析为具体的对象。
这部分代码完全是按照协议规则来实现的,通过一个游标 pos 来向前移动,在移动过程中识别不同格式的数据。
最复杂的是 list 类型的数据,以 * 开头,后面跟着一个整数,表示列表中所有元素的数量,然后就是每一个列表元素的值,循环解析即可。
package demo;
import java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
public class SimpleRedisClient implements Closeable {
private String host;
private int port;
private String auth;
private Socket socket = null;
public SimpleRedisClient(String host, int port, String auth) {
this.host = host;
this.port = port;
this.auth = auth;
try {
socket = new Socket(this.host, this.port);
socket.setSoTimeout(8 * 1000);//8秒
} catch (Exception ex) {
socket = null;
ex.printStackTrace();
}
}
public boolean connect() throws IOException {
if (socket == null || auth == null || auth.length() <= 0) {
return false;
}
String response = execute("AUTH", auth);
if (response == null || response.length() <= 0) {
return false;
}
String res = new SimpleRedisData(response).getString();
return "OK".compareTo(res) == 0;
}
@Override
public void close() {
try {
if (socket != null) {
socket.shutdownOutput();
socket.close();
}
//System.out.println("closed");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public String getString(String key) {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
try {
String response = execute("GET", key);
return new SimpleRedisData(response).getString();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public String setString(String key, String value) {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
try {
String response = execute("SET", key, value);
return new SimpleRedisData(response).getString();
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public String deleteKey(String key) throws IOException {
if (socket == null || key == null || key.isEmpty()) {
return null;
}
String response = execute("DEL", key);
return new SimpleRedisData(response).getString();
}
public List<String> getKeys(String pattern) throws IOException {
if (socket == null || pattern == null || pattern.isEmpty()) {
return null;
}
String response = execute("KEYS", pattern);
return new SimpleRedisData(response).getStringList();
}
public String execute(String... args) throws IOException {
if (socket == null || args == null || args.length <= 0) {
return null;
}
//System.out.println(StringUtil.join(args, " "));
StringBuilder request = new StringBuilder();
request.append("*" + args.length).append("
");//参数的数量
for (int i = 0; i < args.length; i++) {
request.append("$" + args[i].getBytes("utf8").length).append("
");//参数的长度
request.append(args[i]).append("
");//参数的内容
}
socket.getOutputStream().write(request.toString().getBytes());
socket.getOutputStream().flush();
StringBuilder reply = new StringBuilder();
int bufSize = 1024;
while (true) {
byte[] buf = new byte[bufSize];
int len = socket.getInputStream().read(buf);
if (len < 0) {
break;
}
String str = new String(buf, 0, len);
reply.append(str);
if (str.endsWith("
")) {
break;
}
}
String response = reply.toString();
//System.out.println("response: " + response);
return response;
}
}


