Redis RedLock算法和底层源码分析

Redlock红锁算法

官网地址:Distributed Locks with Redis | Redis

为什么要使用RedLock?

解释:

        线程 1 首先获取锁成功,将键值对写入 redis 的 master 节点,在 redis 将该键值对同步到 slave 节点之前,master 发生了故障;redis 触发故障转移,其中一个 slave 升级为新的 master,此时新上位的master并不包含线程1写入的键值对,因此线程 2 尝试获取锁也可以成功拿到锁,此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据。 我们加的是排它独占锁,同一时间只能有一个建redis锁成功并持有锁,严禁出现2个以上的请求线程拿到锁。危险的。

RedLock算法设计理念

Redis也提供了Redlock算法,用来实现基于多个实例的分布式锁。

锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作。Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用。最下方还有笔记。

翻译:

设计理念

该方案也是基于(set 加锁、Lua 脚本解锁)进行改良的,所以redis之父antirez 只描述了差异的地方,大致方案如下:

        假设我们有N个Redis主节点,例如 N = 5这些节点是完全独立的,我们不使用复制或任何其他隐式协调系统,为了取到锁客户端执行以下操作:

  1. 获取当前时间,以毫秒为单位;
  2. 依次尝试从5个实例,使用相同的 key 和随机值(例如 UUID)获取锁。当向Redis 请求获取锁时,客户端应该设置一个超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为 10 秒,则超时时间应该在 5-50 毫秒之间。这样可以防止客户端在试图与一个宕机的 Redis 节点对话时长时间处于阻塞状态。如果一个实例不可用,客户端应该尽快尝试去另外一个 Redis 实例请求获取锁;
  3. 客户端通过当前时间减去步骤 1 记录的时间来计算获取锁使用的时间。当且仅当从大多数(N/2+1,这里是 3 个节点)的 Redis 节点都取到锁,并且获取锁使用的时间小于锁失效时间时,锁才算获取成功;
  4. 如果取到了锁,其真正有效时间等于初始有效时间减去获取锁所使用的时间(步骤 3 计算的结果)。
  5. 如果由于某些原因未能获得锁(无法在至少 N/2 + 1 个 Redis 实例获取锁、或获取锁的时间超过了有效时间),客户端应该在所有的 Redis 实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。

该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用 master 节点,同时由于舍弃了 slave,为了保证可用性,引入了 N 个节点,官方建议是 5。

客户端只有在满足下面的这两个条件时,才能认为是加锁成功:

条件1:客户端从超过半数(大于等于N/2+1)的Redis实例上成功获取到了锁;

条件2:客户端获取锁的总耗时没有超过锁的有效时间。

解决方案

为什么是奇数?  N = 2X + 1   (N是最终部署机器数,X是容错机器数)

1、先知道什么是容错

失败了多少个机器实例后我还是可以容忍的,所谓的容忍就是数据一致性还是可以Ok的,CP数据一致性还是可以满足。

加入在集群环境中,redis失败1台,可接受。2X+1 = 2 * 1+1 =3,部署3台,死了1个剩下2个可以正常工作,那就部署3台。

加入在集群环境中,redis失败2台,可接受。2X+1 = 2 * 2+1 =5,部署5台,死了2个剩下3个可以正常工作,那就部署5台。

2、为什么是奇数?

最少的机器,最多的产出效果

加入在集群环境中,redis失败1台,可接受。2N+2= 2 * 1+2 =4,部署4台

加入在集群环境中,redis失败2台,可接受。2N+2 = 2 * 2+2 =6,部署6台

Redisson

Redisson就是RedLock的实现。

Redisson是java的redis客户端之一,提供了一些api方便操作redis。

Redisson官网:Redisson: Easy Redis Java client with features of In-Memory Data Grid

Redisson之Github:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

Redisson之解缺分布式锁:https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers

Redisson使用

根据之前案例V8.0版本进行改造,详细可查阅:Redis 分布式锁_Please Sit Down的博客-CSDN博客

如何使用

官网:81-可重入锁reentrant-lock

V9.0 版本(单机版)

pom.xml

<!--redisson-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.4</version>
</dependency>

config配置

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);//设置key序列化方式stringredisTemplate.setKeySerializer(new StringRedisSerializer());//设置value的序列化方式jsonredisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}//单Redis节点模式@Beanpublic Redisson redisson() {Config config = new Config();config.useSingleServer().setAddress("redis://192.168.111.175:6379").setDatabase(0).setPassword("111111");return (Redisson) Redisson.create(config);}
}

