Redis 笔记二

概览

1.高并发秒杀问题及可能出现的bug

2.秒杀场景JVM级别锁和分布式锁

3.大厂分布式锁Redisson框架

4.从Redisson源码剖析lua解决锁原子性问题

5.从Redisson源码剖析经典锁续命问题

6.Redis主从架构锁失效如何解决

7.Redlock分布式锁高并发下可能存在的问题

8.双十一大促如何将分布式锁性能提升100倍

9.放置订单重复提交或支付分布式锁方案设计

10.防止取消订单误支付bug分布式锁方案设计

1.减库存操作的实现和可能存在的问题

快速待见一个redis环境:Redis和Redis可视化管理工具的下载和安装_redisdesktopmanager下载-CSDN博客

新建一个springboot快速构建:pom.xml

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.9</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>Demo project for Spring Boot</description><properties><java.version>18</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

(1)实现一个简单的减库存方案

它有哪些问题?

没有线程安全的保护措施,多个进程访问时,可能会导致超卖问题。

    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}return "end";}

(2)为了实现线程安全,我们加一个简单的锁Synchronized

同步代码块,通过内置排序锁实现,多个进程访问时,排队进行

它有什么问题?

在单机模式下,能够保证线程安全,但是在分布式集群下,还是会线程不安全,导致超卖问题

    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){synchronized (this){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}}return "end";}

(3)使用Redis实现分布式锁

使用分布式锁的原因是在集群结构上加锁,解决集群环境下多并发导致超卖问题。

SETNX命令:  setnx key value

  • 将key的值设置为value,当且仅当key不存在
  • 若key存在,则该命令无任何操作
  • SETNX是Set if not exists的简写
  • 可用版本>=1.0.0

使用该命令保证只有一个用户可以修改成功,另一个用户的操作不生效。

