源码篇--Redis 通信协议

news2024/11/15 17:40:22

文章目录

  • 前言
  • 一、Redis 的通信过程:
  • 二、RESP 协议:
  • 三、客户端模拟RESP 通信:
  • 总结


前言

在我们知道redis 的网络模型后,继续看下 redis 的通信协议。


一、Redis 的通信过程:

Redis是一个CS架构的软件,通信一般分两步 (不包括pipeline和PubSub):

  • 客户端 (client) 向服务端 (server) 发送一条命令
  • 服务端解析并执行命令,返回响应结果给客户端
  • 因此客户端发送命令的格式、服务端响应结果的格式必须有一个规范,这个规范就是通信协议。

而在Redis中采用的是RESP (Redis Serialization Protocol) 协议:

  • Redis 1.2版本引入了RESP协议
  • Redis 2.0版本中成为与Redis服务端通信的标准,称为RESP2
  • Redis6.0版本中,从RESP2升级到了RESP3协议,增加了更多数据类型并且支持6.0的新特性–客户端缓存
    但目前,默认使用的依然是RESP2协议

二、RESP 协议:

客户端 (client) 向服务端 (server) 发送指令,服务端完成对应操作后返回结果 ,那么服务端怎么知道客户端传入过来的数据类型是什么呢,因为只有知道了数据类型,服务端才能去正确的包装Redisobject 对象 进行存储;

在RESP中,通过首字节的字符来区分不同数据类型,常用的数据类型包括5种:

  • 单行字符串::首字节是“+,后面跟上单行字符串,以CRLF(“\r\n”) 结尾。例如返回”OK”:“+OK\r\n”
    此种字符中不能包含 \r\n, 这个通常用于服务端返回消息,如 ping ->pong 和 执行客户端命令成果返回ok;
  • 错误(Errors):首字节是“-“,与单行字符串格式一样,只是字符串是异常信息,
    例如:”-Error message\r\n";
    只出现在 服务端接到客户端的命令,在执行命令过程中出现错误;
  • 数值:首字节是“:’,后面跟上数字格式的字符串,以CRLF结尾。例如:“:10\r\n”
  • 多行字符串:首字节是$’,表示二进制安全的字符串,最大支持512MB:
  • 数组:首字节是"*“,后面跟上数组元素个数,再跟上元素,元素数据类型不限:可以是字符串,数值,数组;事实上客户端在给服务端发送命令是 使用的就是这个格式:

这样Redis 就可以来发送各种各样的数据给服务端了,现在还有一个问题,服务端怎么知道 发送的命令是正确的读完了呢, 答案是客户端在发送命令之前给每中数据 都标记上 命令的长度:$ 跟上长度,如发送的数据是字符串:
在这里插入图片描述
对于服务端返回的数据结果也会将数据类型及长度进行标注,在服务端的返回 长度有如下含义:

  • 如果是正常代表字符串长度;
  • 如果是0 代表空字符串 “$0\r\n\r\n”
  • 如果是-1 在代表字符串不存在 “$-1\r\n\r\n”,如根据key没有找到值;

客户端在给服务端发送命令时使用的就是数组的格式
在这里插入图片描述

  • *3 表示数组中有3个元素;
  • $3 表示数据类型是多行字符串,长度是3个字节;
  • $4 表示数据类型是多行字符串,长度是4个字节;
  • $6 表示数据类型是多行字符串,长度是6个字节;
  • 最后服务端就可以解析出来正确的命令 set name 虎哥

三、客户端模拟RESP 通信:

