分布式锁实现方案--redis、zookeeper、mysql

news2024/11/15 16:23:12

分布式锁的几种实现方式

  • 悲观锁和乐观锁
  • 分布式锁的实现要求 -- 互斥性、避免死锁、可重入性、高可用行、性能
    • 互斥-只能有一个客户端持有锁 -- redis setnx
    • 避免死锁
      • 引入过期时间 -- redis ttl
        • 锁过期问题 -- 自动续期 -- redis
        • 释放别人锁问题 -- 锁附加唯一性 -- 给每个客户端设置唯一ID?
      • 可重入 redis+lua计数器
    • 容错
  • 主从一致性
  • Redis分布式缓存实现--Redisson
    • 使用代码
      • 1. Maven 依赖导入
      • 2. 配置文件参数配置(需要根据你的情况进行修改)
      • 3. 创建 Redisson 客户端
      • 4. 使用分布式锁
      • 方法说明
        • 1. RLock.lock()
        • 2. RLock.unlock()
    • 互斥性 -- SET lKey randId NX PX 30000
    • 避免死锁
    • 可重入性
    • 性能
    • 高可用性(主从一致性)
      • 普通模式(前述方式)锁的问题
      • 高可用环境下(sentinel或redis cluster)的锁实现--RedLock
        • RedLock实现思路
        • RedLock使用代码
        • Redisson实现源码
    • Redis分布式锁参考资料
  • zookeeper实现分布式锁
    • 基于zookeeper的分布式锁实现 --- Curator
    • 互斥性+避免死锁
    • 避免死锁
    • 可重入性
    • 高可用行
    • 性能
    • 参考

悲观锁和乐观锁

分布式锁的实现要求 – 互斥性、避免死锁、可重入性、高可用行、性能

分布式锁应该具有:

互斥性:任意时刻只能有一个客户端持有锁
锁超时释放: 锁超时会自动释放,防止死锁
可重入性: 一个线程获取锁之后可以再次对请求加锁
高可用、高性能:加锁和解锁需要开销尽可能低,同时要保证高可用
安全性:锁只能被持有客户端删除,不能被其他客户端删除

互斥-只能有一个客户端持有锁 – redis setnx

避免死锁

引入过期时间 – redis ttl

比如TTL为5秒,进程A获得锁
问题是5秒内进程A并未释放锁,被系统自动释放,进程B获得锁
刚好第6秒时进程A执行完,又会释放锁,也就是进程A释放了进程B的锁
仅仅加个过期时间会设计到两个问题:锁过期和释放别人的锁问题

锁过期问题 – 自动续期 – redis

锁过期问题的出现,是我们对持有锁的时间不好进行预估,设置较短的话会有【提前过期】风险,但是过期时间设置过长,可能锁长时间得不到释放。

这种情况同样有处理方式,可以开启一个守护进程(watch dog),检测失效时间进行续租,比如Java技术栈可以用Redisson来处理。

释放别人锁问题 – 锁附加唯一性 – 给每个客户端设置唯一ID?

锁key的值附加唯一性:针对释放别人锁这种问题,我们可以给每个客户端进程设置【唯一ID】,作为锁(所有客户端锁key相同)的值,这样我们就可以在应用层就进行检查唯一ID。

可重入 redis+lua计数器

一个线程获取了锁,但是在执行时,又再次尝试获取锁会发生什么情况?

是的,导致了重复获取锁,占用了锁资源,造成了死锁问题。

我们了解下什么是【可重入】:指的是同一个线程在持有锁的情况下,可以多次获取该锁而不会造成死锁,也就是一个线程可以在获取锁之后再次获取同一个锁,而不需要等待锁释放。

解决方式:比如实现Redis分布式锁的可重入,在实现时,需要借助Redis的Lua脚本语言,并使用引用计数器技术,保证同一线程可重入锁的正确性。

容错

容错性是为了当部分节点(redis节点等)宕机时,客户端仍然能够获取锁和释放锁,一般来说会有以下两种处理方式:

  1. 一种像etcd/zookeeper这种作为锁服务能够自动进行故障切换,因为它本身就是个集群
  2. 另一种可以提供多个独立的锁服务,客户端向多个独立锁服务进行请求,某个锁服务故障时,也可以从其他服务获取到锁信息,但是这种缺点很明显,客户端需要去请求多个锁服务。

