文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

自己动手写一个能操作redis的客户端

2023-06-05 06:42

关注

作者:孤独烟 来源:微信订阅号(程序员孤独烟)

原文链接:https://mp.weixin.qq.com/s/IBynkex-FHhvJ3tmizvJhA

引言

redis大家在项目中经常会使用到。官网也提供了多语言的客户端供大家操作redis,如下图所示

自己动手写一个能操作redis的客户端

但是,大家有思考过,这些语言操作redis背后的原理么?其实,某些大神会说

只要按照redis的协议,发送指定数据给redis,监听返回值即可。


确实,本质原理就是如上面那句话所说。博主也是以这种思路,去看了一下JAVA端的开源组件jedis的源码,然后取其精华,写了一个段能操作redis的demo,希望大家能有所收获。


jedis的github地址为:
https://github.com/xetorthio/jedis


有兴趣的童鞋,也可以自行去阅读。需要说明的是,这毕竟不是源码分析系列文章,不是带你去看jedis的源码。只是借鉴思路,写一个能操作redis的程序。

正文

首先,我先说一下操作思路,如下图所示

自己动手写一个能操作redis的客户端

说明一下,上面的第四步,就是我们自己要写的操作redis的小demo。

1、先写一个socket监听6379端口

这个程序很easy,度娘一下出来一大把

import java.io.IOException;import java.io.InputStreamReader;import java.io.Reader;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {    public static void main(String[] args) throws IOException {        ServerSocket server = new ServerSocket(6379);        Socket socket = server.accept();        byte[] chars = new byte[64];        socket.getInputStream().read(chars);        System.out.println(new String(chars));    }}

2、采用开源客户端,操作一次redis

我这里用的是JAVA语言的jedis,大家自己也可以用其他的任意语言组件,目的是为了采集客户端在操作redis时,发送出的数据

import redis.clients.jedis.Jedis;public class RedisTest {    public static void main(String[] args) {        Jedis jedis = new Jedis("127.0.0.1", 6379);        jedis.set("eat", "I want to eat");    }}

3、看看socket监听到的数据

在这里运行一下第二步的代码,查看第一步的代码输出的数据,如下所示

*3$3SET$3eat$13I want to eat

那么,这组数据是什么含义呢?
我们去官网进行查询。原来,redis的客户端和服务端采取了一种RESP协议。相应文档地址如下
https://redis.io/topics/protocol
RESP设计巧妙,它的前景在于下面三个方面:

那么+、-、*、:、$这些符号是什么意思呢?
官网有这么一段话

In RESP, the type of some data depends on the first byte:  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 "*"
 Additionally RESP is able to represent a Null value using a special variation of Bulk Strings or Array as specified later.
 In RESP different parts of the protocol are always terminated with "\r\n" (CRLF).

翻译过来
(1)简单字符串Simple Strings, 以 "+"加号 开头
(2)错误Errors, 以"-"减号 开头
(3)整数型Integer, 以 ":" 冒号开头
(4)大字符串类型Bulk Strings, 以 "$"美元符号开头,长度限制512M
(5)组类型Arrays,以 "*"星号开头
并且,协议的每部分都是以 "\r\n" (CRLF) 结尾的。

OK,那我们刚才的那一串的数据的意思就是(没有看到""\r\n",是因为已经转义了,所以无法看到):

*3   数组包含3个元素,分别是SET、eat、I want to eat$3   是一个字符串,且字符串长度为3SET  字符串的内容$3   是一个字符串,且字符串长度为3eat  字符串的内容$13  是一个字符串,且字符串长度为13I want to eat 字符串的内容

提问,如果是get命令,那么传输的RESP的内容长什么样?
比如有一个命令get eat,那么此时的内容如下所示

*2$3GET$3eat

没有\r\n是因为已经转义了,所以没看到。其他的命令,可以自行测试。

4、尝试构造一样的数据操作redis

OK,经过上面的铺垫。我们如果要对redis做一个set操作,则构造set命令的RESP协议内容,并且利用socket编程,将这串内容发送给redis即可。这里用java的socket编程实现,用其他语言也是一样的。
我们有一个类 RedisClient.java
代码如下

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.net.UnknownHostException;public class RedisClient {    private Socket socket;                                         private OutputStream outputStream;    private InputStream inputStream;    public RedisClient(String host, int port){        try {            this.socket = new Socket(host,port);            this.outputStream = this.socket.getOutputStream();            this.inputStream = this.socket.getInputStream();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }    public String set(final String key, String value) {        StringBuilder sb = new StringBuilder();        //虽然输出的时候,会被转义,然而我们传送的时候还是要带上\r\n        sb.append("*3").append("\r\n");        sb.append("$3").append("\r\n");        sb.append("SET").append("\r\n");        sb.append("$").append(key.length()).append("\r\n");        sb.append(key).append("\r\n");        sb.append("$").append(value.length()).append("\r\n");        sb.append(value).append("\r\n");        byte[] bytes= new byte[1024];        try {            outputStream.write(sb.toString().getBytes());            inputStream.read(bytes);        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return new String(bytes);    }    public static void main(String[] args) {        RedisClient redisClient = new RedisClient("127.0.0.1", 6379);        String result = redisClient.set("eat", "please eat");        System.out.println(result);         }}

上面的public String set(final String key, String value)方法中,显示了,我们假如需要对redis进行set操作,需要传输的RESP协议的内容。记住,一定要带\r\n字符作为结尾
OK,运行上述代码,你会发现你可以往redis中set数据了,并且控制台输出如下

+OK

提问,你自己会封装get命令么?

总结

本文以一种循序渐进的方式带领大家写了一个能操作redis的demo,希望大家有所收获。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