package com.example.springredisms.mock.redis;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class RedicClient {
    static Socket s;
    static PrintWriter writer;
    static BufferedReader reader;

    public static void main(String[] args) {
        try {
        	// 和服务的建立socket 连接
            s = new Socket("localhost", 6379);
			// 获取写数据流
            writer = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), StandardCharsets.UTF_8));
			// 获取读数据流
            reader = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));

			// 密码认证命令
            sendRequest("auth", "123456");
            // 解析请求
            Object o1 = handleResponse();
            System.out.println("o1 = " + o1);

            sendRequest("set", "name", "张三");

            Object o2 = handleResponse();
            System.out.println("o2 = " + o2);

            sendRequest("get", "name");

            Object o3 = handleResponse();
            System.out.println("o3 = " + o3);

            sendRequest("SADD", "runoobkey1", "王五", "赵六", "李四");

            Object o4 = handleResponse();
            System.out.println("o4 = " + o4);


            sendRequest("SMEMBERS", "runoobkey1");

            Object o5 = handleResponse();
            System.out.println("o5 = " + o5);

        } catch (Exception ex) {
            try {
                if (null != reader) {
                    reader.close();
                }
                if (null != writer) {
                    writer.close();
                }
                if (null != s) {
                    s.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }



    private static Object handleResponse() throws IOException {
        Object o;
        int prefix = reader.read();
        switch (prefix) {
            case '+':
                o = reader.readLine();
                break;
            case '-':
                throw new RuntimeException(reader.readLine());
            case ':':
                o = Long.parseLong(reader.readLine());
                break;
            case '$':
                Integer len = Integer.parseInt(reader.readLine());
                if (-1 == len) {
                    o = null;
                } else if (0 == len) {
                    o = "";
                } else {
                    // 简化 :直接读一行(正常应该都len 字节)
                    o = reader.readLine();
                }
                break;
            case '*':
                Integer arrayLng = Integer.parseInt(reader.readLine());
                if (arrayLng <= 0) {
                    return null;
                }
                List<Object> list = new ArrayList<>(arrayLng);
                o = list;
                for (Integer i = 0; i < arrayLng; i++) {
                    list.add(handleResponse());
                }
                break;
            default:
                throw new RuntimeException("不合法的数据格式!");
        }
        return o;
    }

    // set name 张三
    private static void sendRequest(String... args) {
        // 数组元素的个数
        writer.println("*" + args.length);
        for (String arg : args) {
            // 命令的字节数
            writer.println("$" + arg.getBytes(StandardCharsets.UTF_8).length);
            // 命令
            writer.println(arg);
        }
        // 写出
        writer.flush();
    }
}

在这里插入图片描述


总结

Redis 使用 RESP 协议 进行客户端与服务端的通信,通过对数据进行不同的头部标识 来区分数据类型,通过长度来保证读取的完整性。

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

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

相关文章

[GXYCTF2019]禁止套娃(特详解)

刚打开页面什么都没有&#xff0c;抓包也什么都没有 那就dirsaerch扫一下&#xff0c;发现状态码都是429&#xff0c;访问太快了&#xff08;这里很多师傅都没有说明或者说清楚&#xff09; 这里改了一下线程&#xff08;kali自带的&#xff0c;如果用的脚本要加前面要加python…

【vue】图片加载骨架

一、前言 在网速较低或者网站的服务器宽带只有几MB的情况下&#xff0c;网页中的图片加载时&#xff0c;要么空白&#xff0c;要么像打印机一样一行一行地“扫描”出来&#xff0c;为了提升用户体验&#xff0c;可以给图片标签外加一层骨架。 无骨架 有骨架 二、详细设计 每张…

Mysql 删除数据

从数据表中删除数据使用DELETE语句&#xff0c;DELETE语句允许WHERE子句指定删除条件。DELETE语句基本语法格式如下&#xff1a; DELETE FROM table_name [WHERE <condition>]; table_name指定要执行删除操作的表&#xff1b;“[WHERE <condition>]”为可选参数&a…

宠物用品/宠物自动饮水机方案

宠物自动饮水机方案原理 宠物自动饮水机&#xff0c;也叫做智能宠物饮水机&#xff0c;是一种为宠物设计的智能化饮水器。应用核心主要在于智能化水泵控制&#xff0c;以及外围传感器电路。 宠物自动饮水机使用方便&#xff0c;不用频繁的换水。另外&#xff0c;自来水的水质可…

二阶魔方、四阶魔方 还原公式图文教程

二阶魔方 四阶魔方 在家无聊&#xff0c;翻东西的时候看到了以前买的4阶魔方&#xff0c;就随便在网上复制了一个4阶魔方的教程&#xff0c;以免忘记。 本文四阶魔方公式图解是针对四阶魔方教程的降阶法做更多的解释和说明。 四阶魔方被认为是2-5阶魔方玩法中最不好复原的,虽…

一文说清楚仿真与数字孪生的关系

获取更多资讯&#xff0c;赶快关注上面的公众号吧&#xff01; 文章目录 何为仿真何为数字孪生 最近看群里的小伙伴在疯狂讨论数字孪生&#xff0c;今天我也谈谈自己的理解。 之前还在北航读博的时候&#xff0c;北航陶飞教授已经算是数字孪生领域的领军人物&#xff0c;也专门…

Ubuntu 隐藏Telnet主机SSH服务时显示版本信息问题

一、背景 默认情况下&#xff0c;我们通过telnet服务器的22端口&#xff0c;能够获取OpenSSH服务的banner信息(如下图所示)。而低版本的OpenSSH存在许多高危漏洞。。为了安全我们要隐藏这个信息。 二、隐藏Telnet版本信息 当使用telnet命令&#xff0c;telnet 192.168.31.20…

SeaTunnel Web安装 一把成

安装相关jar包&#xff0c;以及SeaTunnel 和Web 打成的包&#xff0c;可以直接使用&#xff0c;但是需要安装MySQL客户端的分享&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1qrt1RAX38SgIpNklbQJ7pA 提取码&#xff1a;0kmf 1. 环境准备 环境名称版本系统环境C…

WPOpenSocial实现WordPress的QQ登录

个人建站不可避免的需要自己搭建用户数据库的问题&#xff0c;可用户却往往因为注册繁琐而放弃浏览您的网站&#xff0c;由此可见&#xff0c;一个社交账号一键登录方式尤为重要。选择适合您网站需求的社交插件&#xff0c;可以提升用户互动&#xff0c;增加社交分享&#xff0…

高端车规MCU的破局之路

目录 1 低质量的无效内卷 2 高端车规MCU产品共性 2.1 支持标定测量 2.2 低延迟通信加速 2.3 完备的网络安全解决方案 2.4虚拟化 3 国产替代的囚徒困境 1 低质量的无效内卷 近几年&#xff0c;车规MCU国产替代的呼声此消彼长&#xff0c;但仍然集中在低端产品。 从产…

后序遍历的线索化二叉树

对于后序遍历&#xff0c;需要明确&#xff0c;往往叶子结点&#xff0c;只能指向右子树&#xff08;如果右子树存在的情况&#xff09;&#xff0c;或者指向该结点&#xff08;因为这才是后序遍历&#xff09;&#xff0c;同样在进行退出到前一次递归的时候&#xff0c;我们要…

基于Redis的高可用分布式锁——RedLock

目录 RedLock简介 RedLock工作流程 获取锁 释放锁 RedLock简介 Redis作者提出来的高可用分布式锁由多个完全独立的Redis节点组成&#xff0c;注意是完全独立&#xff0c;而不是主从关系或者集群关系&#xff0c;并且一般是要求分开机器部署的利用分布式高可以系统中大多数存…

再学vue3的优势

vue3 对 vue2 有什么优势 性能更好&#xff08;编译优化、使用proxy等&#xff09;体积更小更好的TS支持更好的代码组织更好的逻辑抽离更多新功能 vue3 和 vue2 的生命周期有什么区别 Options API生命周期 beforeDestroy改为beforeUnmountdestroyed改为umounted其他沿用vue…

网络安全01--负载均衡

目录 一、环境准备 1.1三台虚拟机 二、开始搭建负载均衡&#xff1a; 2.1准备一下源 2.2正式安装 2.3Nginx安装情况 三、负载均衡--轮询&#xff08;round robin&#xff09; 3.1在 http 部分添加如下负载均衡配置&#xff1a; 3.2简单解释一下server端&#xff1a; …

vxe-table表格合并行和虚拟滚动冲突

项目一直用的vxe-table 2.0版本&#xff0c;支持表格的虚拟滚动&#xff0c;最近要做表格合并行功能&#xff0c;虚拟滚动便失效了&#xff0c;强行虚拟滚动&#xff0c;合并行会有错行现象。 vxe-table2.0给出的解释是&#xff1a;合并行不能和虚拟滚动一起使用。 目前找到两种…

代码之外的艺术:程序员的写作利器

在这个信息爆炸的时代&#xff0c;知识的管理和团队协作成了重要的技能。对于个人和企业来说&#xff0c;高效的文档和笔记服务平台是保持竞争力的关键工具。今天&#xff0c;让我们深入对比一下当前市场上流行的几款服务&#xff1a;石墨文档、腾讯文档、语雀、有道云笔记、No…

Linux:理解信号量以及内核中的三种通信方式

文章目录 共享内存的通信速度消息队列msggetmsgsndmsgrcvmsgctl 信号量semgetsemctl 内核看待ipc资源单独设计的模块ipc资源的维护 理解信号量总结 本篇主要是基于共享内存&#xff0c;延伸出对于消息队列和信号量&#xff0c;再从内核的角度去看这三个模块实现进程间通信 共享…

剖析Elasticsearch面试题:分词、倒排索引、文本相似度TF-IDF,揭秘分段存储与段合并,解密写索引技巧,应对深翻页问题的实用解决方案!

1、谈谈分词与倒排索引的原理 当谈到Elasticsearch时&#xff0c;分词与倒排索引是两个关键的概念&#xff0c;理解它们对于面试中展示对Elasticsearch工作原理的理解至关重要。 「1. 分词&#xff08;Tokenization&#xff09;&#xff1a;」 分词是将文本分解成一个个单独…

智能水肥一体化灌溉系统:提升农业生产效率的数字化解决方案

一、设备介绍(key-iot.com.cn)&#xff1a; 我们的星创易联设备是智能水肥一体化灌溉系统的核心组成部分。该设备由多个先进的传感器和执行器组成&#xff0c;可以对环境因素、土壤湿度和植物生长状态进行实时监测。其中包括&#xff1a; 1. 土壤湿度传感器&#xff1a;通过监…

电商API接口接入|电商爬虫实践附代码案例

1.爬虫是什么 首先应该弄明白一件事&#xff0c;就是什么是爬虫&#xff0c;为什么要爬虫&#xff0c;百度了一下&#xff0c;是这样解释的&#xff1a;网络爬虫&#xff08;又被称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追…