当前位置: 首页 > news >正文

Redis2——协议与异步方式

文章目录

  • Redis2——协议与异步方式
    • 1. Redis Pipeline
    • 2. Redis事务
      • 2.1 无锁事务控制(乐观事务控制)
      • 2.2 事务语句与lua脚本
      • 2.3 事务特性ACID
    • 3. 通信方式
      • 3.1 hiredis库
      • 3.2 同步连接
      • 3.3 异步连接
        • 3.3.1 hiredis管理监听事件接口
        • 3.3.2 hiredis + libevent
        • 3.3.3 hiredis + 自定义reactor
    • 学习参考

Redis2——协议与异步方式

本文讲述了Redis pipeline技术,它被用于一次发送和执行多个命令;事务的ACID特性,Redis只能部分满足;最后介绍了实现了Redis客户端的同步连接和异步连接方式。

1. Redis Pipeline

Redis Pipeline 是一种在 Redis 中批量执行命令的技术,用于减少客户端与 Redis 服务器之间通信的开销,从而提高性能。Redis Pipeline是一种客户端提供的技术。

python代码示例如下:

import redis# 创建 Redis 连接
r = redis.Redis(host='localhost', port=6379, db=0)# 使用 pipeline
pipe = r.pipeline()# 批量命令
pipe.set('foo', 'bar')
pipe.get('foo')
pipe.incr('counter')
pipe.mset({'name': 'Alice', 'age': 30})# 执行管道中的所有命令
responses = pipe.execute()# 打印响应
print(responses)

2. Redis事务

定义:事务是由用户定义的一系列操作,被视为一个整体,要么全部执行,要么都不执行,不可分割,其中间状态不能被访问(原子性)。

2.1 无锁事务控制(乐观事务控制)

Redis 的乐观事务主要基于其 WATCHMULTI/EXEC 机制,旨在实现无锁事务控制。通过监控键值变化避免事务冲突,适合高并发、轻量事务。

Redis对于数据的管理使用的是单线程模式,所有的命令会在同一个线程中执行,这使得redis中的大部分命令天然是原子操作,无序借助CAS实现。

  1. 监控键值 (WATCH)
    Redis 使用 WATCH 命令来监视一个或多个键。如果在事务执行之前,任何一个被监视的键发生了变化,事务将被中止。
  2. 事务定义 (MULTI)
    事务通过 MULTI 命令开始,所有后续命令被加入事务队列。
  3. 执行事务 (EXEC)
    当执行 EXEC 时,Redis 检查被监视的键是否被修改过。如果没有,事务中的命令会依次执行;如果有,事务会被放弃。
  4. 回滚机制(DISCARD)
    Redis 的乐观事务没有真正的回滚机制。如果某条命令出错,其他命令依然会执行。

乐观锁的概念

乐观锁是一种基于 “假设冲突较少” 的并发控制策略。它的核心思想是:在操作数据时假设不会发生并发冲突,仅在更新时检查是否有冲突,如果检测到冲突,则采取相应的处理(如重试或报错)。

这种锁机制并不会阻塞其他事务的访问,而是允许多个事务同时操作数据,但在最终提交时检查数据的一致性。

2.2 事务语句与lua脚本

开启事务

MULTI

提交事务

EXEC

回滚事务

DISCARD

监控键值变化

WATCH key

如果事务执行之前,被监控的key值发生了变化,就会导致事务执行失败。

例子如下:

WATCH account1 account2      # 监控两个键
val1 = GET account1          # 获取 account1 的余额
if val1 >= 100:              # 判断余额是否足够MULTI                    # 开始事务DECRBY account1 100      # 从 account1 扣减 100INCRBY account2 100      # 给 account2 增加 100EXEC                     # 提交事务
else:UNWATCH                  # 取消监控# 返回余额不足的错误

Lua脚本实现事务的原子性

执行lua脚本

Redis中内置了一个lua虚拟机。在工程实践中,大多数情况下是使用lua脚本来实现事务的原子性。

EVAL script numkeys key [key ...] [arg ...]

举个例子,将key值加倍并返回:

先定义一个lua脚本

local key = KEYS[1]; 
local val = redis.call("get", key); 
if not val thenval = 1000
end
redis.call("set", key, val * 2); 
return 2 * val;

执行lua脚本

eval 'local key ... val;' 1 val

缓存脚本

但是这样每次都发送完整的lua脚本会造成流量浪费,我们可以先使用script load将脚本发送给redis服务器,得到一个标识符,然后之后就可以发送标识符代替脚本。

发送脚本,获得标识符

script load <script string>