controller

import com.atguigu.redislock.service.InventoryService;
import com.atguigu.redislock.service.InventoryService2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@Api(tags = "redis分布式锁测试")
public class InventoryController {@Autowiredprivate InventoryService inventoryService;@ApiOperation("扣减库存saleByRedisson,一次卖一个")@GetMapping(value = "/inventory/saleByRedisson")public String saleByRedisson() {return inventoryService.saleByRedisson();}
}

service

import cn.hutool.core.util.IdUtil;
import com.atguigu.redislock.mylock.DistributedLockFactory;
import com.atguigu.redislock.mylock.RedisDistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;@Service
@Slf4j
public class InventoryService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Value("${server.port}")private String port;@Autowiredprivate DistributedLockFactory distributedLockFactory;@Autowiredprivate Redisson redisson;public String saleByRedisson() {String retMessage = "";String key = "zzyyRedisLock";RLock redissonLock = redisson.getLock(key);redissonLock.lock();try {//1 查询库存信息String result = stringRedisTemplate.opsForValue().get("inventory001");//2 判断库存是否足够Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);//3 扣减库存if(inventoryNumber > 0) {stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));retMessage = "成功卖出一个商品,库存剩余: "+inventoryNumber;System.out.println(retMessage);}else{retMessage = "商品卖完了,o(╥﹏╥)o";}}finally {redissonLock.unlock();}return retMessage+"\t"+"服务端口号:"+port;}
}

测试

使用Jmeter出现BUG

V9.1 版本(单机版)

解锁操作需要判断是否为自己的锁,不能解其他人的锁。这里使用if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) 判断,Redisson自动会给我们判断是否为自己的锁。

import cn.hutool.core.util.IdUtil;
import com.atguigu.redislock.mylock.DistributedLockFactory;
import com.atguigu.redislock.mylock.RedisDistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

@Service
@Slf4j

public class InventoryService {
    @Autowired
    
private StringRedisTemplate stringRedisTemplate;
    @Value("${server.port}")
    private String port;
    @Autowired
    
private DistributedLockFactory distributedLockFactory;

    @Autowired
    
private Redisson redisson;
    public String saleByRedisson() {
        String retMessage = "";
        String key = "zzyyRedisLock";
        RLock redissonLock = redisson.getLock(key);
        redissonLock.lock();
        try {
            //1 查询库存信息
            
String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2 判断库存是否足够
            
Integer inventoryNumber = result == null : Integer.parseInt(result);
            //3 扣减库存
            
if(inventoryNumber > 0) {
                stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
                retMessage = "成功卖出一个商品,库存剩余: "+inventoryNumber;
                System.out.println(retMessage);
            }else{
                retMessage = "商品卖完了,o(╥╥)o";
            }
        }finally {
            if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
                redissonLock.unlock();
            }

        }
        return retMessage+"\t"+"服务端口号:"+port;
    }
}
 

Redisson源码解析

Redisson也实现了之前版本中的缓存续命功能,Redisson中是使用守护线程进行续命

额外起一个线程,定期检查线程是否还持有锁,如果有则延长过期时间。

Redisson 里面就实现了这个方案,使用“看门狗”定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间;

在获取锁成功后,给锁加一个watchdog,watchdog会起一个定时任务,在锁没有被释放且快要过期的时候会续期。

源码1:通过redisson新建出来的锁key,默认是30秒

源码2

源码位置:RedissonLock.java -> lock() -> tryAcquire() -> tryAcquireAsync()

源码3

流程解释:

1、通过exists判断,如果锁不存在,则设置值和过明时间,加锁成功
2、通过hexists判断,如果锁已存在,并目锁的是当前线程,则证明是重入锁,加锁成功
3、如果锁已存在,但锁的不是当前线程,则证明有其他线程持有锁。返回当前锁的过期时间(代表了锁key的剩余生存时间),加锁失败

源码4

这里面初始化了一个定时器,dely 的时间是 internalLockLeaseTime/3。在 Redisson 中,internalLockLeaseTime 是 30s,也就是每隔 10s 续期一次,每次 30s。

解释:

watch dog自动延期机制

        客户端A加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端A还持有锁key,那么就会不断的延长锁key的生存时间,默认每次续命又从30秒新开始。


自动续期lua脚本分析

源码5:

