redis协议与异步方式学习笔记

news2024/11/13 12:02:17

目录

  • 1 交互方式 pipline
  • 2 广播机制
    • 2.1 概念演示
    • 2.2 使用场景
  • 3 redis事物
    • 3.1 概念
    • 3.2 使用场景
    • 3.3 解决的问题
      • 3.3.1 背景:多线程竞争出现问题
      • 3.3.2 事务
      • 3.3.3 安全性事务
    • 3.4两种类型的“事务”
      • 3.4.1 watch ... multi exec
      • 3.4.2 lua 脚本实现“原子”执行,重点掌握
      • 3.4.3 watch multi exec 与 lua 脚本 的区别
  • 4 redis联通,通过hiredis压缩协议,解析协议,扩展事件处理
    • 4.1 hiredis 安装
    • 4.2 hiredis : 如何引入自己项目中
      • 1 局限性: 一定要使用reactor模型。因为在hiredis中,具体的IO操作是hiredis实现的,
      • 2 项目中要访问redis
        • 2.1 \redis\hiredis\examples example-libevent.c
        • 2.2 \redis\hiredis\adapters libevent.h 适配libevent网络库
        • 2.3 流程备注

1 交互方式 pipline

pipeline  一次性发送多个命令,可以节约时间 (相对于客户端一次发一个命令,然后服务端回复一条命令)

在这里插入图片描述

异步连接 不需要规划, 带一个回调函数
时间窗口限流 ------------>  测试代码 github 地址 找mark 老师要
客户端缓存,然后一起发出(这里不是redis缓存)

2 广播机制

2.1 概念演示

发布者与注册者之间是1:n的关系,类似于观察者模式。
下图中两个客户端监听 “n1”, 第三个客户端向"n1"发布消息:
在这里插入图片描述
还可以是监听 n1.*, 用命令psubscibe
在这里插入图片描述
实际使用redis的时候,会与redis server建立两条连接,一条专门订阅(一直处于等待状态,相当于阻塞了),另一条用来处理种命令。

2.2 使用场景

缺陷:不保证消息一定到达(比如节点宕机),限制了发布订阅的使用场景
哪些场景可以允许丢失?-----用发布订阅模式
比如:游戏服务端发公告

反之:用kafka: 分布式消息队列: 能确保消息一定到达 (kafka 是工业级的,后续再写学习心得)
redis: stream 也可以,但是不推荐

3 redis事物

3.1 概念

用户定义一系列数据库操作,这些操作视为一个完整的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。
工作中没有使用过很复杂的数据库场景,从而就用不到事物

3.2 使用场景

在什么场景下探讨事物? ----> 并发连接的场景:多条连接处理相同的功能

单条连接,多条连接处理不同的功能: 不需要考虑事物

3.3 解决的问题

3.3.1 背景:多线程竞争出现问题

		client1:  1  get count : 1
				      2    set count  2  
		client2:  也要同时去操作count
					  3   set count 3 
		eg1 不希望 eg2影响它
		希望的结果是 3 12  或者 12 3, 而不是 1 3 2。
		1和2要作为一个整体执行,由此可以联想到 C/C++ 的原子性,但是redis中对应的是“事务”,事务可以将12绑定在一起。

3.3.2 事务

为解决并发连接互相干扰的问题,“事务”来了
事务是整体同生共死,假如有ABCD四个节点与服务器连接,每一个节点中都可能有事物,其中任意一个节点都有可能宕机,为了保证事物的安全性,怎么办?==> 安全性事务。

3.3.3 安全性事务

安全性事务具备 ACID特性:(引用3.3.1中的 命令1 2 3 )

A  原子性:要么都成功,要么都失败。
	   遇到失败了,需要回退到执行之前的状态。
C 完整检测一致:key 已经是一个string类型,不能当作另一个类型(如list, set)去操作。
	逻辑上的一致   1  3  2 就破坏了逻辑性(相对于 3 12  或者 12 3)。
I  隔离性:client1 client2应该隔离,需要加锁,串行性执行。
	锁事务(原子变量是 锁总线)。
	后续:mysql: MVCC 非一致锁定夺。
D 持久性:数据刷到磁盘。

lua脚本满足原子性和隔离性,一致性和持久性不满足

3.4两种类型的“事务”

3.4.1 watch … multi exec

语法:

watch  
语句 
multi  #开启事务
n个语句  
# 告诉redis视为一个整体(n个语句依次入队,这里是redis服务端的行为,与pipeline不一样),保证里面的key不会被修改,否则不提交事务,直接返回nil
 exec   # 提交事务  redis服务端,依次执行n个语句的队列,因为是服务端的行为,所以可以控制不会被打断

eg:

WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC

说明:

multi  与 exec之间  :  redis 将事物进行缓存 (acid)	, 然后通过缓存命令统一执行。
用的就是 redis pipeline 技术: 客户端缓存命令统一发送。
但这些可以通过lua脚本去执行,因为redis中含有lua虚拟机,lua可以实时的拿到redis内存数据,引出下一节。

3.4.2 lua 脚本实现“原子”执行,重点掌握

redis 中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redislua 脚本的执行是原子性的;当某个脚本正在执行的时候,不会有其他命令或者脚本被执行;
lua 脚本当中的命令会直接修改数据状态;
lua 脚本 mysql 存储区别:MySQL存储过程不具备事务性,所以也不具备原子性;
注意:如果项目中使用了 lua 脚本,不需要使用上面的事务命令;
eg: eval后面的就是lua脚本

27.0.0.1:6379> set score:10001 100
OK
127.0.0.1:6379> eval 'local key = KEYS[1]; local val = redis.call("get", key); redis.call("set", key, 2*val); return 2*val;' 1  score:10001
(integer) 200
127.0.0.1:6379> eval 'local key = KEYS[1]; local val = redis.call("get", key); redis.call("set", key, 2*val); return 2*val;' 1  score:10001
(integer) 400
127.0.0.1:6379> eval 'local key = KEYS[1]; local val = redis.call("get", key); redis.call("set", key, 2*val); return 2*val;' 1  score:10001
(integer) 800

语法:EVAL script numkeys key [key ...] arg [arg ...]
numkeys 对应上面例子中的1,KEYS[1]表示 score:10001, 可选参数arg没有使用到。
redis.call 相当于调用redis命令
EVAL的第二个参数是参数的个数,后面的参数(从第三个参数),表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。

3.4.3 watch multi exec 与 lua 脚本 的区别

1  multi exec 中间的事物语句拿不到结果
语句之间不能构成依赖逻辑关系   ===> 工作中几乎不会用到

2  相对的 lua都可以 。但是redis 没有回滚机制, lua脚本中有一部分语句执行成功,已经生效,后面的语句报错,与3.3.3.A里面提到的“遇到失败了,需要回退到执行之前的状态”不符。 
要实现完整的原子性,lua需要自己加代码,在报错的地方加上类似 if err 写代码回退 ,回滚到事物之前的状态。
eg: 
	local res, err = redis.pcall(...)
	if (err) {...自己写回滚的代码}

4 redis联通,通过hiredis压缩协议,解析协议,扩展事件处理

4.1 hiredis 安装

进入到 redis/deps/hiredis

make
sudo make install

4.2 hiredis : 如何引入自己项目中

1 局限性: 一定要使用reactor模型。因为在hiredis中,具体的IO操作是hiredis实现的,

也就说读数据read, 写数据write, 建立连接connect都是在hiredis中完成.
不可以使用proactor网络模型是因为 read write connect等操作都是在再proactor中完成,不是在hiredis中完成。

2 项目中要访问redis

参考

2.1 \redis\hiredis\examples example-libevent.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <hiredis.h>
#include <async.h>
#include <adapters/libevent.h>

void getCallback(redisAsyncContext *c, void *r, void *privdata) {
    redisReply *reply = r;
    if (reply == NULL) {
        if (c->errstr) {
            printf("errstr: %s\n", c->errstr);
        }
        return;
    }
    printf("argv[%s]: %s\n", (char*)privdata, reply->str);

    /* Disconnect after receiving the reply to GET */
    redisAsyncDisconnect(c);
}

void connectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Connected...\n");
}

void disconnectCallback(const redisAsyncContext *c, int status) {
    if (status != REDIS_OK) {
        printf("Error: %s\n", c->errstr);
        return;
    }
    printf("Disconnected...\n");
}

int main (int argc, char **argv) {
#ifndef _WIN32
    signal(SIGPIPE, SIG_IGN);
#endif

    struct event_base *base = event_base_new();
    redisOptions options = {0};
    REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
    struct timeval tv = {0};
    tv.tv_sec = 1;
    options.connect_timeout = &tv;


    redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
    if (c->err) {
        /* Let *c leak for now... */
        printf("Error: %s\n", c->errstr);
        return 1;
    }

    redisLibeventAttach(c,base);
    redisAsyncSetConnectCallback(c,connectCallback);
    redisAsyncSetDisconnectCallback(c,disconnectCallback);
    redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
    redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
    event_base_dispatch(base);
    return 0;
}

2.2 \redis\hiredis\adapters libevent.h 适配libevent网络库

 typedef struct redisLibeventEvents {
    redisAsyncContext *context;
    struct event *ev;
    struct event_base *base;
    struct timeval tv;
    short flags;
    short state;
} redisLibeventEvents;

。。。

2.3 流程备注