使用标识符执行脚本

evalsha <script sha code> numkeys key [key ...] [arg ...]

管理脚本标识符

在服务器启动时,先清空原有的脚本缓存

script flush

然后将所有脚本发送给redis并使用一个unordered_map进行缓存,从而可以随时取用。

2.3 事务特性ACID

  1. 原子性(Atomicity) 事务是一个不可分割的单位,要么全部执行,要么全都不执行,其它事务不能访问其中间状态。

    Redis事务具备原子性,但由于Redis不支持回滚,因此即使事务中某些操作执行失败,整个事务也会继续执行下去,直到执行完毕。

  2. 一致性(Consistency)包括数据库本身的完整性约束的一致性和用户定义的逻辑上的一致性。前者举例如类型约束、非空约束R、唯一约束、外键约束等,后者举例如银行转账事务前后,总金额应该保持不变。

    Redis不支持逻辑上的一致性,因为它允许事务中的部分操作执行失败。

  3. 隔离性(Isolation)并发事务之间的影响程度。由于Redis是单线程执行命令,因此天然具有隔离性。

  4. 持久性(Durability)事务的操作的结果是否会持久化到磁盘中,即使数据库重启或崩溃,事务的数据也可以被恢复。

    Redis是基于内存的数据库,其事务是否具备持久性取决于持久化策略和配置:

    • RDB(Redis Database)持久化:通过快照的方式定期将内存中的数据保存到磁盘。

    • AOF(Append-Only File)持久化:通过将每个写命令追加到日志文件中,来记录所有写操作。

    设置持久化策略为每个写命令都持久化到磁盘:

    # redis.conf
    appendonly yes
    appendfsync always 
    

综上,Redis事务具备原子性和隔离性,不具备一致性,是否具备持久性取决于持久化策略

3. 通信方式

3.1 hiredis库

如果是C++,可以使用redis自带的hireds库进行连接。下面介绍一下hiredis的主要接口:

// 建立redis同步连接
redisContext *redisConnect(const char *ip, int port);
// 建立redis异步连接
redisContext *redisConnectNonBlock(const char *ip, int port);
// 发送命令给redis,如果是同步连接,成功发送返回值为reply,否则返回NULL
void *redisCommand(redisContext *c, const char *format, ...);
// 发送异步命令给redis,还可以设置返回后的回调函数
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
// 获取reply
int redisGetReply(redisContext *c, void **reply);
// 销毁reply
void freeReplyObject(void *reply);
// 销毁redis连接上下文
void redisFree(redisContext *c);

3.2 同步连接

同步连接是指与redis进行同步通信的连接,发送数据和接收数据时可能需要等待从而造成线程阻塞,大量时间被浪费在等待IO传输上。因此在业务中一般不采用这种方式。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>int main() {redisContext *c;redisReply *reply;struct timeval timeout = {1, 500000}; // 1.5 secondsc = redisConnectWithTimeout("127.0.0.1", 6379, timeout);if (c == NULL || c->err){printf("Connecting fails\n");if (c != NULL){printf("error: %s\n", c->errstr);redisFree(c);}exit(1);}reply = redisCommand(c, "set name eeuu");if (c->err){printf("c->err: %d\n", c->err);perror("redis:");}else{printf("replay type: %d\n", reply->type);printf("%s\n", reply->str);}freeReplyObject(reply);redisFree(c);return 0;
}

3.3 异步连接

Redis异步连接依靠非阻塞IO实现,发送命令时不会等待结果返回,而是设置回调函数,当该命令返回结果时自动调用该回调函数来处理结果。

3.3.1 hiredis管理监听事件接口

hiredis主要通过IO事件监测回调函数读写数据的接口与底层的网络IO层交互。

下面看一下IO检测接口:

typedef struct redisAsyncContext {.../* Event library data and hooks */struct {void *data;/* Hooks that are called when the library expects to start reading/writing. These functions should be idempotent. */void (*addRead)(void *privdata);	// 注册读事件void (*delRead)(void *privdata);	// 注销读事件void (*addWrite)(void *privdata);	// 注册写事件void (*delWrite)(void *privdata);	// 注销写事件void (*cleanup)(void *privdata);	// 清空事件 void (*scheduleTimer)(void *privdata, struct timeval tv);	// 注册定时事件} ev;
} redisAsyncContext;

我们可以在创建redisAsyncContxt时进行设置。

当监听的事件发生时,需要网络层调用hiredis提供的处理函数。

void redisAsyncHandleRead(redisAsyncContext *ac);
void redisAsyncHandleWrite(redisAsyncContext *ac);
void redisAsyncHandleTimeout(redisAsyncContext *ac);

也就是说,网络层需要提供管理IO事件的函数以及当事件发生时调用读写函数。

用户层 hiredis层 网络层 redisAsyncCommand() addWrite() redisAsyncHandleWrite() addread() redisAsyncHandleRead() redisCallbackFn() 用户层 hiredis层 网络层
3.3.2 hiredis + libevent

libevent 是一个高效、跨平台的事件驱动编程库,主要用于构建高性能的网络应用程序和服务。它抽象化了底层的 I/O 多路复用机制,提供了一套简单的 API,用于处理事件通知、定时器和异步 I/O。

在linux平台上,libevent采用epoll实现IO复用。

适配libevent的IO事件管理函数:

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);
}static void redisLibeventCleanup(void *privdata) {redisLibeventEvents *e = (redisLibeventEvents*)privdata;if (!e) {return;}event_del(e->ev);event_free(e->ev);e->ev = NULL;if (e->state & REDIS_LIBEVENT_ENTERED) {e->state |= REDIS_LIBEVENT_DELETED;} else {redisLibeventDestroy(e);}
}

看一下hiredis自带的适配libevent的事件派发函数:

static void redisLibeventHandler(int fd, short event, void *arg) {((void)fd);redisLibeventEvents *e = (redisLibeventEvents*)arg;e->state |= REDIS_LIBEVENT_ENTERED;#define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\redisLibeventDestroy(e);\return; \}if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {redisAsyncHandleTimeout(e->context);CHECK_DELETED();}if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {redisAsyncHandleRead(e->context);CHECK_DELETED();}if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {redisAsyncHandleWrite(e->context);CHECK_DELETED();}e->state &= ~REDIS_LIBEVENT_ENTERED;#undef CHECK_DELETED
}
3.3.3 hiredis + 自定义reactor

自定义一个reactor模式的网络层的思路类似libevent,主要是要适配hiredis的接口。

以下是采用异步连接时的主函数,其中有3个hiredis提供的接口:

  1. redisAsyncConnect是hiredis的接口,可以异步建立连接,当连接成功建立后会自动调用用户自定义的ConnectCallback。
  2. redisAsyncSetConnectCallback设置连接建立后回调函数
  3. redisAsyncSetDisconnectCallback设置连接断开后的回调函数
int main(int argc, char **argv) {R = create_reactor();redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);if (c->err) {/* Let *c leak for now... */printf("Error: %s\n", c->errstr);return 1;}redisAttach(R, c);redisAsyncSetConnectCallback(c, connectCallback);redisAsyncSetDisconnectCallback(c, disconnectCallback);eventloop(R);release_reactor(R);return 0;
}

剩下还有3个用户定义的函数:

  1. create_reactor创建reactor的上下文环境,包括epfd、events缓冲区等。

  2. redisAttach,主要负责将reactor上下文环境与连接上下文绑定,代码如下:

    static int redisAttach(reactor_t *r, redisAsyncContext *ac) {redisContext *c = &(ac->c);redis_event_t *re;/* Nothing should be attached when something is already attached */if (ac->ev.data != NULL)return REDIS_ERR;/* Create container for ctx and r/w events */re = (redis_event_t*)hi_malloc(sizeof(*re));if (re == NULL)return REDIS_ERR;re->ctx = ac;re->e.fd = c->fd;re->e.r = r;// dont use event buffer, using hiredis's bufferre->e.in = NULL;re->e.out = NULL;re->mask = 0;ac->ev.addRead = redisAddRead;ac->ev.delRead = redisDelRead;ac->ev.addWrite = redisAddWrite;ac->ev.delWrite = redisDelWrite;ac->ev.cleanup = redisCleanup;ac->ev.data = re;return REDIS_OK;
    }
    

    可见其主要是创建并填充了一个redis_event_t,然后为redis异步连接上下文设置回调函数,最后将redis_event_t交给连接上下文保存。

    redis_event_t是对reactor的event_t的扩展,结构体如下:

    typedef struct {event_t e;	// 保存fd、epoll上下文、缓冲区、回调函数int mask;	// 管理已注册的事件redisAsyncContext *ctx;
    } redis_event_t;typedef struct event_s event_t;struct event_s {int fd;reactor_t *r;buffer_t *in;buffer_t *out;event_callback_fn read_fn;event_callback_fn write_fn;error_callback_fn error_fn;
    };
    

    为异步连接上下文设置回调函数是关键步骤,分别对应注册读事件、写事件、注销读事件、写事件、清空事件。hiredis会在需要时调用这些回调函数。例如,当我们调用redisAsyncCommand时,hiredis会注册调用addWrite回调函数,然后在用户代码中,我们再epoll中注册写事件,当写事件触发时,调用我们设置的write_fn。

    read_fn和write_fn被保存于event_t中,也是需要用户定义和设置:

    static void redisReadHandler(int fd, int events, void *privdata) {((void)fd);((void)events);printf("redisReadHandler %d\n", fd);event_t *e = (event_t*)privdata;redis_event_t *re = (redis_event_t *)(char *)e;redisAsyncHandleRead(re->ctx);
    }static void redisWriteHandler(int fd, int events, void *privdata) {((void)fd);((void)events);event_t *e = (event_t*)privdata;redis_event_t *re = (redis_event_t *)(char *)e;redisAsyncHandleWrite(re->ctx);
    }
    

    在上面的回调函数中,调用了redisAsyncHandleRead和redisAsyncHandleWrite这两个hiredis提供的API来将数据从内核读写到用户态的buffer中,这个buffer也是hiredis提供的。redisAsyncHandleWrite函数读取到一个完整的响应后会调用用户在调用redisAsyncCommand时设置的回调函数。

    我们可以选择在注册读写事件的时候设置读写事件发生时的回调函数,当然应该也可以选择在创建redis_event_t的时候就设置。

    static void redisAddRead(void *privdata) {redis_event_t *re = (redis_event_t *)privdata;re->e.read_fn = redisReadHandler;redisEventUpdate(privdata, EPOLLIN, 0);
    }
    static void redisAddWrite(void *privdata) {redis_event_t *re = (redis_event_t *)privdata;re->e.write_fn = redisWriteHandler;redisEventUpdate(privdata, EPOLLOUT, 0);
    }
    