解锁lua脚本解释

多机使用Redisson案例

理论来源

这个锁的算法实现了多redis实例的情况,相对于单redis节点来说,优点在于 防止了 单节点故障造成整个服务停止运行的情况且在多节点中锁的设计,及多节点同时崩溃等各种意外情况有自己独特的设计方法。

Redisson 分布式锁支持 MultiLock 机制可以将多个锁合并为一个大锁,对一个大锁进行统一的申请加锁以及释放锁。

最低保证分布式锁的有效性及安全性的要求如下:

1.互斥;任何时刻只能有一个client获取锁

2.释放死锁;即使锁定资源的服务崩溃或者分区,仍然能释放锁

3.容错性;只要多数redis节点(一半以上)在使用,client就可以获取和释放锁        

网上讲的基于故障转移实现的redis主从无法真正实现Redlock:

因为redis在进行主从复制时是异步完成的,比如在clientA获取锁后,主redis复制数据到从redis过程中崩溃了,导致没有复制到从redis中,然后从redis选举出一个升级为主redis,造成新的主redis没有clientA 设置的锁,这是clientB尝试获取锁,并且能够成功获取锁,导致互斥失效;

代码来源

https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers

备注:目前RedLock已被遗弃,换用MultiLock

案例

1、启动3台Redis主机实例(都是主节点,使用docker)

docker run -p 6381:6379 --name redis-master-1 -d redis

docker run -p 6382:6379 --name redis-master-2 -d redis

docker run -p 6383:6379 --name redis-master-3 -d redis

进入实例命令

docker exec -it redis-master-1 /bin/bash   或者 docker exec -it redis-master-1 redis-cli

docker exec -it redis-master-2 /bin/bash   或者 docker exec -it redis-master-2 redis-cli

docker exec -it redis-master-3 /bin/bash   或者 docker exec -it redis-master-3 redis-cli

2、pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.10.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atguigu.redis.redlock</groupId><artifactId>redis_redlock</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.19.1</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><!--swagger-ui--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version><scope>compile</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.11</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

3、yml

server.port=9090
spring.application.name=redlock

spring.swagger2.enabled=true

spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
spring.redis.mode=single

spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10

spring.redis.single.address1=192.168.111.185:6381
spring.redis.single.address2
=192.168.111.185:6382
spring.redis.single.address3
=192.168.111.185:6383

4、配置文件

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {@AutowiredRedisProperties redisProperties;@BeanRedissonClient redissonClient1() {Config config = new Config();String node = redisProperties.getSingle().getAddress1();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}@BeanRedissonClient redissonClient2() {Config config = new Config();String node = redisProperties.getSingle().getAddress2();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}@BeanRedissonClient redissonClient3() {Config config = new Config();String node = redisProperties.getSingle().getAddress3();node = node.startsWith("redis://") ? node : "redis://" + node;SingleServerConfig serverConfig = config.useSingleServer().setAddress(node).setTimeout(redisProperties.getPool().getConnTimeout()).setConnectionPoolSize(redisProperties.getPool().getSize()).setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());if (StringUtils.isNotBlank(redisProperties.getPassword())) {serverConfig.setPassword(redisProperties.getPassword());}return Redisson.create(config);}}
import lombok.Data;@Data
public class RedisPoolProperties {private int maxIdle;private int minIdle;private int maxActive;private int maxWait;private int connTimeout;private int soTimeout;// 池大小private  int size;
}
import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "spring.redis", ignoreUnknownFields = false)
@Data
public class RedisProperties {private int database;/*** 等待节点回复命令的时间。该时间从命令发送成功时开始计时*/private int timeout;private String password;private String mode;/*** 池配置*/private RedisPoolProperties pool;/*** 单机信息配置*/private RedisSingleProperties single;
}
import lombok.Data;@Data
public class RedisSingleProperties {private  String address1;private  String address2;private  String address3;
}

5、controller

import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;@RestController
@Slf4j
public class RedLockController {public static final String CACHE_KEY_REDLOCK = "ATGUIGU_REDLOCK";@AutowiredRedissonClient redissonClient1;@AutowiredRedissonClient redissonClient2;@AutowiredRedissonClient redissonClient3;boolean isLockBoolean;@GetMapping(value = "/multiLock")public String getMultiLock() throws InterruptedException {String uuid =  IdUtil.simpleUUID();String uuidValue = uuid+":"+Thread.currentThread().getId();RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);RedissonMultiLock redLock = new RedissonMultiLock(lock1, lock2, lock3);redLock.lock();try {System.out.println(uuidValue+"\t"+"---come in biz multiLock");try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(uuidValue+"\t"+"---task is over multiLock");} catch (Exception e) {e.printStackTrace();log.error("multiLock exception ",e);} finally {redLock.unlock();log.info("释放分布式锁成功key:{}", CACHE_KEY_REDLOCK);}return "multiLock task is over  "+uuidValue;}}

