从RESP的角度理解事务和管道

news2025/4/24 16:44:48

1. RESP 是什么?

16 进制 0d 0a 就是 \r\n

RESP 就是 Redis 服务端和客户端之间进行通信的协议,它是建立在 TCP 之上的一种简单的应用层协议。你可以把它理解成 HTTP 协议,不过它更加的简单。

它支持很多数据类型,这里列举几个常用的:

  • Simple Strings 简单字符串
  • Bulk Strings 批量字符串
  • Arrays 数组

简单字符串是格式是:以 + 开始,接着是字符串(不允许含有 \r 或者 \n),结尾是 \r\n。它用于传输短的,极小开销的非二进制字符串。例如:+OK\r\n,这个通常是用于服务端基于客户端的响应。如下图所示:

在这里插入图片描述

批量字符串的格式是:以 $ 开始,接着是字符串的长度,\r\n 分隔,字符串本身,\r\n 结尾。例如:$5\r\nhello\r\n,获取上次设置的键值:

在这里插入图片描述

这是抓包获取的结果,右下角红色框选的即为服务器的响应的解码形式(左侧为16进制形式)。你在程序层面看到的是 CrazyDragon,但是在网络层面它是这样的形势:$11\r\nCrazyDragon\r\n

在这里插入图片描述

数组的格式是:以 * 开头,接着是数组元素的个数,\r\n 分隔,然后是数组的每一个元素。例如:*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n 表示 "hello""world" 两个字符串。现在我们用批量命令 mget 获取两个 author:001author:002 的值。

在这里插入图片描述

同样的抓包分析它,可以看到返回的即是 Arrays 格式的数据,把它写成这样的形式就更加明了了:*2\r\n$11\r\nCrazyDragon\r\n$3\r\nTom\r\n,只不过它不显示转义符。

在这里插入图片描述

这里只是简单介绍一下 RESP,具体的规范请直接查看官方文档,并自己抓包分析:

Redis serialization protocol specification

2. Redis 的管道和事务

在 Redis 中,实现批量操作可以通过管道或者事务来实现,它的主要区别是:

  1. Redis 的 pipeline 是客户端行为,对于服务器来说是透明的。它就是当客户端需要执行多条 redis 命令时,通过管道一次性将要执行的多条命令发送给服务端。
  2. Reeis 的事务是服务器行为,它是通过 MULTI EXEC WATCH DISCARD 来完成的。当用户输入 MULTI,接下来输入的指令,会被服务端存储起来,最后当作一个原子性的指令执行。

刚才在第一部分,我们已经知道了它们之间的通信协议。所以,我们就可以从网络的层次来了解这个过程,因为之前这些区别都是从文本获取的,大多数人只是机械的记忆了区别,却没有真正的看过它们的区别。既然我们可以通过网络抓包来分析,那么就来做一些有意义的事情吧!所以,我们就来从 RESP 的角度来看一下它们的区别。

2.1 安装软件

既然需要查看 Redis 的网络通信,那么自然是需要一个 Redis 了。这里我是下载的 github 上面的一个 windows 便携版。这个版本使用起来很方便,只是测试的话,不需要去启动虚拟机,然后再启动 docker 容器了。然后还需要一个抓包软件,这里推荐使用 WireShark,在官网下载安装即可。

在这里插入图片描述

在这里插入图片描述

2.2 抓包准备工作

先启动 redis 服务端,再启动一个客户端并连接,然后启动 wireshark 并设置好过滤条件即可。

启动 Redis 服务器:

在这里插入图片描述

启动 Redis 客户端,并插入三条数据:

在这里插入图片描述

启动 WireShark,选择 loopback 适配器(因为这里用的是 127.0.0.1 loopback 地址):

在这里插入图片描述

然后进入抓包页面,这里会实时显示已经获取的包,但是它会显示所有经过 loopback 的包(各种协议和端口),显然我们是不需要那么多的,太多了也会形成干扰,所以要过滤一下:tcp.port == 6379

