Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性

文章目录

  • 前言
  • 1.使用SpringBoot +Redis 原生实现方式
  • 2.使用redisson方式实现
  • 3. 使用Redis+Lua脚本实现
    • 3.1 lua脚本
      • 代码逻辑
    • 3.2 与SpringBoot集成
  • 4. Lua脚本方式和Redisson的方式对比
  • 5. 源码地址
  • 6. Redis从入门到精通系列文章
  • 7. 参考文档

在这里插入图片描述

前言

背景:最近有社群技术交流的同学,说面试被问到商品库存扣减的问题。我大概整理了一下内容,方便大家理解。其实无外乎就是分布式锁和Redis命令的原子性问题

在分布式系统中,保证数据的原子性和一致性是一个关键问题。特别是在库存扣减等场景中,确保操作的原子性是至关重要的,以避免数据不一致和并发冲突的问题。为了解决这个挑战,我们可以利用 Redis 数据库的强大功能来实现库存扣减的原子性和一致性。

本博客将介绍两个关键技术:Redis Lua脚本和Redisson,它们在库存扣减场景中的应用。Lua脚本是一种嵌入在 Redis 服务器中执行的脚本语言,具有原子性执行和高效性能的特点。而Redisson是一个基于 Redis 的分布式 Java 对象和服务框架,提供了丰富的功能和优势。

所以无论是对于中小型企业还是大型互联网公司,保证库存扣减的原子性和一致性都是至关重要的。本博客将帮助读者全面了解如何利用 Redis Lua脚本和 Redisson 来实现这一目标,为他们的分布式系统提供可靠的解决方案。让我们一起深入研究这些强大的工具,提升我们的分布式系统的性能和可靠性。

1.使用SpringBoot +Redis 原生实现方式

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class StockService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
	// 扣减商品库存
    public void decreaseStock(String productId, int quantity) {
        String lockKey = "lock:" + productId;
        String stockKey = "stock:" + productId;

        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();

        Boolean acquiredLock = valueOperations.setIfAbsent(lockKey, "locked");

        try {
            if (acquiredLock != null && acquiredLock) {
                // 获取锁成功,设置锁的过期时间,防止死锁
                redisTemplate.expire(lockKey, 5, TimeUnit.SECONDS);

                Integer currentStock = (Integer) valueOperations.get(stockKey);
                if (currentStock != null && currentStock >= quantity) {
                    int newStock = currentStock - quantity;
                    valueOperations.set(stockKey, newStock);
                    System.out.println("库存扣减成功");
                } else {
                    System.out.println("库存不足,无法扣减");
                }
            } else {
                System.out.println("获取锁失败,其他线程正在操作");
            }
        } finally {
            // 释放锁
            if (acquiredLock != null && acquiredLock) {
                redisTemplate.delete(lockKey);
            }
        }
    }
}  

我们思考一下,以上这种写法存在几个问题,这种问题

  1. 锁的释放问题:在当前代码中,锁的释放是通过判断获取锁成功与否来决定是否释放锁。然而,如果在执行redisTemplate.expire设置锁的过期时间之后,代码发生异常导致没有执行到锁的释放部分,将会导致锁无法及时释放,进而可能导致其他线程无法获取锁。为了解决这个问题,可以考虑使用Lua脚本来实现原子性的获取锁和设置过期时间。

  2. 锁的重入问题:当前代码中,没有对锁的重入进行处理。如果同一个线程多次调用decreaseStock方法,会导致获取锁失败,因为锁已经被当前线程占用。为了解决这个问题,可以考虑使用ThreadLocal或者维护一个计数器来记录锁的重入次数,以便在释放锁时进行正确的处理。

解决方法
对于上述代码的优化,可以考虑以下几点:

  1. 使用setIfAbsent方法设置锁,并将锁的过期时间与设置锁合并为一个原子操作,以避免在获取锁后再次操作Redis的时间开销。可以使用opsForValue().setIfAbsent(lockKey, "locked", 5, TimeUnit.SECONDS)来实现。这样可以确保获取锁和设置过期时间是一个原子操作,避免了两次Redis操作的时间间隔。

  2. 使用lua脚本来实现锁的释放,以确保释放锁的原子性。通过使用execute方法执行lua脚本,可以将锁的释放操作合并为一个原子操作。以下是示例代码:

String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
Long releasedLock = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), "locked");
if (releasedLock != null && releasedLock == 1) {
    // 锁释放成功
}
  1. 使用Redisson等可靠的分布式锁框架,它们提供了更丰富的功能和可靠性,并且已经解决了很多与分布式锁相关的问题。这些框架可以简化代码并提供更强大的锁管理功能,例如重入锁、公平锁、红锁等。你可以在项目中引入Redisson等框架,并使用它们提供的分布式锁功能。

