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

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

RESP协议介绍

RESP协议特点

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

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

手写Redis客户端

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客户端是不是就有思路啦。

自己实现一个

基于对源码的借鉴,简易的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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值