多线程并发在redis排队,单线程处理,拿到锁的处理,锁使用完,要删除,不然会造成死锁问题。

    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){String lockKey="product_101";Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan");//jedis.setnx(k,v)if(!result){return "biz_code";}int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}return "end";}

改进一:当程序异常,未及时对持有的锁释放,也会导致死锁问题。

               解决:将锁删除放在finally里,保证一定会执行。

改进二:当程序挂掉,但是锁没有释放,finally也没有执行,还是会导致死锁问题。

               解决:设置超时时间,如果程序挂了,到时间自动释放锁,不影响后续操作。

    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){String lockKey="product_101";Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan");//jedis.setnx(k,v)//设置超时时间stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);if(!result){return "biz_code";}try{int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}}finally {stringRedisTemplate.delete(lockKey);}return "end";}

改进三: 获取锁和超时时间分开写,可能会获取锁还没设置超时时间的时候挂掉,还是会导致前面那个问题。

              解决:用redis内置方法,获取锁的同时设置超时时间,保证操作原子性。

String lockKey="product_101";
//Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan");//jedis.setnx(k,v)
设置超时时间
//stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan",10, TimeUnit.SECONDS);//jedis.setnx(k,v)

(4)高并发场景下还有什么问题?

问题一:高并发场景下存在,一个线程刚加锁,就被另一个线程解锁的问题,导致锁一直刚获取就失效。

问题的关键在:不能释放别人的锁。应确保谁加锁,谁释放。

 给锁设置线程ID,确保加锁和释放锁的是同一线程。

Rlock redissonLock=redisson.getLock(lockKey);
redissonLock.lock();
    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){String lockKey="product_101";//        Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan");//jedis.setnx(k,v)
//        //设置超时时间
//        stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);String clientId = UUID.randomUUID().toString();Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10, TimeUnit.SECONDS);//jedis.setnx(k,v)if(!result){return "biz_code";}Rlock redissonLock=redisson.getLock(lockKey);redissonLock.lock();// setIfAbsent(lockKey,clientId,30,TimeUnit.SECONDS)try{int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}}finally {if(clientId.equals(stringRedisTemplate.opsForValue().get("stock")))stringRedisTemplate.delete(lockKey);}return "end";}

问题二:线程获取锁,但是执行过程还没结束,卡顿,但是锁过期了,导致需要重新弄加锁的问题。

解决:无论设置多大的超时时间都有一定的概率导致这个问题,所以解决该问题的核心点是:锁续命机制——线程执行结束前,不断给即将过期的锁增加超时时间,以延长锁的寿命。

finally {if(clientId.equals(stringRedisTemplate.opsForValue().get("stock")))stringRedisTemplate.delete(lockKey);
}

2.Redis分布式加锁与锁续命机制

1.Redisson和Jedis的区别是什么?

Redisson 和 Jedis 的简单比较_redisson代替jedis-CSDN博客

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.6.5</version>
</dependency>
	@Beanpublic Redisson redisson(){//此为单机模式Config config=new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);//config.setLockWatchdogTimeout(10000);//设置分布式锁watch dog超时时间return (Redisson) Redisson.create(config);}

Controller

@Autowired
private Redisson redisson;

2.Redis加锁的代码

    @Autowiredprivate Redisson redisson;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("~/deduct_stock")public String deductStock(){String lockKey="product_101";//        Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhangsan");//jedis.setnx(k,v)
//        //设置超时时间
//        stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);String clientId = UUID.randomUUID().toString();Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10, TimeUnit.SECONDS);//jedis.setnx(k,v)if(!result){return "biz_code";}Rlock redissonLock=redisson.getLock(lockKey);redissonLock.lock();// setIfAbsent(lockKey,clientId,30,TimeUnit.SECONDS)try{int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")if(stock>0){int realStock =stock-1;stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value)System.out.println("扣减成功,剩余库存:"+realStock);}else{System.out.println("扣减失败,库存不足");}}finally {redissonLock.unlock();
//            if(clientId.equals(stringRedisTemplate.opsForValue().get("stock")))
//                stringRedisTemplate.delete(lockKey);}return "end";}

3.Redis加锁底层实现

加锁的代码:

RLock redissonLock=redisson.getLock(lockKey);
redissonLock.lock();// setIfAbsent(lockKey,clientId,30,TimeUnit.SECONDS)

RedissonLock.class的lock()加锁: 

具体的加锁代码是由Lua脚本实现的。

4.Lua脚本实现redis原子性操作

        <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {this.internalLockLeaseTime = unit.toMillis(leaseTime);return this.commandExecutor.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hset', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then" +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",Collections.singletonList(this.getName()), new Object[]{this.internalLockLeaseTime, this.getLockName(threadId)});

Redis Lua脚本

Redis 2.6推出Redis Lua脚本,允许开发者使用Lua语言编写脚本传到Redis中执行。通过内置Lua解释器,可以使用EVAL命令对Lua脚本进行求值。

-------------------------------------------------------------------------------------------------------------------------

Redis Lua的优势

减少网络开销:

        将原先五次请求放入redis服务器上完成。

        使用脚本,减少了网络往返时延,和管道类似。

原子操作:

        Redis将整个脚本作为一个整体执行,不允许中间插入其他命令。管道不是原子的,但Redis的批量操作(类似mset)是原子的

替代Redis事务

        redis自带的事务功能很鸡肋,而redis的lua脚本几乎实现了常规事务功能。官网推荐redis事务可以使用Redis Lua脚本替代。

-------------------------------------------------------------------------------------------------------------------------

EVAL命令格式:

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

  • script参数是一段Lua脚本程序,运行再redis内置的lua解释器里。被定义为一个Lua函数。
  • numkeys参数用于指定键名参数个数[key...]
  • 表示脚本中所用到的那些Redis键key。键名可以在Lua通过全局变量KEYS数组,用1为基址的形式访问:KEYS[1]

>eval "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]} 2 key1 key2 first second

)1 "key1"

)2 "key2"

)3 "first"

)4 "second"