完整代码:

#include <hiredis/hiredis.h>
#include <hiredis/async.h>
#include <hiredis/sds.h>#include "reactor.h"
#include "adapter_async.h"static reactor_t *R;char *rtype[] = {"^o^","STRING","ARRAY","INTEGER","NIL","STATUS","ERROR","DOUBLE","BOOL","MAP","SET","ATTR","PUSH","BIGNUM","VERB",
};void dumpReply(struct redisAsyncContext *c, void *r, void *privdata) {redisReply *reply = (redisReply*)r;switch (reply->type) {case REDIS_REPLY_STATUS:case REDIS_REPLY_STRING:printf("[req = %s]reply:(%s)%s\n", (char*)privdata, rtype[reply->type], reply->str);break;case REDIS_REPLY_NIL:printf("[req = %s]reply:(%s)nil\n", (char*)privdata, rtype[reply->type]);break;case REDIS_REPLY_INTEGER:printf("[req = %s]reply:(%s)%lld\n", (char*)privdata, rtype[reply->type], reply->integer);break;case REDIS_REPLY_ARRAY:printf("[req = %s]reply(%s):number of elements=%lu\n", (char*)privdata, rtype[reply->type], reply->elements);for (size_t i = 0; i < reply->elements; i++) {printf("\t %lu : %s\n", i, reply->element[i]->str);}break;case REDIS_REPLY_ERROR:printf("[req = %s]reply(%s):err=%s\n", (char*)privdata, rtype[reply->type], reply->str);break;default:printf("[req = %s]reply(%s)\n", (char*)privdata, rtype[reply->type]);break;}
}void
connectCallback(const redisAsyncContext *c, int status) {if (status != REDIS_OK) {printf("Error: %s\n", c->errstr);stop_eventloop(R);return;}printf("Connected...\n");redisAsyncCommand((redisAsyncContext *)c, dumpReply, "hmset role:10001", "hmset role:10001 name mark age 31 sex male");int a = 10;redisAsyncCommand((redisAsyncContext *)c, dumpReply, "hgetall role:10001", "hgetall role:10001");// ....
}void
disconnectCallback(const redisAsyncContext *c, int status) {if (status != REDIS_OK) {printf("Error: %s\n", c->errstr);stop_eventloop(R);return;}printf("Disconnected...\n");stop_eventloop(R);
}int main(int argc, char **argv) {R = create_reactor();redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);if (c->err) {/* Let *c leak for now... */printf("Error: %s\n", c->errstr);return 1;}redisAttach(R, c);redisAsyncSetConnectCallback(c, connectCallback);redisAsyncSetDisconnectCallback(c, disconnectCallback);eventloop(R);release_reactor(R);return 0;
}

学习参考

学习更多相关知识请参考零声 github。

相关文章:

Redis2——协议与异步方式