在这里插入图片描述

进行端口过滤之后,这个页面就没有数据了。因为这时还没有网络通信,所以这时没有捕获的网络数据包:

在这里插入图片描述

2.3 开始抓包

2.3.1 执行事务的网络数据包

这里通过开启一个事务来获取数据,在事务中执行三次 get 指令,最后执行整个事务,这里的演示比较简单(TX 即是 Transaction 的简写)。

在这里插入图片描述

这里我们不关注这个结果,我们关注的是它执行的过程,直接看网络抓包的结果,下图即是上面整个事务从开始到执行成功的网络包。从协议列(protocol),可以看出来 RESP 是基于 TCP 的,所以每一个 RESP 之前都需要 TCP 的三次握手建立连接。

注:这里不得不说一句:WireShark 真是一个伟大的软件!它是直接识别了 RESP 了。所以,多了解一些东西,原来透明i的概念就会显现出来了。

在这里插入图片描述

我们这里只需要重点关注应用层的 RESP 即可,所以传输层的 TCP 报文也过滤掉:tcp.port == 6379 and resp

在这里插入图片描述

这样再看这个协议的交互就清晰多了,它还是很常见的 Request-Response 模式。我们来看一个 Request 和一个 Response。

在这里插入图片描述
在这里插入图片描述

把数据给复制出来(选择以可打印的格式):

在这里插入图片描述

下面两个分别是请求报文和响应报文(不是同一个请求和响应报文),因为换行符 \r\n 不可见,所以我给它手动补上了(你复制下来不是转移字符)。

*2\r\n$3\r\nget\r\n$10\r\nauthor:002\r\n
*3\r\n$11\r\nCrazyDragon\r\n$3\r\nTom\r\n$5\r\nPeter\r\n

前面我们已经简单了解了 RESP,这里大家看到这个报文应该就能知道它的意思了。

2.3.2 执行管道的网络数据包

老实说,只是使用管道和事务的话,是很难了解它们的区别。

在这里插入图片描述

直接看抓包的信息,这里可以看出,通过管道执行,它就是将命令按照 RESP 的格式给拼接起来,然后直接发送出去了,响应也是每一条命令的执行结果以及最终返回的数据。

在这里插入图片描述

不过这里有点不对劲,因为管道命令里面也是事务。我去看了一下,是因为 Python 的 redis 库默认的管道命令是原子性的。不过,这里并不需要,我们给它关掉,重新抓一个包吧。

在这里插入图片描述

在这里插入图片描述
这样就是最传统的非原子性的管道了,下面是它的报文(这里是直接复制的报文,我就不把 \r\n 打出来了,不过你应该知道的),可以看出来它们就是简单的命令拼接。

*2
$3
GET
$10
author:001
*2
$3
GET
$10
author:002
*2
$3
GET
$10
author:003

3. 总结

通过网络抓包分析,我们可以清晰的看出来。事务的每一条指令都会进行一次网络请求,所以在 QUEUED 阶段失败了,整个事务就失败了,因为服务端是可以感知的,成功了它才会发送 QUEUED 指令。而管道呢,则是把若干条指令拼接起来一次性发送,它最大的作用是节省了多次建立连接所需要的时间(不要小看了每次建立断开连接是开销,累计起来是很庞大的!)。

4. 额外的内容

前面我们简单了解了 RESP,以及在此基础上去观察事务和管道命令在执行上面的区别。那么我们还能用它来做什么呢?来整一个活!

代码示例:

package main

import (
	"fmt"
	"net"
	"strings"
)

func main() {
	DragonRedisClient()
}