主从一致性

redis集群(包括redis

Redis分布式缓存实现–Redisson

使用代码

1. Maven 依赖导入

在这里插入图片描述

2. 配置文件参数配置(需要根据你的情况进行修改)

在这里插入图片描述

3. 创建 Redisson 客户端

在这里插入图片描述

4. 使用分布式锁

在需要使用分布式锁的地方注入RedissonClient实例,并使用getLock方法创建一个分布式锁对象(RLock)。
在这里插入图片描述

方法说明

1. RLock.lock()

使用 Rlock.lock() 方法时 ,如果当前没有其他线程或进程持有该锁,那么调用线程将立即获得锁定,并继续执行后续的代码。如果其他线程或进程已经持有了该锁,那么调用线程将被阻塞,直到该锁被释放为止。

此外,Rlock.lock() 方法还具有以下特点:

  • 可重入性: 同一个线程可以多次调用 Rlock.lock() 方法而不会造成死锁,只需确保每次 lock() 调用都有相应的 unlock() 调用与之匹配。
  • 超时机制: 可以通过 lock() 方法中的参数设置等待锁的超时时间,避免因为无法获得锁而一直等待。
  • 自动续期: 当线程持有锁的时间超过设置的锁的过期时间时,Redisson 会自动延长锁的有效期,避免因为业务执行时间过长而导致锁过期。
  • 防止死锁: Redisson 通过唯一标识锁的 ID 来区分不同的锁,防止发生死锁。
2. RLock.unlock()

RLock.unlock()方法用于释放由Redission分布式锁所保护的资源。它允许持有锁的线程主动释放锁,从而允许其他线程获取该锁并访问共享资源。

注意事项:

  • RLock.unlock()方法应该在保护的临界区代码执行完毕后进行调用,以确保锁的及时释放。
  • 在多线程环境下,释放锁的顺序应该与获取锁的顺序相对应,以避免死锁或资源争用的问题。
  • 如果当前线程没有持有锁,调用RLock.unlock()方法不会抛出异常,也不会影响其他线程。
  • 如果Redisson客户端刚加锁成功,并且未指定leaseTime,后台会启动一个定时任务watchdog, 每隔10s检查key,key如果存在就为它⾃动续命到30s后;在watchdog定时任务存在的情况下,如果不是主动释放锁,那么key将会⼀直的被watchdog这个定时任务维持加锁。但是如果客户端宕机了,定时任务watchdog也就没了,也就没有锁续约机制了,那么过完30s之后,key会⾃动被删除、key对应的锁也自动被释放了。

互斥性 – SET lKey randId NX PX 30000

不能用setnx lkey lvalue expire lockKey 30

//保证原子性执行命令
SET lKey randId NX PX 30000

Redission实现-lua脚本:
redis锁数据结构(数据类型为HSet):<lockKey, <线程key, 可重入计数器>>
线程key=GUID+当前线程ID,value为可重入计数器

解决了以下问题:

  1. 互斥性:用lua脚本保证操作的原子性进而保证了互斥性
  2. 锁过期问题:设置超期时间+看门狗:看门狗默认将锁的过期时间设置为30秒,并且每10秒重新设置过期时间为30秒,直到线程终止后看门狗才不再运行。
  3. 释放别人锁(线程key唯一标识当前线程,释放锁时,先判断redis中存储的线程key是不是当前线程,若是才释放锁)
  4. 可重入性:用value作为可重用计数器,每次重新加(解)锁,可重入计数器便加(减)1
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return commandExecutor.evalWriteAsync(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.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }
  

避免死锁

见上方解释。

可重入性

见上方

性能

最高,高于zookeeper

高可用性(主从一致性)

普通模式(前述方式)锁的问题

事实上上面的实现琐的方式的最大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:

  1. 在Redis的master节点上拿到了锁;
  2. 但是这个加锁的key还没有同步到slave节点;
  3. master故障,发生故障转移,slave节点升级为master节点;
  4. 导致锁丢失。

高可用环境下(sentinel或redis cluster)的锁实现–RedLock

RedLock实现思路

正因为如此,Redis作者antirez基于分布式环境下提出了一种更高级的分布式锁的实现方式:Redlock。笔者认为,Redlock也是Redis所有分布式锁实现方式中唯一能让面试官高潮的方式。

antirez提出的redlock算法大概是这样的:

在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。我们确保将在N个实例上使用与在Redis单实例下相同方法获取和释放锁。现在我们假设有5个Redis master节点,同时我们需要在5台服务器上面运行这些Redis实例,这样保证他们不会同时都宕掉。

为了取到锁,客户端应该执行以下操作:

  1. 获取当前Unix时间,以毫秒为单位。
  2. 依次尝试从5个实例,使用相同的key和具有唯一性的value(例如UUID)获取锁。当向Redis请求获取锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试去另外一个Redis实例请求获取锁。
  3. 客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数(N/2+1,这里是3个节点)的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
  4. 如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。
  5. 如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。
RedLock使用代码
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://172.29.1.180:5378")
        .setPassword("a123456").setDatabase(0);
RedissonClient redissonClient1 = Redisson.create(config1);

Config config2 = new Config();
config2.useSingleServer().setAddress("redis://172.29.1.180:5379")
        .setPassword("a123456").setDatabase(0);
RedissonClient redissonClient2 = Redisson.create(config2);

Config config3 = new Config();
config3.useSingleServer().setAddress("redis://172.29.1.180:5380")
        .setPassword("a123456").setDatabase(0);
RedissonClient redissonClient3 = Redisson.create(config3);

String resourceName = "REDLOCK";
RLock lock1 = redissonClient1.getLock(resourceName);
RLock lock2 = redissonClient2.getLock(resourceName);
RLock lock3 = redissonClient3.getLock(resourceName);

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
boolean isLock;
try {
    isLock = redLock.tryLock(500, 30000, TimeUnit.MILLISECONDS);
    System.out.println("isLock = "+isLock);
    if (isLock) {
        //TODO if get lock success, do something;
        Thread.sleep(30000);
    }
} catch (Exception e) {
} finally {
    // 无论如何, 最后都要解锁
    System.out.println("");
    redLock.unlock();
}

最核心的变化就是RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);,因为我这里是以三个节点为例。