2.使用redisson方式实现

可能有一些同学对Redisson不太了解,我大概讲解一下他的一些优秀之处。
Redisson 是一个基于 Redis 的分布式 Java 对象和服务框架,它提供了丰富的功能和优势,使得在分布式环境中使用 Redis 更加方便和可靠。可以这么说,Redisson 是目前最牛逼最强的基于Redis的分布式锁工具,没有之一,所以大家可以在项目中放心大胆的使用,有问题再说问题,不要太过羁绊

Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上

  1. 分布式锁: 提供了可重入锁、公平锁、联锁、红锁等多种分布式锁的实现,可以用于解决并发控制问题。它支持锁的自动续期和异步释放,可以防止锁的过期导致的问题,并提供了更高级的功能如等待锁、超时锁等。

  2. 分布式集合: 提供了一系列分布式集合的实现,如分布式列表、集合、有序集合、队列、阻塞队列等。这些分布式集合可以安全地在多个节点之间共享和操作数据,提供了高效的数据存储和访问机制。

  3. 分布式对象:Redisson 支持在分布式环境中操作 Java 对象。它提供了分布式映射、分布式原子变量、分布式计数器等功能,可以方便地对分布式对象进行存储、操作和同步。

  4. 优化的 Redis 命令:Redisson 通过优化 Redis 命令的调用方式,提供了更高效的数据访问。它使用了线程池和异步操作,可以在一次网络往返中执行多个 Redis
    命令,减少了网络延迟和连接数,提高了性能和吞吐量。

  5. 可扩展性和高可用性:Redisson 支持 Redis 集群和哨兵模式,可以轻松应对大规模和高可用性的需求。它提供了自动的故障转移和主从切换机制,确保在节点故障时系统的可用性和数据的一致性。
    一个基于Redis实现的分布式工具,有基本分布式对象和高级又抽象的分布式服务,为每个试图再造分布式轮子的程序员带来了大部分分布式问题的解决办法。

吹了那么多概念,请show me code。ok 接下来我们使用Redisson库实现的库存的原子性和一致性。

  1. 添加依赖:在pom.xml文件中添加以下依赖以使用Redisson库来实现分布式锁。
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.1</version>
</dependency>
  1. 配置Redisson:在Spring Boot的配置文件中添加Redisson的配置,例如application.properties
# Redisson配置
spring.redisson.config=classpath:redisson.yaml

resources目录下创建redisson.yaml文件,并配置Redis连接信息和分布式锁的相关配置。以下是一个示例配置:

singleServerConfig:
  address: "redis://localhost:6379"
  password: null
  database: 0
  connectionPoolSize: 64
  connectionMinimumIdleSize: 10
  subscriptionConnectionPoolSize: 50
  dnsMonitoringInterval: 5000
  lockWatchdogTimeout: 10000
  1. 创建一个名为StockService的服务类,修改decreaseStock方法来使用分布式锁:
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.concurrent.TimeUnit;

@Service
public class StockService {
    private static final String STOCK_KEY = "stock:product123";
    private static final String LOCK_KEY = "lock:product123";

    @Autowired
    private ReactiveRedisTemplate<String, String> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    public Mono<Boolean> decreaseStock(int quantity) {
        RLock lock = redissonClient.getLock(LOCK_KEY);
        return Mono.fromCallable(() -> {
          //==核心代码start==
            try {
                boolean acquired = lock.tryLock(1, 10, TimeUnit.SECONDS);
                if (acquired) {
                    Long stock = redisTemplate.opsForValue().get(STOCK_KEY).block();
                    if (stock != null && stock >= quantity) {
                        redisTemplate.opsForValue().decrement(STOCK_KEY, quantity).block();
                        return true;
                    }
                }
                return false;
            } finally {
                lock.unlock();
            }
           //==核心代码结束==
        });
    }

    public Mono<Long> getStock() {
        return redisTemplate.opsForValue().get(STOCK_KEY)
                .map(stock -> stock != null ? Long.parseLong(stock) : 0L);
    }
}

StockService中,我们首先通过redissonClient.getLock方法获取一个分布式锁对象 RLock,并使用tryLock方法尝试获取锁。如果成功获取到锁,则执行库存扣减操作。在操作完成后,释放锁。通过使用分布式锁,我们确保了在并发场景下只有一个线程可以执行库存扣减操作,从而保证了原子性和一致性。