文章目录 Redis2——协议与异步方式1. Redis Pipeline2. Redis事务2.1 无锁事务控制&#xff08;乐观事务控制&#xff09;2.2 事务语句与lua脚本2.3 事务特性ACID 3. 通信方式3.1 hiredis库3.2 同步连接3.3 异步连接3.3.1 hiredis管理监听事件接口3.3.2 hiredis libevent3.3.…...

面向下一代技术,遨游通讯如何助力北斗规模化应用提速?

近日&#xff0c;纪念北斗卫星导航系统工程建设三十周年座谈会在北京隆重召开&#xff0c;据悉&#xff0c;我国计划在2035年完成下一代北斗系统的建设。“北斗牵手&#xff0c;守护永久”北斗三号短报文公众应用商用试验启动仪式也于本月在雄安新区举行&#xff0c;会上透露&a…...

vue实现echarts饼图自动轮播

echarts官网&#xff1a;Examples - Apache ECharts echartsFn.ts 把echarts函数封装成一个文件 import * as echarts from "echarts";const seriesData [{"value": 12,"name": "过流报警"},{"value": 102,"name&qu…...

数据分析的尽头是web APP?

数据分析的尽头是web APP&#xff1f; 在做了一些数据分析的项目&#xff0c;也制作了一些数据分析相关的web APP之后&#xff0c;总结自己的一些想法和大家分享。 1.web APP是呈现数据分析结果的另外一种形式。 数据分析常见的结果是数据分析报告&#xff0c;可以是PPT或者…...

windows电脑上安装树莓派操作系统

在Windows电脑上安装树莓派通常涉及以下几个步骤:准备安装工具、下载树莓派系统镜像、烧录系统到SD卡、配置树莓派以及远程连接(如果需要无显示器操作)。以下是详细的步骤说明: 一、准备安装工具 安装树莓派官方烧录工具: 下载并安装Raspberry Pi Imager。这是一个官方的…...

openssl编译安装升级为新版本

文章目录 1、下载版本2、上传并解压3、编译安装4、验证 1、下载版本 https://www.openssl.org/source/old/1.1.1/ 2、上传并解压 tar zxvf openssl-1.1.1s.tar.gz 3、编译安装 注意&#xff1a;要提前安装好 gcc perl cd openssl-1.1.1s ./config --prefix/usr/local/open…...

监控视频汇聚平台:Liveweb视频监控管理平台方案详细介绍

Liveweb国标视频综合管理平台是一款以视频为核心的智慧物联应用平台。它基于分布式、负载均衡等流媒体技术进行开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台具备多种功能&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上报、语音对讲、…...

【论文复现】基于BERT的语义分析实现

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ WRN: 宽度残差网络 概述语义分类文本分类情感分类 实现原理 核心逻辑pre_deal.pytrain.pytest_demo.py 实现方式&演示效果训练阶段测试阶…...

SMOTE | 使用SMOTE算法来处理不平衡数据的问题

需求 在学习机器学习识别信用卡欺诈交易这个项目的时候&#xff0c;样本数据集非常不平衡&#xff1a; data_df_new[Class].value_counts()0&#xff1a; 正常 1&#xff1a;欺诈 在这里了解到了SMOTE算法&#xff1a; 过采样&#xff08;Oversampling&#xff09; 过采样是…...

week 9 - Entity-Relationship Modelling

一、数据库设计的重要性 • 设计数据库可使查询更高效、简洁。 • 减少数据冗余&#xff08;data redundancy&#xff09;&#xff0c;提升表的整洁性。 二、Key Components of ER Modelling 实体-关系建模的基本构成 1. 实体&#xff08;Entity&#xff09;&#xff1a;表…...

彻底理解如何保证ElasticSearch和数据库数据一致性问题

一.业务场景举例 需求&#xff1a; 一个卖房业务&#xff0c;双十一前一天&#xff0c;维护楼盘的运营人员突然接到合作开发商的通知&#xff0c;需要上线一批热门的楼盘列表&#xff0c;上传完成后&#xff0c;C端小程序支持按楼盘的名称、户型、面积等产品属性全模糊搜索热门…...

JS基础知识05-对象、Ajax、JSON

目录 一、对象 1.1.对象&#xff08;Object&#xff09; 1.创建对象 对象的常用方法 1.2.Math对象 1.数学常数 2.数学函数 3.随机数生成 4.对数方法 1.3.Date对象 创建Date对象 获取日期和时间的方法 设置日期和时间的方法 日期的格式化方法 二、Ajax 1.创建XM…...

pandas 读写excel

