通常我们调用Jedis提供的API来操作Redis,本文将手写一个简单的Redis客户端,实现Jedis的set、get和incr功能。
用Jedis连接NIO服务端,发送指令
NIO服务端以前写过,代码就不贴了,可参考此链接Java基础 BIO & NIO 设计思想。
JedisTest类
package com.tbryant.jedistest;
import redis.clients.jedis.Jedis;
public class JedisTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 8888);
System.out.println(jedis.set("tonny", "bryant"));
System.out.println(jedis.get("tonny"));
System.out.println(jedis.incr("count"));
}
}
运行结果
*:星号是开始符,3表示后面跟着3组指令,每2行组成一个指令。
$:dollar符后面跟数字,表示指令长度。
由此可见Jedis连接NIO服务端成功,并发送set方法对应的三组指令,但未收到服务端的返回数据,最终超时异常关闭连接。
自定义Redis Client
角色划分
常量类:存储指令符号和操作谓词。
自定义Socket类:用于发送和读取数据。
自定义RedisClient类:提供API操作Redis。
测试类:使用自定义RedisClient与Redis交互。
Constant类
package com.tbryant.customredisclient;
public class Constant {
public static final String START = "*";// 开始符
public static final String LENGTH = "$";// 指令长度符
public static final String LINE = "\r\n";// 换行符
public enum command {
SET,
GET,
INCR
}
}
CustomSocket类
package com.tbryant.customredisclient;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class CustomSocket {
private Socket socket;
private InputStream inputStream;
private OutputStream outputStream;
public CustomSocket(String ip, int port) {
try {
socket = new Socket(ip, port);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
// 把拼接完的指令发送给redis
public void send(String cmd) {
try {
outputStream.write(cmd.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
// 接收redis返回的数据
public String read() {
byte[] bytes = new byte[1024];// TODO 处理超过1024的返回数据
int count = 0;
try {
count = inputStream.read(bytes);
} catch (IOException e) {
e.printStackTrace();
}
return new String(bytes, 0, count);
}
}
CustomRedisClient类
package com.tbryant.customredisclient;
public class CustomRedisClient {
private CustomSocket redisSocket;
public CustomRedisClient(String host, int port) {
redisSocket = new CustomSocket(host, port);
}
public String set(String key, String value) {
redisSocket.send(convertToCommand(Constant.command.SET, key.getBytes(), value.getBytes()));
return redisSocket.read();
}
public String get(String key) {
redisSocket.send(convertToCommand(Constant.command.GET, key.getBytes()));
return redisSocket.read();
}
public String incr(String key) {
redisSocket.send(convertToCommand(Constant.command.INCR, key.getBytes()));
return redisSocket.read();
}
public static String convertToCommand(Constant.command command, byte[]... bytes) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Constant.START).append(bytes.length + 1).append(Constant.LINE);
stringBuilder.append(Constant.LENGTH).append(command.toString().length()).append(Constant.LINE);
stringBuilder.append(command.toString()).append(Constant.LINE);
for (byte[] aByte : bytes) {
stringBuilder.append(Constant.LENGTH).append(aByte.length).append(Constant.LINE);
stringBuilder.append(new String(aByte)).append(Constant.LINE);
}
return stringBuilder.toString();
}
}
Application类
package com.tbryant.customredisclient;
public class Application {
public static void main(String[] args) {
CustomRedisClient redisClient = new CustomRedisClient("127.0.0.1", 6379);
System.out.println(redisClient.set("tonny", "bryant"));
System.out.println(redisClient.get("tonny"));
System.out.println(redisClient.incr("count"));
}
}
测试代码用CustomRedisClient替换Jedis,端口号指向Redis服务端,其他代码与JedisTest保持一致。
运行结果
操作Redis成功,并接收到Redis返回数据。查看Redis官网可以得知Redis遵循RESP协议,该协议规定Redis返回符号定义:
+:加号后面跟简单字符串。
-:减号后面跟错误信息。
::冒号后面跟int类型。
$:doller符后面跟长字符串。
*:星号后面跟数组。
源码地址:CustomJedis模块