小飞知识库 | YeLu🤠MiLu🤪 小飞知识库 | YeLu🤠MiLu🤪
  • 函数式编程
  • Spring
  • SpringMVC
  • SpringBoot
  • SpringCloud
  • Mybatis
  • JVM
  • JUC并发编程
  • 设计模式
  • 单元测试
  • Redis
  • RabbitMQ
  • mysql
  • oracle
  • linux
  • nginx
  • docker
  • elasticSearch
  • windows
  • 虚拟机
  • 监控系统
  • https
  • 内网穿透
  • 前端文章

    • JavaScript
  • 页面

    • HTML
    • CSS
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • TypeScript
    • JS设计模式总结
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 版本管理

    • Git笔记
  • 项目构建

    • maven
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • JAR包相关
  • 关于
  • 收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YeLu

爱技术的YeLu🤠
  • 函数式编程
  • Spring
  • SpringMVC
  • SpringBoot
  • SpringCloud
  • Mybatis
  • JVM
  • JUC并发编程
  • 设计模式
  • 单元测试
  • Redis
  • RabbitMQ
  • mysql
  • oracle
  • linux
  • nginx
  • docker
  • elasticSearch
  • windows
  • 虚拟机
  • 监控系统
  • https
  • 内网穿透
  • 前端文章

    • JavaScript
  • 页面

    • HTML
    • CSS
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • TypeScript
    • JS设计模式总结
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 版本管理

    • Git笔记
  • 项目构建

    • maven
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • JAR包相关
  • 关于
  • 收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Redis

    • redis介绍

    • redis安装

    • redis基础

    • redis进阶

      • 主从复制
      • 哨兵模式
      • 集群模式
      • Spring整合Redis
      • 手写Redis客户端(jedis)
        • 1、整体的架构设计
        • 2、Jedis发送给Redis服务端的数据的格式
          • 2.1、编写模拟服务端
          • 2.2、然后编写客户端请求
          • 2.3、得到数据格式
        • 3、手写客户端
          • 3.1、编写辅助枚举
          • 3.2、编写协议层
          • 3.3、编写通信层
          • 3.4、编写接口层
          • 3.5、测试
      • redis常见问题
  • 缓存
  • Redis
  • redis进阶
YeLu🤠
2023-04-05
目录

手写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.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、得到数据格式

官方数据格式 (opens new window)

*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

# 3、手写客户端

# 3.1、编写辅助枚举

用于枚举redis操作命令

public enum Commond {
    SET, GET
}
1
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

# 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

# 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

编写客户端

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

# 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
#redis
最近更新: 2025/07/30, 15:37:56
Spring整合Redis
redis常见问题

← Spring整合Redis redis常见问题→

最近更新
01
服务端配置
07-30
02
frp 安装
07-30
03
Prometheus采集Springboot应用
02-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 | YeLu🤠MiLu🤪 | MIT License 蜀ICP备2024116879号 | 川公网安备51012202001998号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
欢迎你,我的朋友
看板娘