return {KEYS[1], KEYS[2], ARGV[1], ARGV[2]}   被求值的Lua脚本,数字2指定了键名参数的数量

key1 key2是键名参数,分别使用KEYS[1], KEYS[2]访问

first second是附加参数, 分别用ARGV[1], ARGV[2]访问

-------------------------------------------------------------------------------------------------------------------------

在Lua脚本中,可以使用redis.call()函数执行redis命令

jedis.set("product_stoc_10016","15");//初始化商品10016的库存String script="local count=redis.call('get',KEYS[1])"+"local a=tonumber(count)"+"local b=tonumber(ARGV[1])"+"if a>=b then "+"redis.call('set',KEYS[1],a-b)"+"return 1"+"end"+"return 0";Object obj=jedis.eval(script,Arrays.asList(product_stock_10016),Arrays.asList("10"));
System.out.println(obj);

不要再Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他命令。

redis是单线程、单线程执行脚本。管道不会阻塞redis.

5.Redis可重入锁的实现

为什么可以保证原子性——因为这里直接使用redis的操作命令,redis操作是原子性的。

SETNX可以用作加锁原语(locking primitive)。

如:SETNX lock.foo <current Unix time+lock timeout +1>

返回1:客户端获取锁成功,可通过DEL lock.foo释放锁

返回0:获取失败,有其他客户端加锁

        如果是非阻塞锁(nonblocking lock),则返回调用,或进入一个重试循环,直到获取锁或重试超时。【超时时间是为了解决死锁问题】

Redis在这里加入的锁是可重入锁、异步回调的。

这里断的刷新过期时间,多线程不会死锁吗?这里的锁是可重入锁,异步回调

不断刷新过期时间,过期时间不会无限长吗?

若快过期了但是还没有执行完,则进入锁续命,延续过期时间。

每十秒续命以一次?

RedissonLock.class

3.Redis性能优化

redis单线程处理,相互等待,对于性能还是有影响的

1.主从切换丢锁

数据异步,所以主从切换,如果锁是数据,就会可能由数据丢失导致锁丢失。

场景:主节点数据还没有同步给从节点,主节点挂了,从节点成为新的主节点,但丢失了部分数据。

zookeeper (偏向CP):一致性,锁同步一半才算成功——ZAB崩溃恢复,重新选举的机制,确保数据不丢失

redis (偏向AP):即想要redis高性能,又不想丢锁——redlock:一半加锁即为成功——数据一致性,和zookeeper底层相同——>redlock机制

CAP原理:

【大数据专题】大数据理论基础01之分布式CPA原理深入理解_分布式cpa理论-CSDN博客

百度安全验证

RAFT算法:

RAFT算法详解-腾讯云开发者社区-腾讯云

2.redlock实现原理

Redis使用红锁来解决这个问题:只有当集群中有一半的节点加锁成功,就认为加锁成功。

红锁实现简单,但存在一些问题:

  • 主从切换,切完当前主节点有之前主节点的锁嘛?
  • 切完还能保证一般加锁成功嘛?至少5台,保证最多2台挂掉。
  • 为什么保证奇数台:3-5台奇数台,节约资源,选举无平局。

除此之外,还可以通过持久化来防止数据丢失:

持久化重启,锁丢失,没有一般加锁成功和解——每条命令持久化,但是性能差

通过修改配置打开AOF功能:

# appendonly yes

从现在开始,每执行一个改变数据集的操作,就被追加到AOF文件末尾。

redis重启时,通过重新执行AOF来重建数据集

配置redis多久将数据fsync到磁盘:

appendfsync always:        每次有新命令就追加AOP文件,慢,但安全

appendfsync everysec:        每秒fsync一次追加,快,其丢只丢一秒的数据【推荐,兼顾速度和安全】

appendfsync no:        从不fsync,将数据交给系统处理,更快,但不安全。

3.Redis优化方案

4-5种优化方案:

1.分段锁

将库存数据分成10端,性能提升10倍