1 客户端:与redis建立连接,把指令压缩成redis协议发给redis服务器;
2 redis服务器: 解析执行,返回结果(也要压缩成redis协议的数据)。
3 客户端:再把 redis协议数据解析出来
客户端与服务端的连接 通过 hiredis 来管理,包含:
a 压缩协议
b 解析协议
c 扩展: 事件处理,可以用来适配reactor模型
reactor 回顾:
1 事件对象
2 注册事件
3 事件循环
4 触发事件 —> 处理 IO
参考\redis\hiredis\adapters libevent.h适配libevent网络库,关键部分如下:

static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
    redisLibeventEvents *e = (redisLibeventEvents *)privdata;
    const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;

    if (isRemove) {
        if ((e->flags & flag) == 0) {
            return;
        } else {
            e->flags &= ~flag;
        }
    } else {
        if (e->flags & flag) {
            return;
        } else {
            e->flags |= flag;
        }
    }

    event_del(e->ev);
    event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
                 redisLibeventHandler, privdata);
    event_add(e->ev, tv);
}

static void redisLibeventAddRead(void *privdata) {
    redisLibeventUpdate(privdata, EV_READ, 0);
}

static void redisLibeventDelRead(void *privdata) {
    redisLibeventUpdate(privdata, EV_READ, 1);
}

static void redisLibeventAddWrite(void *privdata) {
    redisLibeventUpdate(privdata, EV_WRITE, 0);
}

static void redisLibeventDelWrite(void *privdata) {
    redisLibeventUpdate(privdata, EV_WRITE, 1);
}
 /* Register functions to start/stop listening for events */
    ac->ev.addRead = redisLibeventAddRead;
    ac->ev.delRead = redisLibeventDelRead;
    ac->ev.addWrite = redisLibeventAddWrite;
    ac->ev.delWrite = redisLibeventDelWrite;
    ac->ev.cleanup = redisLibeventCleanup;
    ac->ev.scheduleTimer = redisLibeventSetTimeout;

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

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

相关文章

2023-01-11 LightDB高可用常用操作-管理.md

LightDB-高可用常用操作-管理篇 安装环境 操作系统&#xff1a;centos7 服务器IP:1.192.168.121.112 (主)2.192.168.121.113 (从)3.192.168.121.114 (哨兵-可选) 主库重启操作 1.先停止备库的keepalived,在root用户下执行 # 1.获得备库keepalived进 程pid [rootlocalhost ~]#…

闪亮登场!在树莓派上点亮LED灯的简单详细方法

文章目录 树莓派开发与STM32开发的比较原理图以及树莓派引脚展示点灯步骤读取树莓派布局 树莓派开发与STM32开发的比较 树莓派和STM32都是常用的嵌入式设备&#xff0c;都可以使用GPIO来控制LED灯。它们的点灯方式和使用的编程语言以及开发环境略有不同: 相同点&#xff1a; 控…

第五节 Hacker 登录界面

登录框用户界面 1. Entry 输入框 Tkinter中的Entry组件是一个单行文本输入框&#xff0c;用于接收用户在GUI应用程序中输入的信息。它可以被设置为只读或可编辑状态&#xff0c;可以设置输入的文本格式及长度限制等。当用户输入完成后&#xff0c;可以通过调用Entry组件的get(…

软件工程——第3章需求分析知识点整理

本专栏是博主个人笔记&#xff0c;主要目的是利用碎片化的时间来记忆软工知识点&#xff0c;特此声明&#xff01; 文章目录 1.需求分析的基本任务&#xff1f; 2.在需求分析阶段结束前&#xff0c;系统分析员应该做什么&#xff1f; 3.对软件系统有哪些综合要求&#xff1f…

基于Java农家乐信息平台设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

渣土车密闭运输识别算法 yolov7

渣土车密闭运输识别系统通过pythonyolov7网络模型技术&#xff0c;渣土车密闭运输识别算法对渣土车的密闭运输情况进行实时监测&#xff0c;检测到有未密闭的渣土车进入工地区域或者行驶在道路上时&#xff0c;渣土车密闭运输识别算法将自动发出警报提示现场管理人员及时采取措…

【openGauss数据库安全策略配置】--略有小成

【openGauss数据库安全策略配置】--略有小成 &#x1f53b; 一、openGauss数据库安全策略&#x1f530; 1.1 账户安全策略⛳ 1.1.1 自动锁定和解锁帐户&#x1f4a7; 1.1.1.1 配置failed_login_attempts参数&#x1f4a7; 1.1.1.2 配置password_lock_time参数 ⛳ 1.1.2 手动锁定…

MySQL实战解析底层---count(*)这么慢,该怎么办

目录 前言 count(*)的实现方式 用缓存系统保存计数 在数据库保存计数 不同的count用法 前言 在开发系统的时候&#xff0c;你可能经常需要计算一个表的行数&#xff0c;比如一个交易系统的所有变更记录总数这时候你可能会想&#xff0c;一条select count(*) fromt 语句不就…

