如何手写一个Redis客户端呢?

如何手写一个Redis客户端呢?,第1张

如何手写一个Redis客户端呢?

我们要在 Java 中 *** 作 Redis,怎么做呢?首先我们先来了解一下 Redis Serialization Protocol (Redis 序列化协议),这个是 Redis 提供的一种,客户端和 Redis 服务端通信传输的编码协议,服务端收到消息后,会基于这个约定编码进行解码。

测试使用的redis是安装在虚拟机中的。

  • 打开 Wireshark 工具,对 VMnet8 这个网络进行抓包

  • 增加过滤条件

    ip.dst_host==192.168.221.128 and tcp.port in {6379}
    
  • 使用 RDM 工具连接到 Redis Server 进行 key-value *** 作,比如执行 set name cc

  • 通过 Wireshark 工具监控数据包内容,可以看到实际发出的数据包是:

    *3rnrnSETrnrnnamernrncc
    
    • 其中 *3 代表参数个数,set name cc, 表示三个参数。
    • 表示属性长度,表示包含 3 个字符。

客户端和服务器发送的命令或数据一律以 rn (CRLF 回车 + 换行)结尾。

所以基于这个协议,我们可以自己实现一个 Java 客户端。

自定义客户端

下面我们通过抓包相关的命令,了解 Redis 客户端的工作机制。

常量池

定义相关常量,包括协议中的相关常用符号及get、set方法。

public class CommandConstant {

    public static final String START = "*";

    public static final String LENGTH = "$";

    public static final String LINE = "rn";

    public enum CommandEnum {
        SET,
        GET
    }
    
}
CustomClientSocket

CustomClientSocket 用来建立网络通信连接,并且发送数据指定到 RedisServer;

public class CustomerRedisClientSocket {

    private Socket socket;

    private InputStream inputStream;

    private OutputStream outputStream;

    public CustomerRedisClientSocket(String ip, int port) {
        try {
            socket = new Socket(ip, port);
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void send(String cmd) {
        try {
            outputStream.write(cmd.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String read() {
        byte[] bytes = new byte[1024];
        int count = 0;
        try {
            count = inputStream.read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(bytes, 0, count);
    }
}
客户端
public class CustomerRedisClient {

    private CustomerRedisClientSocket customerRedisClientSocket;

    //建立连接
    public CustomerRedisClient(String host, int port) {
        customerRedisClientSocket = new CustomerRedisClientSocket(host, port);
    }

    //设置
    public String set(String key, String value) {
        customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.SET, key.getBytes(), value.getBytes()));
        return customerRedisClientSocket.read(); //在等待返回结果的时候,是阻塞的
    }

    //获取
    public String get(String key) {
        customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.GET, key.getBytes()));
        return customerRedisClientSocket.read();
    }

    //根据协议转换参数
    public static String convertToCommand(CommandConstant.CommandEnum commandEnum, byte[]... bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(CommandConstant.START).append(bytes.length + 1).append(CommandConstant.LINE);
        stringBuilder.append(CommandConstant.LENGTH).append(commandEnum.toString().length()).append(CommandConstant.LINE);
        stringBuilder.append(commandEnum.toString()).append(CommandConstant.LINE);
        for (byte[] by : bytes) {
            stringBuilder.append(CommandConstant.LENGTH).append(by.length).append(CommandConstant.LINE);
            stringBuilder.append(new String(by)).append(CommandConstant.LINE);
        }
        return stringBuilder.toString();
    }
}
测试

通过自定义客户端向redis存值并读取。

public class MainClient {
    public static void main(String[] args) {
        CustomerRedisClient customerRedisClient = new CustomerRedisClient("127.0.0.1", 6379);

        System.out.println(customerRedisClient.set("customer", "jack"));
        System.out.println(customerRedisClient.get("customer"));
    }
}

$4 代表长度为4,代表返回结果jack的长度。

总结

当我们了解了相关原理后,可以发现我们手写一个自定义客户端其实不是想象中的那么难,虽然我们的这个自定义客户端很简陋…hahhaha…

项目地址

项目地址

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5582577.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-14

发表评论

登录后才能评论

评论列表(0条)

保存