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

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 {@Resourceprivate 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: nulldatabase: 0connectionPoolSize: 64connectionMinimumIdleSize: 10subscriptionConnectionPoolSize: 50dnsMonitoringInterval: 5000lockWatchdogTimeout: 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";@Autowiredprivate ReactiveRedisTemplate<String, String> redisTemplate;@Autowiredprivate 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 -- 返回扣减成功
elsereturn 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";@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Autowiredprivate 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

相关文章:

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

文章目录 前言1.使用SpringBoot Redis 原生实现方式2.使用redisson方式实现3. 使用RedisLua脚本实现3.1 lua脚本代码逻辑 3.2 与SpringBoot集成 4. Lua脚本方式和Redisson的方式对比5. 源码地址6. Redis从入门到精通系列文章7. 参考文档 前言 背景&#xff1a;最近有社群技术交…...

【从零开始学Kaggle竞赛】泰坦尼克之灾

目录 0.准备1.问题分析挑战流程数据集介绍结果提交 2.代码实现2.1 加载数据2.1.1 加载训练数据2.1.2 加载测试数据 2.2 数据分析2.3 模型建立与预测 3.结果提交 0.准备 注册kaggle账号后&#xff0c;进入titanic竞赛界面 https://www.kaggle.com/competitions/titanic 进入后界…...

输出无重复的3位数和计算无人机飞行坐标

编程题总结 题目一&#xff1a;输出无重复的3位数 题目描述 从{1,2,3,4,5,6,7,8,9}中随机挑选不重复的5个数字作为输入数组‘selectedDigits’&#xff0c;能组成多少个互不相同且无重复数字的3位数?请编写程》序&#xff0c;从小到大顺序&#xff0c;以数组形式输出这些3位…...

muduo 29 异步日志

目录 Muduo双缓冲异步日志模型: 异步日志实现: 为什么要实现非阻塞的日志...

Qt 对象序列化/反序列化

阅读本文大概需要 3 分钟 背景 日常开发过程中&#xff0c;避免不了对象序列化和反序列化&#xff0c;如果你使用 Qt 进行开发&#xff0c;那么有一种方法实现起来非常简单和容易。 实现 我们知道 Qt 的元对象系统非常强大&#xff0c;基于此属性我们可以实现对象的序列化和…...

从零学算法(非官方题库)

输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A:3/ \4 5/ \1 2给定的树 B&#xff1a;4 / 1返回 true&#xff0c;因为 B 与 A 的一个子树拥有相…...

Java # JVM内存管理

一、运行时数据区域 程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池、直接内存 二、HotSpot虚拟机对象 对象创建&#xff1a; 引用检查类加载检查分配内存空间&#xff1a;指针碰撞、空闲列表分配空间初始化对象信息设置&#xff08;对象头内&#xff0…...

大疆第二批笔试复盘

大疆笔试复盘(8-14) 笔试时候的状态和下来复盘的感觉完全不一样,笔试时脑子是懵的。 (1)输出无重复三位数 题目描述 从 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } \left \{ 1,2,3,4,5,6,7,8,9 \right \...

【Linux】磁盘或内存 占用比较高要怎么排

当 Linux 磁盘空间满了时 请注意&#xff0c;在进行任何删除操作之前&#xff0c;请确保你知道哪些文件可以安全删除&#xff0c;并备份重要文件&#xff0c;以免意外丢失数据。当 Linux 磁盘空间满了时&#xff0c;可以按照以下步骤进行排查&#xff1a; 检查磁盘使用情况&…...

解决xss转义导致转码的问题

一、xss简介 人们经常将跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;缩写为CSS&#xff0c;但这会与层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;的缩写混淆。因此&#xff0c;有人将跨站脚本攻击缩写为XSS。跨站脚本攻击&#xff…...

numba 入门示例

一维向量求和&#xff1a; C A B 在有nv 近几年gpu的ubuntu 机器上&#xff0c; 环境预备&#xff1a; conda create -name numba_cuda_python3.10 python3.10 conda activate numba_cuda_python3.10conda install numba conda install cudatoolkit conda install -c nvi…...

BUUCTF 还原大师 1

题目描述&#xff1a; 我们得到了一串神秘字符串&#xff1a;TASC?O3RJMV?WDJKX?ZM,问号部分是未知大写字母&#xff0c;为了确定这个神秘字符串&#xff0c;我们通过了其他途径获得了这个字串的32位MD5码。但是我们获得它的32位MD5码也是残缺不全&#xff0c;E903???4D…...

自定义hook之首页数据请求动作封装 hooks

本例子实现了自定义hook之首页数据请求动作封装 hooks&#xff0c;具体代码如下 export type OrganData {dis: Array<{ disease: string; id: number }>;is_delete: number;name: string;organ_id: number;parent_id: number;sort: number; }; export type SwiperData …...

2023上半年京东手机行业品牌销售排行榜(京东数据平台)

后疫情时代&#xff0c;不少行业都迎来消费复苏&#xff0c;我国智能手机市场在今年上半年也实现温和的复苏&#xff0c;手机市场的出货量回暖。 根据鲸参谋平台的数据显示&#xff0c;2023年上半年&#xff0c;京东平台上手机的销量为2830万&#xff0c;环比增长约4%&#xf…...

lodash之cloneDeep()源码阅读笔记

lodash之cloneDeep()源码阅读笔记 基本上都在写业务代码&#xff0c;没有机会写库&#xff0c;还是想了解一下lodash的库源码是怎么样的&#xff0c;平时用的最多的就是cloneDeep()方法了&#xff0c;终于有空详细看看其中的源码。 本文基于lodash5.0.0版本的源码进行阅读。 /…...

算法模版,今天开始背

二分查找算法 int left_bound(int[] nums, int target) {int left 0, right nums.length - 1;// 搜索区间为 [left, right]while (left < right) {int mid left (right - left) / 2;if (nums[mid] < target) {// 搜索区间变为 [mid1, right]left mid 1;} else if …...

新的 Python URL 解析漏洞可能导致命令执行攻击

Python URL 解析函数中的一个高严重性安全漏洞已被披露&#xff0c;该漏洞可绕过 blocklist 实现的域或协议过滤方法&#xff0c;导致任意文件读取和命令执行。 CERT 协调中心&#xff08;CERT/CC&#xff09;在周五的一份公告中说&#xff1a;当整个 URL 都以空白字符开头时&…...

react项目做的h5页面加载缓慢优化(3s优化到0.6s)

打包到生产环境时去掉SOURCEMAP 禁用生成 Source Map 是一种权衡&#xff0c;可以根据项目的实际需求和优化目标来决定是否禁用。如果您对调试需求不是特别强烈&#xff0c;可以考虑在生产构建中禁用 Source Map 以获取更好的性能。但如果需要保留调试能力&#xff0c;可以在生…...

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…...

UI设计师个人工作感悟5篇

UI设计师个人工作感悟一 工作一年了&#xff0c;结合我自身谈谈UI设计的重要性。现在主流的论坛建站程序有两种 Phpwind 和Discuz(Phpwind被阿里巴巴收购 Discuz被腾讯收购这两个论坛程序都是开源免费的)&#xff0c;利用这两种程序我都分别建立过论坛&#xff0c;我第一次用的…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...