3. 使用Redis+Lua脚本实现

使用Lua脚本来实现库存扣减的原子性操作 。使用了Spring Data Redis提供的RedisTemplate来与Redis进行交互,并使用DefaultRedisScript定义了Lua脚本。通过ScriptExecutor执行Lua脚本,将库存扣减的逻辑放在脚本中实现。

decreaseStock方法中,我们 定义了Lua脚本,然后创建了一个DefaultRedisScript对象,并指定脚本返回值的类型为Boolean。接下来,我们通过scriptExecutor.execute方法执行Lua脚本,并传递脚本、键(STOCK_KEY)和参数(quantity)作为参数。

getStock方法则使用Mono.fromSupplier来获取当前库存数量,与Lua脚本无关。

3.1 lua脚本

代码逻辑

  1. 通过redis.call('GET', KEYS[1])从Redis中获取键(KEYS[1])对应的库存数量,并使用tonumber将其转换为数字类型。
  2. 检查库存是否足够进行扣减,即判断stock是否存在且大于等于传入的扣减数量(ARGV[1])。
  3. 如果库存足够,使用redis.call('DECRBY', KEYS[1], ARGV[1])扣减库存。
  4. 返回true表示扣减成功,否则返回false表示扣减失败。
-- 从Redis中获取当前库存
local stock = tonumber(redis.call('GET', KEYS[1]))

-- 检查库存是否足够扣减
if stock and stock >= tonumber(ARGV[1]) then
    -- 扣减库存
    redis.call('DECRBY', KEYS[1], ARGV[1])
    return true -- 返回扣减成功
else
    return false -- 返回扣减失败
end

3.2 与SpringBoot集成

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.script.ScriptExecutor;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.Collections;

@Service
public class StockService {
    private static final String STOCK_KEY = "stock:product123";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ScriptExecutor<String> scriptExecutor;

    public Mono<Boolean> decreaseStock(int quantity) {
        String script = "local stock = tonumber(redis.call('GET', KEYS[1]))\n" +
                "if stock and stock >= tonumber(ARGV[1]) then\n" +
                "    redis.call('DECRBY', KEYS[1], ARGV[1])\n" +
                "    return true\n" +
                "else\n" +
                "    return false\n" +
                "end";

        RedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        return scriptExecutor.execute(redisScript, Collections.singletonList(STOCK_KEY), String.valueOf(quantity));
    }

    public Mono<Long> getStock() {
        return Mono.fromSupplier(() -> {
            String stock = redisTemplate.opsForValue().get(STOCK_KEY);
            return stock != null ? Long.parseLong(stock) : 0L;
        });
    }
}

4. Lua脚本方式和Redisson的方式对比

在使用Lua脚本执行库存扣减操作时,通常不需要显式地加锁。这是因为Redis执行Lua脚本的机制保证了脚本的原子性。

当Redis执行Lua脚本时,会将整个脚本作为一个单独的命令进行执行。在执行期间,不会中断脚本的执行,也不会被其他客户端的请求打断。这使得Lua脚本在执行期间是原子的,即使在高并发的情况下也能保证操作的一致性。

因此,在上述的Lua脚本中,我们没有显式地加锁来保护库存扣减操作。通过使用Lua脚本,我们充分利用了Redis的原子性操作特性,避免了显式加锁的开销和复杂性。

需要注意的是,如果有其他并发操作也需要对库存进行扣减或修改,可能需要考虑加锁机制来保证操作的原子性。这种情况下,可以使用分布式锁来控制对库存的访问,以确保并发操作的正确性。

使用Redisson的方式和使用Lua脚本的方式在实现库存扣减时有一些不同之处。 我们做成一个表格可以清晰的对比一下,方便理解记忆,其实在项目的真正实践过程中,这两种方式也是比较常见的。但是具体使用哪一种要看大家公司的技术积累和使用偏好。
所以我们总结一下。选择适当的方式取决于具体的需求和场景。如果你需要更灵活的控制、更多的分布式功能或者对性能要求较高,那么使用Redisson库可能是一个不错的选择。而如果你希望简化实现并减少依赖,而且对性能要求不是非常高,那么使用Lua脚本可能更为合适。

方式实现复杂性灵活性性能开销分布式环境功能
Redisson库需要额外的依赖和配置,编写相关代码提供更多功能选项,如超时设置、自动续期等可能涉及网络通信和分布式锁管理的性能开销提供更丰富的分布式功能
Lua脚本无额外依赖,只需编写Lua脚本相对简单,专注于库存扣减逻辑通常具有较低延迟和较高性能专注于库存扣减操作,无其他分布式功能的支持