func DragonRedisClient() {

	// 连接到 TCP 的 6379 端口
	conn, err := net.Dial("tcp", "127.0.0.1:6379")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()

	// 执行命令:set love "I love you yesterday and today."
	var builder strings.Builder
	builder.WriteString("*3") // 3 个字符串
	builder.WriteString("\r\n")
	builder.WriteString("$3") // set 字符串长度
	builder.WriteString("\r\n")
	builder.WriteString("set") // set
	builder.WriteString("\r\n")
	builder.WriteString("$4") // love 字符串长度
	builder.WriteString("\r\n")
	builder.WriteString("love") // love 键名
	builder.WriteString("\r\n")
	builder.WriteString("$31") // love 值内容长度
	builder.WriteString("\r\n")
	builder.WriteString(`I love you yesterday and today.`) // love 值内容
	builder.WriteString("\r\n")

	// 发送请求报文
	_, err = conn.Write([]byte(builder.String()))
	if err != nil {
		fmt.Println(err)
		return
	}

	// 读取响应报文
	resp := make([]byte, 20)
	n, err := conn.Read(resp)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(resp[:n]))
}

执行结果:

在这里插入图片描述

网络数据包(前面搞错了长度,导致请求一直解析失败,哈哈,最下面才是成功的。):

在这里插入图片描述

在终端查看命令,第一个 get love 是在未执行程序前,第二个是在执行程序后:

在这里插入图片描述

所以,你说这算是个什么东西?当然了,这里是非常简陋的一个代码,哈哈。

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

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

相关文章

企业数字化转型的关键技术有哪些?_光点科技

随着科技的不断进步和信息技术的快速发展,企业数字化转型已经成为保持竞争力和适应市场变化的关键举措。在这个数字化时代,企业需要借助先进的技术来优化业务流程、提升效率,以及更好地满足客户需求。以下是企业数字化转型过程中的关键技术。…

4V-28V Vin,6A同步降压DCDC变换器,集成3.3V和150mA LDO——SCT2361FPBR

SCT2361是一种高效率的同步降压型DC-DC变换器,集成3.3V和150mA LDO。输入电压范围为4V-28V,输出电压可调为0.6V,具有3mmx3mm的小QFN封装,可提供连续6A的输出电流。该器件将高、低压侧功率mosfet集成,使导通损耗降到最低…

某次护网红队getshell的经历

信息收集 某企业提供信息:企业官网的真实外网ip,内网ip 企业官网比较硬,从控股超过51%的子公司入手 通过企查查找到一堆控股高的子公司,通过ICP/IP地址/域名信息备案管理系统查找子公司官网,收集二级域名。通过google…

Linux 调试技术 Kprobe

目录 用途:一、技术背景1.1 kprobes的特点与使用限制1.2 kprobe原理 二、 基于kprobe探测模块的探测方式2.1、struct kprobe结构体2.2 kprobe API函数2.3 示例代码参考资料: 用途: 判断内核函数是否被调用,获取调用上下文、入参以…

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段

『SpringBoot 源码分析』run() 方法执行流程:(2)刷新应用上下文-准备阶段 基于 2.2.9.RELEASE问题:当方法进行了注释标记之后,springboot 又是怎么注入到容器中并创建类呢? 首先创建测试主程序 package …

第49节:cesium 倾斜模型osgb转3dtiles,并加载(含源码+视频)

结果示例: 完整步骤: 1、启动并登陆cesiumlab 2、准备OSGB模型数据(含下载地址) 链接:https://pan.quark.cn/s/46ac7b0b2bed 提取码:TvWL3、倾斜模型切片 选择倾斜模型data文件夹 空间参考、零点坐标 默认 强制双面关闭、无光照 打开

天津和则百顺国际贸易有限公司被选为中国自主创新企业

在2023年4月,天津和则百顺国际贸易有限公司凭借在全国自主创新企业宣传推广活动中的出色表现,经过相关单位审核,正式被评选为中国自主创新企业,并荣获《中国自主创新企业》荣誉证书。 作为始终走在中国自主创新前沿的企业,天津和则百顺国际贸易有限公司,以下简称和则百顺,对于获…

