zoukankan      html  css  js  c++  java
  • 【原创】自己动手写一个能操作redis的客户端

    引言

    redis大家在项目中经常会使用到。官网也提供了多语言的客户端供大家操作redis,如下图所示
    image
    但是,大家有思考过,这些语言操作redis背后的原理么?其实,某些大神会说

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

    确实,本质原理就是如上面那句话所说。博主也是以这种思路,去看了一下JAVA端的开源组件jedis的源码,然后取其精华,写了一个段能操作redis的demo,希望大家能有所收获。jedis的github地址为:https://github.com/xetorthio/jedis。
    有兴趣的童鞋,也可以自行去阅读。需要说明的是,这毕竟不是源码分析系列文章,不是带你去看jedis的源码。只是借鉴思路,写一个能操作redis的程序。

    正文

    首先,我先说一下操作思路,如下图所示
    image
    说明一下,上面的第四步,就是我们自己要写的操作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
    $3
    SET
    $3
    eat
    $13
    I want to eat
    

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

    Simple to implement.
    Fast to parse.
    Human readable.

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

    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 " " (CRLF).

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

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

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

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

    *2
    $3
    GET
    $3
    eat
    

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

    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();
    		//虽然输出的时候,会被转义,然而我们传送的时候还是要带上
    
    		sb.append("*3").append("
    ");
    		sb.append("$3").append("
    ");
    		sb.append("SET").append("
    ");
    		sb.append("$").append(key.length()).append("
    ");
    		sb.append(key).append("
    ");
    		sb.append("$").append(value.length()).append("
    ");
    		sb.append(value).append("
    ");
    		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协议的内容。记住,一定要带 字符作为结尾
    OK,运行上述代码,你会发现你可以往redis中set数据了,并且控制台输出如下

    +OK
    

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

    总结

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

  • 相关阅读:
    Django入门
    Python从入门到放弃
    Python中的元类(metaclass)
    蓝鲸gse启动失败
    VS2019添加微软ReportViewer
    DevExpress WinForms各版本与 .NET、Visual Studio 的版本兼容性
    SQL语句查询每个分组的前N条记录的实现方法
    如何查看Windows安装版本号
    学习webpack
    Python3.x将代码打包成exe程序并添加图标
  • 原文地址:https://www.cnblogs.com/rjzheng/p/9347749.html
Copyright © 2011-2022 走看看