5. 源码地址

https://github.com/wangshuai67/Redis-Tutorial-2023

6. Redis从入门到精通系列文章

  • 《【Redis实践篇】使用Redisson 优雅实现项目实践过程中的5种场景》
  • 《Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性》
  • 《SpringBoot Redis 使用Lettuce和Jedis配置哨兵模式》
  • 《Redis【应用篇】之RedisTemplate基本操作》
  • 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
  • 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
  • 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
  • 《Redis 从入门到精通【进阶篇】之Redis事务详解》
  • 《Redis从入门到精通【进阶篇】之对象机制详解》
  • 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
  • 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
  • 《Redis从入门到精通【进阶篇】之持久化RDB详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
  • 《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
    在这里插入图片描述大家好,我是冰点,今天的Redis【实践篇】之Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

7. 参考文档

redisson 参考文档 https://redisson.org/documentation.html

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Tuxera NTFS Mac2023最新免费版Mac读写工具

有时候我们在使用苹果笔记本的时候&#xff0c;会遇到一些问题&#xff0c;比如怎么打开移动硬盘&#xff0c;或者为什么苹果电脑读不出U盘。这些问题可能让我们感到困惑和沮丧&#xff0c;但其实都有解决办法。本文就来为大家介绍一下苹果笔记本怎么打开移动硬盘和苹果电脑读不…

pdf怎么转换成jpg图片?这几个转换方法了解一下

pdf怎么转换成jpg图片&#xff1f;转换PDF文件为JPG图片格式在现代工作中是非常常见的需求&#xff0c;比如将PDF文件中的图表、表格或者图片转换为JPG格式后使用在PPT演示、网页设计等场景中。 【迅捷PDF转换器】是一款非常实用的工具&#xff0c;可以将PDF文件转换成多种不同…

mac ssh连接另一台window虚拟机vm

vmware配置端口映射 编辑(E) > 虚拟网络编辑器(N)... > NAT设置(S)... window防火墙&#xff0c;入站规则添加5555端口 控制面板 > 系统和安全 > Windows 防火墙>高级设置>入站规则>新建规则... tips windows查看端口命令&#xff1a;netstat -ano | f…

中电金信:技术实践|Flink多线程实现异构集群的动态负载均衡

导语&#xff1a;Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算。本文主要从实际案例入手并结合作者的实践经验&#xff0c;向各位读者分享当应用场景中异构集群无法做到负载均衡时&#xff0c;如何通过Flink的自定义多线程来实现异…

cloud_mall-notes01

1、登录 1.1 获取token令牌 登录时的ajax请求&#xff1a; 后端路由配置处理&#xff1a; 登录的路由配置 作用&#xff1a;把oAuth2.0颁发的token存储到redis中 package com.powernode.config;import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject;…

12 注册登录

12 注册登录 整体概述 使用数据库连接池实现服务器访问数据库的功能&#xff0c;使用POST请求完成注册和登录的校验工作。 本文内容 介绍同步实现注册登录功能&#xff0c;具体涉及到流程图、载入数据库表、提取用户名和密码、注册登录流程与页面跳转的代码实现。 流程图&a…

【vue】vue中的插槽以及使用方法

插槽 普通插槽 1、在父组件中直接调用子组件的标签&#xff0c;是可以渲染出子组件的内容&#xff1b;如果在子组件标签中添加了内容&#xff0c;父组件就渲染不出来了&#xff1b; ParentComponent.vue&#xff1a; <template><div><h1>Parent Componen…

常见分辨率时序信息