那么如果是哨兵模式呢?需要搭建3个,或者5个sentinel模式集群(具体多少个,取决于你)。

那么如果是集群模式呢?需要搭建3个,或者5个cluster模式集群(具体多少个,取决于你)。

Redisson实现源码

既然核心变化是使用了RedissonRedLock,那么我们看一下它的源码有什么不同。这个类是RedissonMultiLock的子类,所以调用tryLock方法时,事实上调用了RedissonMultiLock的tryLock方法,精简源码如下:

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    // 实现要点之允许加锁失败节点限制(N-(N/2+1))
    int failedLocksLimit = failedLocksLimit();
    List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());
    // 实现要点之遍历所有节点通过EVAL命令执行lua加锁
    for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
        RLock lock = iterator.next();
        boolean lockAcquired;
        try {
            // 对节点尝试加锁
            lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
        } catch (RedisConnectionClosedException|RedisResponseTimeoutException e) {
            // 如果抛出这类异常,为了防止加锁成功,但是响应失败,需要解锁
            unlockInner(Arrays.asList(lock));
            lockAcquired = false;
        } catch (Exception e) {
            // 抛出异常表示获取锁失败
            lockAcquired = false;
        }

        if (lockAcquired) {
            // 成功获取锁集合
            acquiredLocks.add(lock);
        } else {
            // 如果达到了允许加锁失败节点限制,那么break,即此次Redlock加锁失败
            if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
                break;
            }               
        }
    }
    return true;
}

详见:Redisson实现Redis分布式锁的N种姿势

  1. Redisson锁能解决主从数据一致的问题吗
    不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁
    在这里插入图片描述

Redis分布式锁参考资料

  • 从认知到实现,一文读懂实现分布式锁的五种方案
  • 分布式锁的各种实现,看完这篇你就懂了!
  • 详细解释Redis的SET NX命令

