Redis学习笔记(五):redis使用的RESP报文格式和手写Redis简易客户端


Redis客户端和服务端交互是通过tcp协议,在通讯的报文格式使用的是RESP协议规范,也就是意味只要和Redis服务端建立Scoket连接,通过RESP报文格式传输数据就可以实现Redis客户端和服务端的交互。看起来是很简单的,但是实际上的确是这么简单,RESP报文格式的可读性也是很高的。

1. RESP协议介绍

1.1 RESP协议特点

RESP是Redis通讯的协议规范,有以下几种特点:

  • 简单的实现,人工也就可以写的出来
  • 快速的被计算机解析
  • 简单的可以被人工解析
  • 基于网络层,Redis在tcp端口6379(默认)上监听到来的连接(本质是Socket),客户端连接到来时,Redis服务器为此建立一个tcp连接。

1.2 RESP格式规范

RESP中涉及到主要的两个符号,分别是*$,其中*表示此报文里面有几个$符,准确的说是几组。$表示本组数据所占的字符数。文字干巴巴,直接看例子:

*3
$3
SET
$3
key
$5
joker
  • 报文总共有三组$数据组成,所以开头的*标明的值是3
  • 第一组数据是SET,占用3个字符,所以$标明的值为3,下面的两组以此类推

为了可阅读性上面写成是一列,但是实际上他们是组成一个字符串发送,需要注意的是,每一行都是独立的一行,需要在字符串中加入\r\n换行才行,压缩后如下:

*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\njoker\r\n

注意压缩成字符串后末尾也是需要\r\n的。(Redis的AOF持久化文件中也是这样保存的)

了解RESP协议规范的通讯以及报文格式,接下来就可以根据这些知识来写一个属于自己的Redis客户端啦。

2. 手写Redis客户端

2.1 Jedis源码是怎么实现的

手写客户端代码其实不是自创的,在Jedis中就有的,先看一下Jedis内部的实现源码:

protected Connection sendCommand(Protocol.Command cmd, byte[]... args) {
    try {
        this.connect();//建立Socket连接
        Protocol.sendCommand(this.outputStream, cmd, args);//封装报文并将报文写入流中
        ++this.pipelinedCommands;
        return this;
    } catch (JedisConnectionException var6) {
        JedisConnectionException ex = var6;
        try {
            String errorMessage = Protocol.readErrorLineIfPossible(this.inputStream);
            if (errorMessage != null && errorMessage.length() > 0) {
                ex = new JedisConnectionException(errorMessage, ex.getCause());
            }
        } catch (Exception var5) {
        }
        this.broken = true;
        throw ex;
    }
}

这段源码并不难找,使用Jedis的set方法,然后一直跟进去就可以。最终方法的位置是redis.clients.jedis.Connection.sebdCommand()

从这个方法的内部实现就可以看出来其实就是通过Socket建立tcp连接,然后将命令和数据转换成RESP协议规范的报文格式,最后通过Socket将数据传入过去。知道这些对于自己写一个Jedis客户端是不是就有思路啦。

2.2 自己实现一个

基于对源码的借鉴,简易的Jedis实现如下:

public class CustomJedis {
    public static void main(String[] args) throws IOException {
        //建立socket连接
        Socket socket = new Socket();
        InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
        socket.connect(socketAddress, 10000);
        //获取scoket输出流,将报文转换成byte[]传入流中
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(command());
        //获取返回的输出流,并打印输出数据
        InputStream inputStream = socket.getInputStream();
        byte[] buffer = new byte[1024];
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
    }
    //组装报文信息
    private static byte[] command() {
        return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
    }
}

但是这里需要注意,上面的实现方式是直接建立socket连接,Redis很多时候是设置密码认证的,如果这样的话上面的代码就需要改动啦。

改动后如下:

public class CustomJedis {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
        socket.connect(socketAddress, 10000);
        OutputStream outputStream = socket.getOutputStream();
        //验证密码
        outputStream.write(auth());
        InputStream inputStream = socket.getInputStream();
        byte[] buffer = new byte[1024];
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
        //发送数据
        outputStream.write(command());
        inputStream.read(buffer);
        System.out.println("返回执行结果:" + new String(buffer));
        inputStream.close();
        outputStream.close();
    }
    //验证
    private static byte[] auth(){
        return "*2\r\n$4\r\nAUTH\r\n$12\r\nitcrud_redis\r\n".getBytes();
    }
    //组装报文信息
    private static byte[] command() {
        return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
    }
}

文章作者: 程序猿洞晓
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 程序猿洞晓 !
评论
 上一篇
Redis学习笔记(六):Redis之消息发布、订阅机制 Redis学习笔记(六):Redis之消息发布、订阅机制
消息的发布和订阅,第一想到的是Kafka、RabbitMQ、ActiveMQ等,但是实际上Redis也是有这个功能,这个功能在Redis中实现很简单,也比较粗暴。没有存储,没有各种订阅模式。只要订阅同一个渠道的订阅者就都可以收到发布到该渠道的信息。如果没有订阅者,消息也不会缓存起来,而是直接丢弃。在简单的功能、能够接受这种模式并且有补偿机制的业务中是可以考虑使用的。下面看一下这个到底是怎么玩的。
2019-08-20
下一篇 
数据结构和算法(一):常用编码算法Base64的前世今生 数据结构和算法(一):常用编码算法Base64的前世今生
几年前面试的时候,数据结构和算法基本都是不问的,但是这几年随着程序猿数量的增加,互联网红利的下滑,面试越来越严格,要求也越来越高。源码+数据结构+算法都是家常便饭啦。对于有跳槽想法的你我,及时的补充数据结构和算法知识可是迫在眉睫的事情。
  目录