引言

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 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,希望大家有所收获。

【原创】自己动手写一个能操作redis的客户端的更多相关文章

  1. 死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  2. 死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  3. 动手写一个简单版的谷歌TPU-指令集

    系列目录 谷歌TPU概述和简化 基本单元-矩阵乘法阵列 基本单元-归一化和池化(待发布) TPU中的指令集 SimpleTPU实例: (计划中) 拓展 TPU的边界(规划中) 重新审视深度神经网络中的 ...

  4. 动手写一个简单版的谷歌TPU-矩阵乘法和卷积

    谷歌TPU是一个设计良好的矩阵计算加速单元,可以很好的加速神经网络的计算.本系列文章将利用公开的TPU V1相关资料,对其进行一定的简化.推测和修改,来实际编写一个简单版本的谷歌TPU.计划实现到行为 ...

  5. 自己动手写一个服务网关-java

    自己动手写一个服务网关 原文链接:https://www.cnblogs.com/bigben0123/p/9252444.html 引言 什么是网关?为什么需要使用网关? 如图所示,在不使用网关的情 ...

  6. 动手写一个简单的Web框架(模板渲染)

    动手写一个简单的Web框架(模板渲染) 在百度上搜索jinja2,显示的大部分内容都是jinja2的渲染语法,这个不是Web框架需要做的事,最终,居然在Werkzeug的官方文档里找到模板渲染的代码. ...

  7. 动手写一个简单的Web框架(Werkzeug路由问题)

    动手写一个简单的Web框架(Werkzeug路由问题) 继承上一篇博客,实现了HelloWorld,但是这并不是一个Web框架,只是自己手写的一个程序,别人是无法通过自己定义路由和返回文本,来使用的, ...

  8. 动手写一个简单的Web框架(HelloWorld的实现)

    动手写一个简单的Web框架(HelloWorld的实现) 关于python的wsgi问题可以看这篇博客 我就不具体阐述了,简单来说,wsgi标准需要我们提供一个可以被调用的python程序,可以实函数 ...

  9. 推荐一个好的Redis GUI 客户端工具

    推荐一个好的Redis GUI 客户端工具 Redis Desktop Manager  

随机推荐

  1. 小米手机Toast显示带应用名称问题解决方法

    近期为了适配刘海屏,向公司申购了一步小米8的手机,然后测试人员那边测出来一堆适配的问题,其中有一个每一个Toast会显示app的名称+显示的内容,然后网上查找了一下解决方法记录一下,顺便封装了Toas ...

  2. 微信小程序中如何获取for循环的item相关值到JS页面的问题

    今天小程序开发过程中,遇到了这个棘手的问题.由于我没有前端基础,只是知道一点儿基本的HTML标签,所以卡了好久,特此分享,望后来的你,可以有所收获. measure  step 1  *.WXML: ...

  3. JSON基础知识点

    一.介绍: JSON是一种轻量级的数据交换格式.易于人阅读和编写.同时也易于机器解析和生成. 二.数据格式: 1.JSON建构于两种数据格式: “名称/值”对(键值对)的集合,不同的语言中,它被理解为 ...

  4. .net工具基础

    MSIL Disassembler(ildasm.exe) -- 将C#程序或类库反汇编处理,显示C#编译器生成的CIL代码 C:\Program Files\Microsoft SDKs\Windo ...

  5. CENTOS7错误:Cannot find a valid baseurl for repo: base/7/x86_6

    CENTOS7错误:Cannot find a valid baseurl for repo: base/7/x86_6 解决办法: 1.进入/etc/sysconfig/network-script ...

  6. WebClient图片下载

    使用WebClient下载文件非常方便,针对有部分网站通过请求头的Referer,做了图片防盗链,可以在webClient加上Referer 来模拟请求 string basePath = Path. ...

  7. lua table排序报错与解决

    lua table排序 table的sort函数 比如按照大小进行排序,下面这种写法在某些情况下可能会排序错误,甚至报invalid order function for sorting table. ...

  8. 多个iframe中根据src获取特定iframe并执行操作

    多个iframe中根据src获取特定iframe并执行操作 前言:在项目中做一个批量编辑工单时需要在一大堆的iframe中的某一个iframe里边再用模态框的形式显示编辑区域,然后再在模态框里边加入i ...

  9. [Hive_7] Hive 中的 DDL 操作

    0. 说明 DDL(Data Definition Languages)语句:数据定义语言 这些语句定义了不同的数据段.数据库.表.列.索引等数据库对象的定义. 常用的语句关键字主要包括 create ...

  10. C# json、xml序列化

    json序列化: public static string SerializeJson<T>(T json) { // 1. 需要添加引用 Newtonsoft.Json.dll,默认为 ...