-Redisson实现分布式锁

  • Redis如何实现分布式锁,单机Redis与集群Redis问题解决方案
  • 看门狗”机制—分布式锁
  • 从原理到实践,五分钟时间带你了解 Redisson 分布式锁的实现方案
    -Redlock:Redis分布式锁的实现
  • Redisson实现Redis分布式锁的N种姿势
  • Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型

zookeeper实现分布式锁

Zookeeper底层是类似于文件系统那样的树结构(称为ZNode),这种树状结构和基于ZNode的数据模型使得ZooKeeper非常适合用于实现分布式协调和同步的场景,例如分布式锁、选举算法等。客户端可以通过创建、读取、更新和删除ZNode来实现对共享数据和协调状态的访问和操作。

下面是ZooKeeper实现分布式锁的基本原理和步骤:

  1. 客户端在ZooKeeper指定的目录下创建一个有序临时节点,代表自己的请求。
  2. 客户端获取目录下所有的子节点,并按节点名称的顺序进行排序。
  3. 客户端判断自己创建的节点是否是当前最小的节点,如果是,则认为获取到了锁;如果不是,则监听比自己小的上一个节点的删除事件。
  4. 如果监听的节点被删除,客户端回到第2步重新判断自己是否获得了锁。
  5. 获取到锁的客户端在完成任务后,删除自己创建的节点,释放锁。

至于ZooKeeper为什么能够实现分布式锁的原因有以下几点:

  1. 有序节点:ZooKeeper的节点是有序的,可以根据节点名称的顺序来实现竞争顺序。通过对节点名称进行排序,客户端可以判断自己是否是当前最小的节点,从而决定是否获得锁。
  2. 临时节点:ZooKeeper的临时节点是会话级别的,当创建节点的客户端会话结束时,该节点会被自动删除。利用临时节点,可以实现锁的自动释放,避免锁被长时间占用。
  3. Watch机制:ZooKeeper提供了Watch机制,客户端可以对节点的变化进行监听。通过监听比自己小的节点的删除事件,可以实现客户端之间的协调,确保只有最小节点的客户端获得锁。
    总体来说,ZooKeeper通过有序节点和临时节点的特性,结合Watch机制,提供了一种简单而可靠的机制来实现分布式锁。客户端通过竞争创建有序临时节点,并监听前一个节点的删除事件,从而实现分布式环境下的互斥访问和协调操作。
    在这里插入图片描述
    在这里插入图片描述

基于zookeeper的分布式锁实现 — Curator

实际项目中,推荐使用 Curator 来实现 ZooKeeper 分布式锁。Curator 是 Netflix 公司开源的一套 ZooKeeper Java 客户端框架,相比于 ZooKeeper 自带的客户端 zookeeper 来说,Curator的封装更加完善,各种API都可以比较方便地使用。Curator主要实现了下面四种锁:
InterProcessMutex:分布式可重入排它锁
InterProcessSemaphoreMutex:分布式不可重入排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器,获取锁的时候获取所有锁,释放锁也会释放所有锁资源(忽略释放失败的锁)。

以下是使用Curator的部分代码示例

  1. 添加依赖到你的pom.xml:
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.6.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>5.6.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-client</artifactId>
    <version>5.6.0</version>
</dependency>
  1. 配置Curator Framework并创建InterProcessMutex实例:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class ZookeeperConfig {
 
    @Bean
    public CuratorFramework curatorFramework() {
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
        client.start();
        return client;
    }
 
    @Bean
    public InterProcessMutex interProcessMutex(CuratorFramework client) {
        return new InterProcessMutex(client, "/lock");
    }
}
  1. 使用InterProcessMutex获取锁和释放锁:
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LockController {
 
    @Autowired
    private InterProcessMutex lock;
 
    @GetMapping("/lock")
    public String lock() {
        try {
            lock.acquire();
            // 这里执行需要互斥的代码
            return "Lock acquired";
        } catch (Exception e) {
            return "Error: " + e.getMessage();
        } finally {
            if (lock.isAcquiredInThisProcess()) {
                lock.release();
            }
        }
    }
}