2.点好多个页面,后端重复提交|重复支付验证,如何验证

token只能针对同一个页面,多个页面没用来说,不能避免这个问题。可以考虑分布式锁尝试解决。

3.支付和取消同时进行

电商场景可能存在的问题

Redis10大性能优化策略-电子发烧友网

4.redis面试题

Redis系列面试题整理(含参考答案) - 知乎

2021年关于Redis最新的50道面试题(含答案)_以下提供多种 redis 优化的做法,错误的是哪个选项-CSDN博客

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

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

相关文章

alfred自定义谷歌翻译workflow

如果要实现自定义workflow&#xff0c;则必须安装付费版的alfred&#xff0c;囊中羞涩的话可以自行淘宝。自定义步骤如下&#xff1a; 1. 新建空的workflow&#xff0c;填写基本信息 2. 开发python脚本 打开该workflow所在目录&#xff0c;进行下面步骤&#xff1a; 首先安装…

OpenCV第 1 课 计算机视觉和 OpenCV 介绍

文章目录 第 1 课 计算机视觉和 OpenCV 介绍1.机器是如何“看”的2.机器视觉技术的常见应用3.图像识别介绍4. 图像识别技术的常见应用5.OpenCV 介绍6.图像在计算机中的存储形式 第 1 课 计算机视觉和 OpenCV 介绍 1.机器是如何“看”的 我们人类可以通过眼睛看到五颜六色的世界…

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)

Cornerstone3D介绍 Cornerstone3D是一个专门为处理三维医学影像而设计的JavaScript库。 它是Cornerstone项目的一部分&#xff0c;旨在为医学影像社区提供高性能、可扩展且易于使用的开源Web工具&#xff0c;专注于提供交互式的3D医学图像浏览体验&#xff0c;适用于多种医学…

9款最新文生图模型汇总!含华为、谷歌、Stability AI等大厂创新模型(附论文和代码)

2023年真是文生图大放异彩的一年&#xff0c;给数字艺术界和创意圈注入了新鲜血液。从起初的基础图像创作跃进到现在的超逼真效果&#xff0c;这些先进的模型彻底变革了我们制作和享受数字作品的途径。 最近&#xff0c;一些大公司比如华为、谷歌、还有Stability AI等人工智能巨…

【Linux】Linux进程信号(上)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【Linux】…

【新加坡机器人学会支持】第三届工程管理与信息科学国际学术会议 (EMIS 2024)

第三届工程管理与信息科学国际学术会议 (EMIS 2024) 2024 3rd International Conference on Engineering Management and Information Science 【国际高级别专家出席/新加坡机器人学会支持】 第三届工程管理与信息科学国际学术会议 (EMIS 2024)将于2024年4月12-14日在中国洛…

算法(4)——前缀和

目录 一、前缀和的定义 二、一维前缀和 三、一维前缀和OJ题 3.1、前缀和 3.2、寻找数组中心下标 3.3、除自身以外数组的乘积 3.4、和为K的数组 3.5、和可被K整除的子数组 3.6、连续数组 四、二位前缀和 4.1、二维前缀和 4.2、矩阵区域和 一、前缀和的定义 对于一个…

40. 组合总和 II - 力扣(LeetCode)