测试

测试地址:http://localhost:9090/multilock

测试命令:

ttl ATGUIGU REDLOCK
HGETALL ATGUIGU_REDLOCK
shutdown
docker start redis-master-1
docker exec -it redis-master-1 redis-cli

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

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

相关文章

使用 CSS 伪类的attr() 展示 tooltip

效果图: 使用场景: 使用React渲染后台返回的数据, 遍历以列表的形式展示, 可能简要字段内容需要鼠标放上去才显示的 可以借助DOM的自定义属性和CSS伪类的attr来实现 所有代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-…

Navicat导入Excel数据顺序变了

项目场景&#xff1a; Navicat导入Excel数据 问题描述 从Excel表格中导入数据到数据库中。但是&#xff0c;在导入的过程中&#xff0c;我们常会发现数据顺序出现了问题&#xff0c;导致数据错位&#xff0c;给数据的处理带来了极大的麻烦。 原因分析&#xff1a; 这个问题的…

vue2.X 中使用 echarts5.4.0实现项目进度甘特图

vue2.X 中使用 echarts5.4.0实现项目进度甘特图 效果图&#xff1a; 左侧都是名称&#xff0c;上面是时间&#xff0c;当中的内容是日志内容 组件&#xff1a; gantt.vue <template><div id"main" style"width: 100%; height: 100%"></…

Ansys Zemax | 用于照明设计中的光源

本课程提供照明系统中光源的介绍&#xff0c;作为照明系统光源的信息中心。本课是照明学习路径的第二课。在这一课中&#xff0c;将描述照明系统中的各种光源类型以及如何这些使用光源。光源是照明系统的起点和支点&#xff0c;可以说是照明设计中最关键的部分。 简介&#xff…

华为云云耀云服务器L实例评测|基于云耀云服务器部署Samba服务

本实验将使用华为云云耀云服务器L实例&#xff0c;使用CentOS 7.9系统&#xff0c;搭建部署Samba服务器&#xff0c;并在本地Windows端进行访问。 文章目录 1、samba介绍2、环境准备3、安装samba软件包4、修改smb.conf配置文件5、添加访问samba的用户6、Windows下访问Samba服务…

使用 multiprocessing 多进程处理批量数据

示例代码 import multiprocessingdef process_data(data):# 这里是处理单个数据的过程return data * 2# 待处理的数据 data [1, 2, 3, 4, 5]def normal_func():# 普通处理方式result []for obj in data:result.append(process_data(obj)return resultdef parallel_func():# …

【FAQ】视频监控管理平台/视频汇聚平台EasyCVR安全检查相关问题及解决方法3.0

智能视频监控系统/视频云存储/集中存储/视频汇聚平台EasyCVR具备视频融合汇聚能力&#xff0c;作为安防视频监控综合管理平台&#xff0c;它支持多协议接入、多格式视频流分发&#xff0c;视频监控综合管理平台EasyCVR支持海量视频汇聚管理&#xff0c;可应用在多样化的场景上&…

Flink、Spark、Hive集成Hudi

环境描述: hudi版本:0.13.1 flink版本:flink-1.15.2 spark版本:3.3.2 Hive版本:3.1.3 Hadoop版本:3.3.4 一.Flink集成Hive 1.拷贝hadoop包到Flink lib目录 hadoop-client-api-3.3.4.jar hadoop-client-runtime-3.3.4.jar 2.下载上传flink-hive的jar包 flink-co…

算法 数据结构 递归冒泡算法 java冒泡算法 优化递归冒泡 数据结构(九)

使用递归算法实现冒泡&#xff1a; package com.nami.algorithm.study.day06;import java.util.Arrays;/*** beyond u self and trust u self.** Author: lbc* Date: 2023-09-05 15:36* email: 594599620qq.com* Description: keep coding*/ public class BubbleSort2 {// p…

大数据-玩转数据-Flink状态后端(下)