互斥性+避免死锁

有序临时节点+监听机制,保证互斥性,并避免死锁

避免死锁

可重入性

为每一个获取锁的客户端维护一个可重入计数器,每次上锁时计数器+1,释放锁时计数器-1。

高可用行

性能

仅次于redis,但zookeeper能保证可靠性,可靠性zk>redis

参考

  • 分布式锁(MySQL、redis、zookeeper)
  • 基于zookeeper+springboot的方式实现分布式锁
  • SpringBoot结合ZooKeeper实现分布式锁

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

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

相关文章

【嵌入式linux开发】智能家居入门6:最新ONENET,物联网开放平台(QT、微信小程序、MQTT协议、ONENET云平台、旭日x3派)

智能家居入门6&#xff1a;最新ONENET物联网开放平台 前言最终现象一、ONENET云平台创建产品与设备二、使用MQTT客户端软件测试2.1 测试前的准备2.2 测试 三、LInux QT代码四、onenet 云端API介绍及微信小程序代码4.1 onenet API介绍4.2 微信小程序代码 五、板端运行 前言 ONE…

LeetCode 热题100-24

回文链表 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff…

四层负载企业实战

通过四层负载LVSkeepalived将请求转发到nginx代理服务器。通过代理服务器访问后端真实应用服务。 拓补图&#xff1a; 准备环境6台机器&#xff1b; lvskeepalived---两台 nginx代理 ---两台 tomcat----一台 php ----一台 所有机器关闭防火墙和selinux 所有机器解析如下…

APP安全检测报告内容详解

一、APP安全检测的必要性 APP安全检测是确保移动应用安全的重要一环。在开发过程中&#xff0c;由于技术、人员和管理等方面的原因&#xff0c;APP可能存在各种安全漏洞和风险。这些漏洞一旦被黑客利用&#xff0c;可能导致用户数据泄露、恶意代码注入、应用被篡改等严重后果。…

Windows下使用QT5.14.2编译MySQL8.0对应的最新64位驱动程序步骤

不得不说mysql更新的速度是真的快&#xff0c;最近一两年都更新了好几个的小版本了。如果安装了高版本的 mysql软件&#xff0c;低版本的驱动就不支持了&#xff0c;因此需要重新使用QT来编译对应的mysql驱动。具体办法如下&#xff1a; 1、官网下载最新的mysql8.0安装包。下载…

day04--js的综合案例

1.1 商品全选 需求&#xff1a;商品全选 1. 全选 &#xff1a;点击全选按钮,所有复选框都被选中 2. 全不选 &#xff1a;点击全不选按钮,所有复选框都被取消选中 3. 反选 &#xff1a; 点击反选按钮,所有复选框状态取反 <!DOCTYPE html> <html lang"en">…

智慧校园云平台电子班牌系统源码,智慧教育一体化云解决方案

智慧校园云平台电子班牌系统&#xff0c;利用先进的云计算技术&#xff0c;将教育信息化资源和教学管理系统进行有效整合&#xff0c;实现生态基础数据共享、应用生态统一管理&#xff0c;为智慧教育建设的统一性&#xff0c;稳定性&#xff0c;可扩展性&#xff0c;互通性提供…

利用PDAL库实现Las文件向PLY文件的转换

1、官网的下载链接 PDAL&#xff08;Point Data Abstraction Library&#xff09;是一个开源的C库&#xff0c;专门用于处理三维点云数据。它提供了数据读取、转换、过滤、统计分析等多种功能&#xff0c;支持多种数据格式&#xff0c;如LAS、LiDAR、ASCII等。在IT行业中&…

Java常用集合(List、Map)类型相关问题整理

一、背景 针对Java基础集合的部分&#xff0c;对一些常见的问题进行整理&#xff0c;方便后续能够随时复习 二、问题与回答 &#xff08;1&#xff09;Java集合类ArrayList初始化时数组的默认长度是多少&#xff1f; 答&#xff1a;在new ArrayList() 这段代码执行完后&a…

类Unix环境在Windows上的演进史