题目描述 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 输入示例 candidates [10,1,2,7,…

Oracle Apps文件系统和数据库的维护工作ADADMIN

ADADMIN主要用于Oracle Apps文件系统和数据库的维护工作&#xff0c;是Apps DBA常用的工具。其一级菜单的具体功能介绍如下&#xff1a; AD Administration Main Menu&#xff1a; Generate Applications Files&#xff1a;如果系统用户无法访问Message、Form或者Report&#…

开始学习vue2(Vue方法)

一、过滤器 过滤器&#xff08;Filters&#xff09;是 vue 为开发者提供的功能&#xff0c;常用于文本的格式 化。过滤器可以用在两个地方&#xff1a;插值表达式 和 v-bind 属性绑定。 过滤器应该被添加在 JavaScript 表达式的尾部&#xff0c;由“管道符 ”进行 调用&#…

【Leetcode 144.二叉树的前序遍历】将二叉树每个节点的值以前序遍历的顺序存入数组中

int* preorderTraversal( struct TreeNode*root, int* returnSize) { }解答代码&#xff1a; int TreeSize(struct TreeNode*root){return rootNULL?0:TreeSize(root->left)TreeSize(root->right)1;}void Prevorder(struct TreeNode*root,int*a,int*pi){if(rootNULL)re…

理解反向代理

反向代理是一个不可或缺的组件。 它在客户端和服务器之间充当中介&#xff0c;提高了安全性、负载平衡和应用性能。 一、反向代理简介 反向代理是一种服务器&#xff0c;它位于客户端和后端服务器之间。与常见的&#xff08;正向&#xff09;代理不同&#xff0c;反向代理代表…

宝塔面板部署MySQL并结合内网穿透实现公网远程访问本地数据库

文章目录 前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 宝塔面板的简易操作性,使得运维难度降低,简化了Linux命令行进行繁琐的配置,下面简单几步,通过宝塔面板cp…

【C++】list的使用

目录 1 构造1.1 无参构造1.2 构造的list中包含n个值为val的元素1.3 用[first, last)区间中的元素构造list1.4 拷贝构造 2 迭代器的使用2.1 begin end2.2 rbegin rend 3 容量操作3.1 empty size 4 获取元素4.1 front back 5 插入、删除、修改5.1 头插-push_front和尾插-push…

oracle 19c rac集群管理 ------ 日志管理

oracle 19C rac 数据库的目录结构及日志路径 在Oracle 19c RAC&#xff08;Real Application Clusters&#xff09;集群中&#xff0c;有多个组件和层级生成的日志文件&#xff0c;记录着集群的活动、事件和错误信息&#xff0c;用于故障诊断、性能优化和集群管理。以下是常见…

ubuntu 20.04 更新 autoconf 版本

前言 由于最近打算交叉编译 python&#xff0c;依赖 libffi 库&#xff0c;而交叉编译 libffi 库&#xff0c;由于使用的是 github 上的 libffi&#xff0c;又提示 autoconf 版本太低了&#xff0c;所以&#xff0c;先更新 autoconf 的版本 当前 ubuntu 20.04 上安装的 autuco…

工作三年的我在思考,前端还值得学习吗

一、聊这个话题的背景 最近准备换工作&#xff0c;准备了一段时间的简历后&#xff0c;偶然看到群里面有人发了张图片。 虽然颇有调侃的意味&#xff0c;但是联想起过往在公司的工作&#xff0c;项目架构设计向来由后端主管&#xff0c;领导们也基本都是后端&#xff0c;自己日…

消息队列RabbitMQ.02.交换机的讲解与使用

目录 RabbitMQ中交换机的基本概念与作用解析 交换机的作用&#xff1a; 交换机的类型&#xff1a; 直连交换机&#xff08;Direct Exchange&#xff09;&#xff1a; 将消息路由到与消息中的路由键&#xff08;Routing Key&#xff09;完全匹配的队列。 主题交换机&#x…

利用Python与Flet写一个图片无损放大软件

一、前言&#xff1a; 将上传的图片无损放大2倍、4倍、8倍 二、利用Flet写个前端页面&#xff1a; 三、前端页面代码&#xff1a; page.title "图片无所放大"page.vertical_alignment "center"page.window_width 500 # windows width is 200 pxpage…

《小学生作文辅导》期刊投稿邮箱

《小学生作文辅导》是国家新闻出版总署批准的正规教育类期刊&#xff0c;适用于全国各小学语文老师事业单位及个人&#xff0c;具有原创性的学术理论、工作实践、科研成果和科研课题及相关领域等人员评高级职称时的论文发表&#xff08;单位有特殊要求除外&#xff09;。 栏目…