在Python中&#xff0c;使用Pandas库读写Excel文件是一个常见的操作。Pandas提供了read_excel和to_excel方法来分别实现读取和写入Excel文件的功能。以下是一些基本的示例&#xff1a; ### 读取Excel文件 python import pandas as pd # 读取Excel文件 df pd.read_excel(pat…...

Windows加固脚本

echo off REM 清屏 cls title 安全策略设置批处理 color f0 echo **************************************** echo write by afei echo https://www.jianshu.com/u/ea4c85fbe8c7 echo **************************************** pause cls color 3f echo ********************…...

28.100ASK_T113-PRO Linux+QT 显示一张照片

1.添加资源文件 2. 主要代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QImage> #include <QPixmap>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);QIm…...

Vue中的计算属性和监听属性

在Vue中&#xff0c;计算属性和监听属性是两种非常有用的功能&#xff0c;它们可以帮助我们更好地管理数据和响应数据的变化。 计算属性 计算属性是基于它们的依赖进行缓存的。只有当依赖发生变化时&#xff0c;计算属性才会重新计算。这使得计算属性非常适合用于执行昂贵的计…...

基于vite创建的react18项目的单元测试

题外话 最近一个小伙伴进了字节外包&#xff0c;第一个活就是让他写一个单元测试。 嗯&#xff0c;说实话&#xff0c;在今天之前我只知道一些理论&#xff0c;但是并没有实操过&#xff0c;于是我就试验了一下。 通过查询资料&#xff0c;大拿们基本都说基于vite的项目&…...

网络——HTTP与HTTPS三次握手和四次挥手

HTTP协议本身并不直接处理TCP连接的建立和关闭&#xff0c;这些是由底层的TCP协议来完成的。但是&#xff0c;由于HTTP通常运行在TCP之上&#xff0c;因此理解TCP的三次握手&#xff08;用于建立连接&#xff09;和四次挥手&#xff08;用于关闭连接&#xff09;对于理解HTTP通…...

自然语言处理:第六十六章 17 种 prompt engineering 方法大集合

本人项目地址大全&#xff1a;Victor94-king/NLP__ManVictor: CSDN of ManVictor 原文地址&#xff1a;17 种 prompt engineering 方法大集合 写在前面: 笔者更新不易&#xff0c;希望走过路过点个关注和赞&#xff0c;笔芯!!! 写在前面: 笔者更新不易&#xff0c;希望走过路…...

MySQL —— MySQL 程序

目录 前言 一、MySQL 程序简介 二、mysqld -- MySQL 服务器 三、mysql -- MySQL 客户端 1. mysql 客户端简介 2. mysql 客户端选项 &#xff08;1&#xff09;指定选项的方式 &#xff08;2&#xff09;mysql 客户端命令常用选项 &#xff08;3&#xff09;在命令行中使…...

AI蛋白质设计与人工智能药物设计

AI蛋白质设计与人工智能药物设计 AI蛋白质设计 一、蛋白质相关的深度学习简介 1.基础概念 1.1.机器学习简介&#xff1a;从手写数字识别到大语言模型 1.2.蛋白质结构预测与设计回顾 1.3.Linux简介 1.4.代码环境&#xff1a;VS code和Jupyter notebook* 1.5.Python关键概…...

Java基础之控制语句:开启编程逻辑之门

一、Java控制语句概述 Java 中的控制语句主要分为选择结构、循环结构和跳转语句三大类&#xff0c;它们在程序中起着至关重要的作用&#xff0c;能够决定程序的执行流程。 选择结构用于根据不同的条件执行不同的代码路径&#xff0c;主要包括 if 语句和 switch 语句。if 语句有…...

安装Fcitx5输入框架和输入法自动部署脚本(来自Mark24)-Ubuntu通用

在Ubuntu22.04上安装rime中文输入法的基本教程 上述文章接近废弃。 使用新逻辑配置基本的Fcitx5的输入法。 安装 第一步&#xff0c;下载相关组件 sudo nala install vim sudo nala install ruby sudo nala install fcitx5-rime第二步&#xff0c;设置语言为Fcitx5 而非 默认…...

软件无线电(SDR)的架构及相关术语

今天简要介绍实现无线电系统调制和解调的主要方法&#xff0c;这在软件定义无线电(SDR)的背景下很重要。 外差和超外差 无线电发射机有两种主要架构——一种是从基带频率直接调制到射频频率&#xff08;称为外差&#xff09;&#xff0c;而第二种超外差是通过两个调制阶段来实…...

刷题分享11_30