自从以Unix为基础的操作系统被发明以来&#xff0c;尝试在Windows环境中模仿Unix操作的工具就一直存在。这种需求源于许多原因&#xff0c;包括Unix对脚本和命令行工具的强大支持&#xff0c;以及Unix和Linux系统在科学、工程和其他技术领域的广泛使用。下面就让我们一起探讨下…

input[type=checkbox]勾选框自定义样式

效果图&#xff1a; <template> <input class"rule-checkbox" type"checkbox" checked v-model"isChecked" /> </template><script setup lang"ts"> import { ref } from vue; const isChecked ref(); </…

应急响应-DDOS-技术指南

初步预判 通常&#xff0c;可从以下几方面判断服务器/主机是否遭受DDoS攻击查看防火墙、流量监控设备、网络设备等是否出现安全告警或大量异常数据包。如图所示&#xff0c;通过流量对比&#xff0c;发现在异常时间段存在大量UDP数据包&#xff0c;并且与业务无关。 通过安全设…

猫毛还是满天飞?宠物空气净化器是个好帮手

家里养了几只可爱的小猫咪&#xff0c;每天都想它们贴贴&#xff0c;有时候看到它们这么可爱的待在家里&#xff0c;都不想出门上班了。每天睁眼是它们&#xff0c;闭眼前也是它们&#xff0c;只要我待在家里&#xff0c;它们就和我一起挪动&#xff0c;好像身边多了几只可爱的…

qt-12工具盒(ToolBox)

工具盒--ToolBox drawer.hdrawer.cppmain.cpp运行图 drawer.h #ifndef DRAWER_H #define DRAWER_H #include <QWidget> #include <QToolBox> #include <QToolButton> #include <QGroupBox> #include <QVBoxLayout>class Drawer : public QToolB…

机械学习—零基础学习日志(如何理解线性代数5)

零基础为了学人工智能&#xff0c;正在快乐学习&#xff0c;每天都长脑子 特征向量和特征值 我们知道&#xff0c;线性映射&#xff0c;也就是矩阵&#xff0c;其实就是平面的一种变换。 但是在矩阵变换过程中&#xff0c;我们会发现有一个方向上&#xff0c;变化方向会与x的…

Python 函数式编程 内置高阶函数及周边【进阶篇 3】推荐

前面我们已经总结并实践了用python获取到了数据。也介绍了python中http网络请求的几种方式&#xff0c;正在学习python开发语言或者对python3知识点生疏需要回顾的请点这里 &#xff0c;本章主要总结了函数式编程及特点 和 python中内置的高阶函数及周边知识&#xff0c;方便自…

Scout Suite:开源云安全审计工具

Scout Suite 是一个开源、多云安全审计工具&#xff0c;旨在评估云环境的安全态势。 Scout Suite 利用云供应商提供的 API 来收集和整理配置数据&#xff0c;从而更轻松地识别潜在风险。 Scout Suite 无需手动筛选云 Web 控制台上的大量页面&#xff0c;而是会自动生成全面清…

ObjectUtils.nullSafeEquals你真的用对了吗?

目录 引言排查思考 引言 在写代码时&#xff0c;我们通常喜欢使用org.springframework.util.ObjectUtils#nullSafeEquals来比较两个对象是否相等&#xff0c;从而避免使用equals方法在对象为空时导致空指针异常。 最近在写代码时&#xff0c;我试图使用stream流的filter&#…

1.Linux_常识

UNIX、Linux、GNU 1、UNIX UNIX是一个分时操作系统&#xff0c;特点是多用户、多任务 实时操作系统&#xff1a;来了请求就去解决请求 分时操作系统&#xff1a;来了请求先存着&#xff0c;通过调度轮到执行时执行 2、Linux Linux是一个操作系统内核 发行版本&#xff1…

Linux - 常用基础指令和命令

文章目录 1、ifconfig指令2、ssh指令3、ls指令4、pwd命令5、cd 指令6、stat命令7、 touch指令8、mkdir指令9、rmdir指令10、rm指令11、man指令12、cp指令13、mv指令14、cat指令15、more指令16、less指令17、head指令18、tail指令19、时间相关的指令20、cal指令21、find指令22、…