一、状态后端 每传入一条数据&#xff0c;有状态的算子任务都会读取和更新状态。由于有效的状态访问对于处理数据的低延迟至关重要&#xff0c;因此每个并行任务(子任务)都会在本地维护其状态&#xff0c;以确保快速的状态访问。 状态的存储、访问以及维护&#xff0c;由一个…

【深入解读Redis系列】(五)Redis中String的认知误区,详解String数据类型

有时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步&#xff0c;请认准https://blog.zysicyj.top 首发博客地址 系列文章地址 需求描述 现在假设有这样一个需求&#xff0c;我们要开发一个图像存储系统。要求如下&#xff1a; 该系统能快…

PbootCMS在搭建网站

1、打开网站 https://www.pbootcms.com/ 2、点击 “本站” 下载最新的网站代码 3、在本地laragon/www下创建目录&#xff08;hejuwuye&#xff09;&#xff0c;并将代码放进去 4、创建本地数据库&#xff0c;数据库名称为&#xff1a; hejuwuye&#xff0c;然后将static/bac…

快速傅里叶变换

引言 目标 傅里叶变化&#xff08;Fourier transform&#xff09;是一种信号处理技术&#xff0c;它可以将时间信号转换为频率信号&#xff0c;即将一组具有相同数量频率的正弦波叠加在一起&#xff0c;形成一组新的正弦波。如果我们把时间信号从频域转换到时域&#xff0c;那么…

SLAM ORB-SLAM2(1)总体框架

SLAM ORB-SLAM2(1)总体框架 1. 简介2. 框架3. TRACKING4. LOCAL MAPPING5. LOOP CLOSING6. MAP1. 简介 ORB-SLAM2 是一个实时和完整的视觉SLAM系统(包括闭环检测、重定位、地图重用等功能) 提供了利用单目、双目以及RGB-D相机完成稀疏三维重建的功能和接口 2. 框架 总体来说…

python项目制作docker镜像,加装引用模块,部署运行!

一、创建Dockerfile # 基于python:3.10.4版本创建容器 FROM python:3.10.4 # 在容器中创建工作目录 RUN mkdir /app # 将当前Dockerfile目录下的所有文件夹和文件拷贝到容器/app目录下 COPY . /app# 由于python程序用到了requests模块和yaml模块&#xff0c; # python:3.10.4基…

二叉树进阶练习

目录 一、根据二叉树创建字符串 二、二叉树的最近公共祖先 三、二叉搜索树与双向链表 四、从前序与中序遍历序列构造二叉树 五、从中序与后序遍历序列构造二叉树 六、二叉树的前序遍历&#xff08;非递归实现&#xff09; 七、二叉树的中序遍历&#xff08;非递归实现&a…

紫光展锐5G芯T820 解锁全新应用场景,让机器人更智能

数字经济的持续发展正推动机器人产业成为风口赛道。工信部数据显示&#xff0c;2023年上半年&#xff0c;我国工业机器人产量达22.2万套&#xff0c;同比增长5.4%&#xff1b;服务机器人产量为353万套&#xff0c;同比增长9.6%。 作为国内商用服务机器人领先企业&#xff0c;云…

应用在儿童平板防蓝光中的LED防蓝光灯珠

现在电子产品多&#xff0c;手机、平板电脑、电子书等等&#xff0c;由于蓝光有害眼睛健康&#xff0c;于是市场上有很多防蓝光的眼镜、防蓝光的手机膜、防蓝光的平板&#xff0c;这些材料和设备到底有没有用&#xff1f;如何正确预防蓝光危害呢&#xff1f; 我们现在所用的灯…

NCTF-2019-Crypto部分 复现

文章目录 SorechildRSAeasyRSAbabyRSA Sore 题目描述&#xff1a; task.py from string import ascii_letters from flag import flagctoi lambda x: ascii_letters.index(x) # 获得所有字母的字符串 itoc lambda x: ascii_letters[x] # 将索引值转换为字母key flag.strip…

关于 Resolution(分辨率、解析力)各单位的意义及相互之间的换算

1、问题背景 最近在调试的项目&#xff0c;有关于对解析力的要求&#xff0c;用 imatest 软件测试 MTF50 的值&#xff0c;如下图所示&#xff0c;可以看到他有不同的单位表示&#xff0c;LW/PH、Cycles/pixel 。另外关于解析力的单位还有LP/mm、L/mm、Cycles/mm、LP/PH&#…