刷题分享 1.(力扣216)这是一道回溯算法的经典题目。对于回溯算法&#xff0c;一般backtracking是没有返回值的&#xff0c;参数也比较不固定&#xff0c;需要根据每个题的特点来具体分析。这道题因为不能取到重复元素&#xff0c;所以需要额外加一个参数startindex&#xff0c;…...

Java技术复习提升 17反射

本章涉及到框架开发中必用的反射以及常用方法 很重要 注重理解并实践 第17章 反射 17.1 一个需求引出反射 package com.fsl; public class Cat {private String name "招财猫";public int age 10; //public的public Cat() {} //无参构造器public Cat(String name)…...

Python中的字符串

Python中的字符串 在Python中&#xff0c;字符串是用于表示文本数据的基本数据类型。字符串可以包含字母、数字、符号和空格等字符。Python提供了多种方式来定义和操作字符串。 字符串的定义 在Python中&#xff0c;字符串可以用单引号 或双引号 "" 括起来。例如…...

B站狂神说Mybatis+Spring+SpringMVC整合理解(ssm框架整合)

文章目录 0.写在前面(对mybatis,spring的理解)&#xff08;不看可跳过&#xff09;0.1 为什么需要mybatis0.2 为什么需要spring0.3为什么需要springmvc 1.新建ssmbuild数据库2.新建Maven项目3.初始化步骤3.1 配置下载maven依赖&#xff0c;构建资源导出3.2 连接数据库3.3建包&a…...

python:文件操作

一、文件路径 在Windows系统中&#xff0c;每个磁盘都有自己的根目录&#xff0c;用分区名加反斜杠来表示。我们定位文件的位置有两种方法&#xff0c;一种是绝对路径&#xff0c;另一种是相对路径。绝对路径是从根目录出发的路径&#xff0c;路径中的每个路径之间用反斜杠来分…...

ECharts柱状图-极坐标系下的堆叠柱状图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…...

HDMI协议

HDMI设计3--HDMI 1.4/2.0 Transmitter Subsystem IP - 皮皮祥 - 博客园 HDMI设计4--HDMI 1.4/2.0 Receiver Subsystem IP - 皮皮祥 - 博客园 HDMI协议 - 标签 - 皮皮祥 - 博客园...

SpringBoot集成Flowable

一、工作流介绍 1、概念 通过计算机对业务流程的自动化管理。工作流是建立在业务流程的基础上&#xff0c;一个软件的系统核心根本上还是系统的业务流程&#xff0c;工作流只是协助进行业务流程管理。 解决的是&#xff1a;在多个参与者之间按照某种预定义的规则自动进行传递文…...

五,[GXYCTF2019]Ping Ping Ping1

进入靶场&#xff0c;有提示 我们在url试着输入本地IP&#xff0c;返回了ping命令 既然要在url处传参&#xff0c;那就用postman&#xff0c;再输入ip127.0.0.1 & ls&#xff0c;试着列出目录内容 ok&#xff0c;好像是个脏话,它过滤了空格 试着穿越又看到了脏话&#xff0…...

Linux -初识 与基础指令1

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【Linux】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 文章目录 &#x1f4da; 前言&#x1f5a5;️ 初识&#x1f510; 登录 root用户&#x1f465; 两种用户➕ 添加用户&#x1f9d1;‍&#x1f4bb; 登录 普通用户⚙️ 常见…...

单片机学习笔记 12. 定时/计数器_定时

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…...

采用片上光学相控阵的激光雷达

激光雷达基础知识 LIDAR 基于众所周知的 RADAR 原理雷达是20世纪初就存在的著名技术激光雷达使用光频率而不是无线电波 激光雷达和雷达 使用相控阵的激光雷达通过干涉来提高方向性 激光雷达的输出剖面是阵列因子和单天线远场的乘积。 N &#xff1a;天线数量 k &#xff1a;…...

LeetCode Hot100 11~20

目录 子串11. 滑动窗口最大值12. 最小覆盖子串 数组13. 最大子数组和14. 合并区间15. 翻转数组16. 除数字自身以外的乘积17. 缺失的第一个正数 矩阵18. 矩阵置零19. 螺旋矩阵20 旋转图像90度 子串 11. 滑动窗口最大值 本题使用deque来维护一个单调队列 注意删除元素和添加元素…...

泰州榉之乡全托机构探讨:自闭症孩子精细动作训练之法

当发现自闭症孩子精细动作落后时&#xff0c;家长们往往会感到担忧和困惑。那么&#xff0c;自闭症孩子精细动作落后该如何训练呢&#xff1f;今天&#xff0c;泰州榉之乡全托机构就来为大家详细解答。 榉之乡大龄自闭症托养机构在江苏、广东、江西等地都有分校&#xff0c;一直…...

Mybatis:CRUD数据操作之修改数据update

Mybatis基础环境准备请看&#xff1a;Mybatis基础环境准备 本篇讲解Mybati数据CRUD数据操作之修改数据 用户在该页面书写需要修改的数据&#xff0c;点击 提交 按钮&#xff0c;就会将数据库中对应的数据进行修改。注意一点&#xff0c;如果哪儿个输入框没有输入内容&#xff…...

模拟器快速上手,助力HarmonyOS应用/服务高效开发

文章目录 1 创建模拟器1&#xff09;打开设备管理界面2&#xff09;设置本地模拟器实例存储路径3&#xff09;创建一个模拟器&#xff08;1&#xff09;选择模拟器设备&#xff08;2&#xff09;创建模拟器&#xff08;3&#xff09;启动模拟器&#xff08;4&#xff09;关闭模…...

ERROR in [eslint] Invalid Options ‘extensions‘ has been removed.

看着这个报错 感觉是版本不对引起的 ERROR in [eslint] Invalid Options: - Unknown options: extensions - extensions has been removed. ERROR in Error: Child compilation failed: [eslint] Invalid Options: - Unknown options: extensions - extensions has b…...

40分钟学 Go 语言高并发:GC原理与优化

GC原理与优化 一、GC基础知识概览 方面核心概念重要性优化目标GC算法三色标记法、并发GC⭐⭐⭐⭐⭐理解GC工作原理垃圾回收策略触发条件、回收步骤⭐⭐⭐⭐⭐掌握GC过程GC调优参数设置、性能监控⭐⭐⭐⭐优化GC效果内存管理内存分配、内存逃逸⭐⭐⭐⭐⭐减少内存压力 让我们…...

【UG\NX二次开发-Block UI】指定方位 VisibleManipulatorHandles 设置控制器手柄可见

特定于块属性 VisibleManipulatorHandles 值 Origin 0x1 原点 Ratate X 0x10 旋转 Ratate Y 0x20 Ratate Z 0x40 Translate X 0x2 平移 Translate Y 0x4 Translate Z 0…...

【Spring】聊聊@EventListener注解原理

1.一个Demo出发 在平时的开发中&#xff0c;其实编写同步线程代码是比较容易的&#xff0c;但是如何将一些操作和另外一些操作进行解除耦合&#xff0c;而事件方式 是一种很好的解耦合方式&#xff0c;比如当一个用户注销一个APP之后&#xff0c;需要发送一些短信 让他引流回来…...

Online Judge——【前端项目初始化】全局状态管理

状态管理&#xff1a;所有页面全局共享的变量&#xff0c;而不是局限在某一个页面中。 适合作为全局状态的数据&#xff1a;比如已登录用户信息。 目录 一、创建user.ts文件二、定义user模块三、获取静态变量四、修改状态变量 一、创建user.ts文件 我们要实现状态管理的话&am…...

微信小程序构建npm失败,没有找到可以构建的npm包

方法&#xff1a;打开终端输入 npm init -y npm install 或 yarn install我用 npm install 下载后并没有出现node_modules, 又用 yarn install 下载&#xff0c;成功了 下载好后&#xff0c;在project.config.json文件添加 "showShadowRootInWxmlPanel": true, …...

《数字图像处理基础》学习07-图像几何变换之最近邻插值法放大图像

目录 一&#xff0c;概念 二&#xff0c;题目及matlab实现 1&#xff0c;解题思路 2&#xff0c;matlab实现 1&#xff09;matlab思路 2&#xff09;完整代码 三&#xff0c;放大图像及matlab实现 一&#xff0c;概念 通过上一篇&#xff0c;我已经学习了使用最邻近插…...

科技为翼 助残向新 高德地图无障碍导航规划突破1.5亿次

今年12月03日是第33个国际残疾人日。在当下科技发展日新月异的时代&#xff0c;如何让残障人士共享科技红利、平等地参与社会生活&#xff0c;成为当前社会关注的热点。 中国有超过8500万残障人士&#xff0c;其中超过2400万为肢残人群&#xff0c;视力障碍残疾人数超过1700万…...

数据结构——有序二叉树的构建遍历查找

树节点 先定义树节点结构&#xff0c;代码如下&#xff1a; package tree;public class TreeNode {public int data;public TreeNode left;public TreeNode right;//数据的类型决定数据在内存中的存储形式,//这样可以接受本类型的数据public TreeNode(int data) {this.datada…...

React 状态管理:Redux 和 MobX 的对比与选择

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...