分辨率列表 分辨率一:640x480(逐行) 分辨率二:800x600(逐行) 分辨率三:1024x768(逐行) 分辨率四:大名鼎鼎720P(逐行) 注:选择720P@30帧的,需拉长HOR TOTAL TIME 分辨率五:1280x800(逐行) 分辨率六:1280x960(逐行

AWS——04篇(AWS之Amazon S3(云中可扩展存储)-02——EC2访问S3存储桶)

AWS——04篇&#xff08;AWS之Amazon S3&#xff08;云中可扩展存储&#xff09;-02——EC2访问S3存储桶&#xff09; 1. 前言2. 创建EC2实例 S3存储桶3. 创建IAM角色4. 修改EC2的IAM 角色5. 连接EC2查看效果5.1 连接EC25.2 简单测试5.2.1 查看桶内存储情况5.2.2 复制本地文件…

DIP: Spectral Bias of DIP 频谱偏置解释DIP

On Measuring and Controlling the Spectral Bias of the Deep Image Prior 文章目录 On Measuring and Controlling the Spectral Bias of the Deep Image Prior1. 方法原理1.1 动机1.2 相关概念1.3 方法原理频带一致度量与网络退化谱偏移和网络结构的关系Lipschitz-controlle…

GrapeCity Documents for PDF (GcPdf) 6.2 Crack

GrapeCity PDF 文档 (GcPdf) 改进了对由 GcPdf 以外的软件生成的现有 PDF 文档的处理 在新的 v6.2 版本中&#xff0c;GcPdf 增强了 PDF 文档的加载和保存&#xff0c;并提供以下优势&#xff1a; GcPdf 现在可以加载和保存可能不严格符合 PDF 规范的 PDF 文档。GcPdf 现在将…

Stable Diffusion + Deform制作指南

1.安装sd以及deform插件,更新后记得重启 需要安装ffmpeg https://ffmpeg.org/download.html 选择对应版本然后安装 如果是windows需要解压后将ffmpeg的bin目录配置在电脑的环境变量里面。 2.准备一张初始开始图片 3.填写参数,这里面参数要注意,宽高一定是32的倍数。如果填写…

【图像分类】理论篇 (4)图像增强opencv实现

随机旋转 随机旋转是一种图像增强技术&#xff0c;它通过将图像以随机角度进行旋转来增加数据的多样性&#xff0c;从而帮助改善模型的鲁棒性和泛化能力。这在训练深度学习模型时尤其有用&#xff0c;可以使模型更好地适应各种角度的输入。 原图像&#xff1a; 旋转后的图像&…

恒运资本:CPO概念发力走高,兆龙互联涨超10%,华是科技再创新高

CPO概念15日盘中发力走高&#xff0c;截至发稿&#xff0c;华是科技涨超15%再创新高&#xff0c;兆龙互联涨逾11%&#xff0c;中贝通讯涨停&#xff0c;永鼎股份、太辰光涨超5%&#xff0c;天孚通讯涨逾4%。 消息面上&#xff0c;光通讯闻名咨询机构LightCounting近日发布的202…

【LeetCode】《LeetCode 101》第十一章:妙用数据结构

文章目录 11.1 C STL11.2 数组448. 找到所有数组中消失的数字&#xff08;简单&#xff09;48. 旋转图像&#xff08;中等&#xff09;74. 搜索二维矩阵&#xff08;中等&#xff09;240. 搜索二维矩阵 II&#xff08;中等&#xff09;769. 最多能完成排序的块&#xff08;中等…

ChatGPT or BingChat

你相信我们对大模型也存在「迷信权威」吗&#xff1f; ChatGPT 的 GPT-4 名声在外&#xff0c;我们就不自觉地更相信它&#xff0c;优先使用它。但我用 ChatALL 比较 AI 大模型们这么久&#xff0c;得到的结论是&#xff1a; ChatGPT GPT-4 在大多数情况下确实是最强&#xf…

Element组件浅尝辄止4:Button组件

Button按钮组件&#xff1a;用途太广泛了&#xff0c;几乎参与到了日常开发中的方方面面 1.如何使用&#xff1f;How? //使用type、plain、round和circle属性来定义 Button 的样式。<el-row><el-button>默认按钮</el-button><el-button type"primar…

阿里云ACP知识点

前言&#xff1a;记录ACP错题 1、在创建阿里云ECS时&#xff0c;每台服务器必须要包含_______用来存储操作系统和核心配置。 系统盘&#xff08;不是实例&#xff0c;实例是一个虚拟的计算环境&#xff0c;由CPU、内存、系统盘和运行的操作系统组成&#xff1b;ESC实例作为云…

【RabbitMQ与SpringBoot集成测试收发消息】

【RabbitMQ与SpringBoot集成测试收发消息】 一、环境说明二、实验步骤三、小结 一、环境说明 安装环境&#xff1a;虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8RabbitMQ版本&#xff1a;rabbitmq-server-3.8.8-1.el7.noarch.rpm编程工具Idea 运行JDK为17 二、实验步骤 在Rab…

九州未来参与编制的开源领域3项团体标准获批发布

日前&#xff0c;中电标2023年第21号团体标准公告正式发布&#xff0c;其中由九州未来参与编制的3项开源领域团体标准正式获批发布&#xff0c;于2023年8月1日正式实施。 具体内容如下&#xff1a; 《T/CESA 1269-2023 信息技术 开源 术语与综述》&#xff0c;本文件界定了信息…