Spring Boot 如何使用 @ExceptionHandler 注解处理异常消息

Spring Boot 如何使用 ExceptionHandler 注解处理异常消息 在 Spring Boot 应用程序中&#xff0c;异常处理是非常重要的一部分。当应用程序出现异常时&#xff0c;我们需要能够捕获和处理这些异常&#xff0c;并向用户提供有用的错误消息。在 Spring Boot 中&#xff0c;可以…

规则引擎--规则逻辑形如“1 (2 | 3)“的抽象

目录 规则下逻辑表达和条件的抽象表达逻辑的编码和抽象 规则&规则集合条件操作符规则规则执行表达式遍历进行操作符计算添加具体条件的执行 规则执行完成后得到最后的结果 规则下逻辑表达和条件的抽象 对于任何一个规则&#xff0c;包括多个条件&#xff0c;都可以抽象成如…

抽象确实JavaScript

看完上篇的添加事件&#xff0c;我想肯定有一万个黑马在奔腾 明明是照着添加宾语的公式来的&#xff0c;为什么会有报错&#xff1f; 事件不是说可以随便设置吗&#xff1f;但是会出问题 this又是什么关键词&#xff1f; value是啥&#xff1f; 围绕这三大疑问&#xff0c…

LED显示产业如何突破芯片短板

LED显示产业在突破芯片短板方面可以采取以下措施&#xff1a; 研发先进的芯片技术&#xff1a;LED显示芯片的研发是关键。通过投入更多资源和资金&#xff0c;研究机构和企业可以致力于开发更先进、更高效的LED显示芯片技术。这包括改进光电转换效率、提高亮度和色彩表现力等方…

Python基础合集 练习25 (正则表达式)

[0123456789] 普通字符 [0-9]简洁写法 在正则表达式中所有字符类型都有对应的编码 在匹配大写英文字母时,应该是 “”" [a-zA-Z]或[A-Za-z] “”" 元字符… … (.[0-9]{1,3}){3}进行重复三次操作 ^\d{9}$ 使用^和$匹配开始和结束位置,\d表示匹配数字,{9}表示…

【C++篇】C++的动态分配内存

友情链接&#xff1a;C/C系列系统学习目录 知识点内容正确性以C Primer&#xff08;中文版第五版&#xff09;、C Primer Plus&#xff08;中文版第六版&#xff09;为标准&#xff0c;同时参考其它各类书籍、优质文章等&#xff0c;总结归纳出个人认为较有逻辑的整体框架&…

处理 Python 3.11 弃用的 PySys_SetPath 和 Py_SetProgramName

在C调用matplotlibcpp.h画图时报错&#xff0c;使用的python版本是3.11.3版本。 解决方案&#xff1a;不重要的话&#xff0c;注释该行代码即可。 Python 3.11 弃用 PySys_SetPath 和 Py_SetProgramName。这 PyConfig API 取代了这些功能和其他功能。此提交使用 PyConfig API …

深入理解深度学习——GPT(Generative Pre-Trained Transformer):基础知识

分类目录&#xff1a;《深入理解深度学习》总目录 《深入理解深度学习——Transformer》系列文章介绍了Transformer&#xff0c;该模型最初被用于机器翻译任务&#xff0c;其出色表现引起了学术界的极大兴趣&#xff0c;其优异的特征提取与语义抽象能力得到了学者的广泛认可&am…

企业做seo有什么好处?SEO 为您的企业带来的 10 大主要优势?

如果您希望建立长期的品牌知名度、扩大目标受众并赚取更多收入&#xff0c;那么搜索引擎优化至关重要。让我们看看 SEO 为您的企业带来的 10 大好处&#xff0c;以及如何实现它们。 1. 它提高了你的可信度 在搜索引擎结果页面上排名靠前的网站通常被搜索引擎认为是高质量和值得…

【unity细节】—怎么将unity编译时和运行时的功能隔开

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐怎么将unity编译时和运行时的功能隔开的问题⭐ 文章目录 ⭐怎么将unity编译时和…

TCP的十个核心机制

目录 前言一 到 三四. 滑动窗口五. 流量控制六. 拥塞控制七. 延时应答八. 捎带应答九. 面向字节流十. 异常处理总结 前言 TCP协议是传输层的重点协议, 负责将数据从发送端传输到接收端. TCP协议是传输控制协议, 顾名思义也就是对数据的传输进行控制的协议. TCP 协议有很多, 我…

android存储3--初始化.unlock事件的处理

android版本&#xff1a;android-11.0.0_r21http://aospxref.com/android-11.0.0_r21 概述&#xff1a;收到unlock事件后&#xff0c;StorageSessionController、vold、storaged模块进行各自初始化操作。 一、StorageManagerService::onUnlockUser处理unlock事件 设备解锁后…