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

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

引言

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


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

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

确实,本质原理就是如上面那句话所说。博主也是以这种思路,去看了一下JAVA端的开源组件jedis的源码,然后取其精华,写了一个段能操作redis的demo,希望大家能有所收获。
jedis的github地址为:
https://github.com/xetorthio/jedis
有兴趣的童鞋,也可以自行去阅读。需要说明的是,这毕竟不是源码分析系列文章,不是带你去看jedis的源码。只是借鉴思路,写一个能操作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
$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 "\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   是一个字符串,且字符串长度为3
SET  字符串的内容
$3   是一个字符串,且字符串长度为3
eat  字符串的内容
$13  是一个字符串,且字符串长度为13
I want to eat 字符串的内容

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

*2
$3
GET
$3
eat

没有\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 blocke.printStackTrace();}}public String set(final String key, String value) {StringBuilder sb = new StringBuilder();//虽然输出的时候,会被转义,然而我们传送的时候还是要带上\r\nsb.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 blocke.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,希望大家有所收获。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

离线安装 VS2017 的正确姿势

国内的网络环境,真的是有很大的不同,有的人装 VS 的时候,号称满速,有的人(其实就是我)要等它下载很久,还告诉我有个组件没有安装成功。很久很久以前,VS 是提供 ISO 版的离线安装包的…

SQL编程

一、变量的分类: 1.全局变量(成员变量): 必须是以为前缀,由系统定义,我们只能读取,不能修改它的值。 2.局部变量: 必须是以为前缀,必须先声明在赋值。 二、局部变量&…

Android中ImageView的旋转与缩放

说明在代码中已经注释 XML代码 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"wra…

ASP.NET Core MVC四种枚举绑定方式

前言 本节我们来讲讲在ASP.NET Core MVC又为我们提供了哪些方便&#xff0c;之前我们探讨过在ASP.NET MVC中下拉框绑定方式&#xff0c;这节我们来再来重点看看枚举绑定的方式&#xff0c;充分实现你所能想到的场景&#xff0c;满满的干货&#xff0c;你值得拥有。 探讨枚举绑…

关于分布式事务、两阶段提交协议、三阶提交协议

转载自 关于分布式事务、两阶段提交协议、三阶提交协议 随着大型网站的各种高并发访问、海量数据处理等场景越来越多&#xff0c;如何实现网站的高可用、易伸缩、可扩展、安全等目标就显得越来越重要。 为了解决这样一系列问题&#xff0c;大型网站的架构也在不断发展。提高…

高级查询

一、子查询&#xff1a; 1.语法&#xff1a; select 列名 from 表名 where 列名 比较运算符(>、<、) (子查询) 2.将子查询和比较运算符联合使用&#xff0c;必须保证子查询的返回结果不大于1条 3.子查询是一个嵌套在select,insert,update和delete语句或者其他子查询中的查…

Android实现动画Dialog

案例为动画实现Dialog退出APP Activity package com.mingrisoft.animdialog; import android.app.Activity; import android.app.AlertDialog; import android.os.Bundle; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import and…

React对标签属性进行限制(props)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>对props进行限制</title> </head> <body><!-- 准备好一个“容器” --><div id"test1"></div><div …

服务的协作:服务间的消息传递——《微服务设计》读书笔记

在微服务集成——《微服务设计》读书笔记文章中&#xff0c;我们说过服务间的消息传递有几种方式&#xff0c;一种是请求/响应技术&#xff0c;另一种是基于事件的机制。 RPC&#xff08;远程过程调用&#xff09; RPC是Remote Procedure Call的简称。 这是请求/响应技术的一种…

事务、视图和索引

一、事务&#xff1a; 1.概念&#xff1a;是作为单个逻辑工作单元执行的一系列操作 2.多个操作作为一个整体向系统提交&#xff0c;要么都执行&#xff0c;要么都不执行。 3.事务是一个不可分割的工作逻辑单元。 二、事务的四个特性&#xff1a; 1.原子性 2.一致性 3.隔离性 4.…

jdbc事务和事务的隔离级别

转载自 jdbc事务和事务的隔离级别 在jdbc的使用中以最简单的jdbc的使用为例&#xff0c;说明了jdbc的具体用法。然而在通常项目中&#xff0c;需要考虑更多内容&#xff0c;例如事务。 事务&#xff0c;在单个数据处理单元中&#xff0c;存在若干个数据处理&#xff0c;要么整…

Android自定义View画钟表

第一种使用背景表盘 主要代码&#xff1a; package com.zjs.zidingyiview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; impo…

记录程序人生

许久&#xff0c;没有在博客上发关于自己的记录了。 以前还是经常在博客上写长篇大论来记录生活的&#xff0c;其中有几篇博文被学生和其他朋友们都做一个励志来勉励自己的&#xff0c;我不由的感到自豪。 那时候在上学&#xff0c;貌似在上班之后就没有在写过类似“励志”的…

理解C# 4 dynamic(3) – DynamicObject的使用

上篇文章"理解C# 4 dynamic(2) – ExpandoObject的使用" 了解了xpandoObject的基本使用。但ExpandoObject的问题就是它是一个万金油&#xff0c;什么都可以做&#xff0c;但是又都不专注。使用DynamicObject正好可以解决这个问题。这篇文章介绍DynamicJson是如何继承…

jsx中的注释的写法

{}表示里面要写js语句 js语句里面的注释为/**/

Java中的位移运算

位移运算符 左移 “a<<b”将二进制形式的a逐位左移b位&#xff0c;最低位空出来的b位补0。 带符号右移 “a>>b”将二进制形式的a逐位右移b位&#xff0c;最高位空出来的b位补原来的符号位。 无符号右移 “a>>>b”将二进制形式的a逐位右移b位&#xff0c…

sqlserver存储过程

一、存储过程 1.预先存储好的sql程序&#xff0c;保存在sqlserver中&#xff0c;通过名称和参数执行。 2.存储过程中可以包含&#xff1a;insert语句&#xff0c;update语句&#xff0c;delete语句&#xff0c;select语句&#xff0c;逻辑控制语句以及变量。 二、存储过程的优点…

微软正在用实际行动告诉你: 拥抱开源,微软是认真的

2017年4月19日至20日&#xff0c;由工业和信息化部指导、中国信息通信研究院主办、云计算开源产业联盟承办的"全球云计算开源峰会"在国家会议中心举行。微软.NET CORE开发平台荣获由峰会云计算开源产业联盟(OSCAR) 评选出的“尖峰开源技术”奖&#xff0c;标志着国内…

初识Windows程序

一、创建windows应用程序的步骤 1.新建项目 2.项目类型为C#项目 3.模板为windows应用窗体 二、构造方法&#xff1a; 语法&#xff1a;访问修饰符 类名(){} 三、命名规范前缀&#xff1a; 标签–>lable–>lbl 文本框–>textbox–>txt 组合框–>combobox–>cb…

React不提交表单并且获取表单中的数据

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>1_非受控组件</title> </head> <body><!-- 准备好一个“容器” --><div id"test"></div><!-- 引入…