console.log封装,显示调用的位置

背景 一般我们都哦说直接调用console.log,但是有时候console.log太多了,非常影响效率,我们想统一开启console.log(不是生产环境移除console.log) 可能封装的代码是这样 window.mylog()>conosle.log但是控制面板哪…

【大数据】Flink 详解(六):源码篇 Ⅰ

Flink 详解(六):源码篇 Ⅰ 55、Flink 作业的提交流程?56、Flink 作业提交分为几种方式?57、Flink JobGraph 是在什么时候生成的?58、那在 JobGraph 提交集群之前都经历哪些过程?59、看你提到 Pi…

命令执行漏洞复现攻击:识别威胁并加强安全

环境准备 这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。 一、攻击相关介绍 原理 主要是输入验证不严格、代码逻辑错误、应用程序或系统中缺少安全机制等。攻击者可以通过构造特定的输入向应用程序或系统注入恶意代码&#xff…

sql:SQL优化知识点记录(七)

(1)索引优化5 (2)索引优化6 (3)索引优化7 查询*, 百分号加右边,否则索引会失效 没建立索引之前都是全表扫描 没建立索引 建立索引: 建立索引 id是主键,他也…

零信任安全模型详解:探讨零信任安全策略的原理、实施方法和最佳实践,确保在网络中实现最小特权原则

在当今日益复杂和危险的网络环境中,传统的网络安全模型已经不再能够满足对抗不断进化的威胁。零信任安全模型应运而生,以其强调“不信任,始终验证”的理念,成为了当今信息技术领域中的热门话题。本文将深入探讨零信任安全模型&…

(leetcode802,拓扑排序,深搜+三色标记)-------------------Java实现

(leetcode802,拓扑排序,深搜三色标记)找到最终的安全状态-------------------Java实现 题目表述 有一个有 n 个节点的有向图,节点按 0 到 n - 1 编号。图由一个 索引从 0 开始 的 2D 整数数组 graph表示,…

【RPC 协议】序列化与反序列化 | lua-cjson | lua-protobuf

文章目录 RPC 协议gRPCJSON-RPC 数据序列化与反序列化lua-cjsonlua-protobuf RPC 协议 在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调…

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录

Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 目录 Unity Android 之 在Unity 中引入 OkHttp的操作注意(OKHttp4.xx- kotlin 的包)简单记录 一、简单介绍 二、OKHttp 4.xx 的 SDK 封装 aar 给 Unity 的使用注意 三、附录 OKHttp 的…

一米ip流量池系统

PC端快速切换移动网络IP 支持全网通sim卡槽,国内三大运营商IP池动态切换,实现真实移动端IP切换。从此换IP再也不用vpn或代理,一个设备搞定 1.兼容国内电信,移动,联通三网通的sim卡4G连接,快速稳定2.可直接…

《算法竞赛·快冲300题》每日一题:“简化农场”

《算法竞赛快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 简…

SpringBoot自定义消息总线

一、前言 在现代的分布式系统中,消息传递已成为一个非常流行的模式。它使得系统内的不同部分可以松耦合地通信,从而实现更高效、更可靠的应用程序。本博客将介绍SpringBoot如何提供简单易用的消息传递机制,并展示如何自定义消息总线以满足特定…

安装使用 d3graph 时出现 TypeError 的解决方法

使用 python 3.7 pip 22.3.1 在清华镜像源 https://pypi.tuna.tsinghua.edu.cn/simple 安装 d3blocks 1.3.2 时,安装成功后导入包时出错: 观察报错信息可以看到出错的代码(902 行)使用了类型指定语法,这是最新的 pyth…

stable diffusion实践操作-电脑硬件查看

本文专门开一节写电脑硬件相关的内容,在看之前,可以同步关注: stable diffusion实践操作 正文 1、检查电脑显存的方法(win10): 鼠标放在工具栏,单击右键打开“任务管理器”,选择顶…