手写Redis客户端(jedis)
本章代码参考:G:\学习资料\java\java学习文档\java技术文档\Spring文档\xufei-spring-word\xufei-projo-redis-client
# 1、整体的架构设计
# 2、Jedis发送给Redis服务端的数据的格式
# 2.1、编写模拟服务端
主要是为了接收 jedis请求redis 服务端的消息
public static void main(String[] args){
try {
ServerSocket socket=new ServerSocket(6379);
Socket socket1 = socket.accept();
InputStream inputStream = socket1.getInputStream();
byte[] buf=new byte[1024];
inputStream.read(buf);
System.out.println(new String(buf));
} catch (IOException e) {
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 2.2、然后编写客户端请求
public static void main(String[] args){
Jedis jedis = new Jedis("127.0.0.1", 6379);
//下一步就是操作这个数据了
jedis.set("xff","abcefg");
System.out.println(jedis.get("xff"));
}
1
2
3
4
5
6
2
3
4
5
6
# 2.3、得到数据格式
*3 数组3 表示的是下面的命令是由 3个数组构成的
$3 字符串3 表示的是下面的set命令是由3个字符构成的
SET
$3 字符串13 xff 由3个字符构成
xff
$6 字符串6 表示的是abcefg由6个字符构成
abcefg
上面这个协议的说明
For Simple Strings the first byte of the reply is "+"
For Errors the first byte of the reply is "-"
For Integers the first byte of the reply is ":"
For Bulk Strings the first byte of the reply is "$"
For Arrays the first byte of the reply is "*"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3、手写客户端
# 3.1、编写辅助枚举
用于枚举redis操作命令
public enum Commond {
SET, GET
}
1
2
3
2
3
# 3.2、编写协议层
最终发送消息
public class Protocol {
/**
* 这个定义的是数组的星星
*/
private static final String START_BYTE = "*";
/**
* 这个定义的是每个字符串长度那个美元符号
*/
private static final String DOLLER_BYTE = "$";
/**
* 这个家伙定义的是换行
*/
private static final String BLANK_BYTE = "\r\n";
/**
* *3 数组3 表示的是下面的命令是由 3个数组构成的
* <p>
* $3 字符串3 表示的是下面的set命令是由3个字符构成的
* SET
* <p>
* $3 字符串13 xff 由3个字符构成
* xff
* <p>
* $6 字符串6 表示的是abcefg由6个字符构成
* abcefg
*
* @param outputStream
* @param commond
* @param args
*/
public static void sendCommand(OutputStream outputStream, Commond commond, byte[]... args) {
//首先是不是要封装数据这个数据要封装成 符合RESP协议格式的数据
StringBuilder builder = new StringBuilder();
builder.append(START_BYTE).append(args.length + 1).append(BLANK_BYTE)
.append(DOLLER_BYTE).append(commond.name().length()).append(BLANK_BYTE)
.append(commond.name()).append(BLANK_BYTE);
//遍历前端传递过来的命令的值
for (byte[] b : args) {
builder.append(DOLLER_BYTE).append(b.length).append(BLANK_BYTE)
.append(new String(b)).append(BLANK_BYTE);
}
System.out.println("构建出来的字符串--------------");
System.out.println(builder);
System.out.println("构建出来的字符串--------------");
//接下来发送数据到Redis服务器
try {
//发送数据到Redis的服务器去
outputStream.write(builder.toString().getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 3.3、编写通信层
public class Connection {
/**
* Socket通信
*/
private Socket socket;
/**
* 连接的主机
*/
private String hostName;
/**
* 端口
*/
private int port;
/**
* 输出流
*/
private OutputStream out = null;
/**
* 输入流
*/
private InputStream in = null;
public Connection(String hostName, int port) {
this.hostName = hostName;
this.port = port;
}
/**
* 发送数据的方法
*/
public void sendCommond(Commond commond, byte[]... args) {
//发送数据需要连接
//获取了这个连接
connect();
//发送数据
Protocol.sendCommand(out, commond, args);
}
/**
* 创建连接
*/
private void connect() {
try {
if (!(isConnected())) {
//第一个是获取连接
socket = new Socket(this.hostName, this.port);
//第二个获取输出流
this.out = socket.getOutputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取服务端 返回的数据内容
*
* @return
*/
public String getServerBack() {
try {
InputStream inputStream = socket.getInputStream();
//接下来 就应该将数据 返回到调用处
byte[] buf = new byte[1024];
inputStream.read(buf);
return new String(buf);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public boolean isConnected() {
return this.socket != null && this.socket.isBound() && !this.socket.isClosed() && this.socket.isConnected() && !this.socket.isInputShutdown() && !this.socket.isOutputShutdown();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 3.4、编写接口层
添加字符编码类
public class StringEncoder {
/**
* 将字符串转换成byte类型的数组
*
* @param val
* @return
* @throws UnsupportedEncodingException
*/
public static byte[] getBytes(String val) {
return val.getBytes(StandardCharsets.UTF_8);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
编写客户端
public class MyClient {
/**
* 通信层对象
*/
private Connection connection;
/**
* 初始化客户端 连接信息
*
* @param hostName
* @param port
*/
public MyClient(String hostName, int port) {
connection = new Connection(hostName, port);
}
/**
* 设置这个数据到Redis的服务器中去
*
* @param key
* @param value
*/
public void set(String key, String value) {
connection.sendCommond(Commond.SET, StringEncoder.getBytes(key), StringEncoder.getBytes(value));
//假设这里要返回 服务器返回的数据怎么玩呢?
System.out.println("服务器返回的内容是:" + connection.getServerBack());
}
/**
* 设置这个数据到Redis的服务器中去
*
* @param key
*/
public String get(String key) {
connection.sendCommond(Commond.GET, StringEncoder.getBytes(key));
String serverBack = connection.getServerBack();
// 假设这里要返回 服务器返回的数据怎么玩呢?
System.out.println("服务器返回的内容是:" + serverBack);
return serverBack;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 3.5、测试
public class MyClientTest extends TestCase {
/**
* 测试自己写的客户端
*/
public void testMyClient() {
MyClient myClient = new MyClient("169.254.182.30", 6379);
myClient.set("my-client", "我自己手写的客户端");
System.out.println(myClient.get("my-client"));
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
最近更新: 2025/07/30, 15:37:56