分布式锁Redisson详解,Redisson如何解决不可重入,不可重试,超时释放,主从一致问题的分析解决(包括源码简单分析)

news2025/1/13 19:51:12

目录

1. Redisson解决不可重入锁导致的死锁问题 

2. 不可重试问题

Pub/Sub 的优势

锁释放的发布逻辑

3. 超时释放的问题

1. 锁的超时释放机制背景

2. 源码分析

2.1 锁的获取

2.2 看门狗机制

2.3 看门狗续期实现

2.4 手动设置锁的过期时间

总结

 

4. 主从一致性 

问题背景

解决方案:使用 MultiLock

使用场景:

MultiLock 的核心思路

2. MultiLock 的结构

关键成员变量:

3. 获取锁 (lock 方法)

4. 解锁操作 (unlock 方法)

锁获取失败时的处理

锁的自动释放

关键代码

详细解释

为什么需要重新设置过期时间?

总结


1. Redisson解决不可重入锁导致的死锁问题 

不可重入锁是指,某个线程获取锁后,在锁释放之前,如果该线程再次尝试获取该锁,则会阻塞自己,从而可能导致死锁问题。

解决方法: Redisson 提供了可重入锁(RLock),支持同一线程多次加锁,锁的计数器会累加。每次解锁时,计数器减少,直到计数器归零才释放锁

使用HashMap的形式,记录每个线程获取锁的次数

 

2. 不可重试问题

在分布式环境下,多个客户端同时尝试获取同一把锁时:

  • 如果直接轮询(频繁重试),会导致 Redis 的压力过大(高频请求)。
  • 如果直接返回失败,则无法利用锁的释放时机,可能会错失锁。

解决方法:在 Redisson 中,通过 tryLock 方法实现了分布式锁的 不可重试 问题的优化。Redisson 的解决方法并不是简单的立即返回失败或者频繁重试,而是利用 Redis 的 发布-订阅(Publish-Subscribe)机制 来高效监听锁释放事件。

在第一次获取锁失败之后,并不是立即返回失败,也不是立刻重新获取锁给CPU造成负担,而是基于一种“观察者”的形态,订阅别人释放锁的信息——Publish-subscribe。

Redisson 使用 Redis 的发布-订阅机制,监听锁的释放事件,从而实现高效的等待锁。

源码位置: 在 Redisson 的 RedissonLock.tryLock 方法中,核心逻辑是:

  1. 首先尝试获取锁。
  2. 如果获取锁失败,订阅锁释放事件(通过 Redis 的 Pub/Sub 机制)。
  3. 等待锁释放通知,再次尝试获取锁。

获取锁 的逻辑

 以下是主要的逻辑源码片段(简化版):

@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    long time = unit.toMillis(waitTime);
    long currentTime = System.currentTimeMillis();

    // 尝试获取锁(首次)
    if (tryAcquire(leaseTime, unit)) {
        return true; // 成功直接返回
    }

    // 订阅锁释放事件
    RFuture<RedissonLockEntry> subscribeFuture = subscribeToChannel();

    try {
        if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
            return false; // 等待超时,返回失败
        }

        // 循环监听锁的释放事件,直到成功获取锁或超时
        while (time > 0) {
            // 再次尝试获取锁
            if (tryAcquire(leaseTime, unit)) {
                return true;
            }

            time -= waitForMessage(time); // 等待锁释放通知
        }

    } finally {
        unsubscribeFromChannel(); // 解订阅
    }

    return false; // 获取锁失败
}

结合源码,以下是 tryLock 的具体执行流程:

  1. 尝试获取锁(非阻塞)

    • Redisson 首次尝试调用 tryAcquire 方法,直接获取锁。
    • 如果成功,则返回 true,完成加锁。
    • 如果失败,说明锁已被其他线程持有。
  2. 订阅锁释放事件

    • 调用 subscribeToChannel 方法,在 Redis 的一个频道上订阅锁的释放事件。
    • Redisson 为每个锁定义一个唯一的频道名,例如 __keyevent@0__:del
    • 当锁被释放时,会通过 Redis 的 PUBLISH 命令通知订阅的客户端。
  3. 等待锁释放通知

    • 如果订阅成功,Redisson 会阻塞当前线程,等待锁释放的消息。
    • 这个阻塞等待由 await 方法实现,监听锁释放信号。
    • 当其他客户端释放锁时,Redis 会发布消息,Redisson 收到后唤醒线程。
  4. 再次尝试获取锁

    • 收到锁释放通知后,Redisson 再次尝试调用 tryAcquire 方法获取锁。
    • 如果成功获取,则返回 true
    • 如果仍然失败(可能被其他线程抢占),继续循环等待,直到超时。
  5. 超时处理

    • 如果在指定的等待时间内没有成功获取锁,Redisson 会退出循环,解订阅锁释放事件,返回 false

Pub/Sub 的优势

这种基于发布-订阅机制的实现方式,解决了锁竞争时的资源浪费问题:

  • 避免频繁轮询: 客户端不会反复尝试请求锁,而是阻塞等待锁的释放通知,降低 Redis 的压力。
  • 实时性高: 一旦锁释放,Redis 会立即发布消息通知所有订阅的客户端,客户端可以快速响应。
  • 效率高: 使用 await 方法阻塞线程,减少 CPU 资源消耗。

锁释放的发布逻辑

锁的释放通过 Redis 的 PUBLISH 命令触发。以下是锁释放的逻辑:

@Override
public void unlock() {
    // 删除锁键
    get(commandExecutor.writeAsync(getRawName(), RedisCommands.DEL, getRawName())).thenAccept(deleted -> {
        if (deleted > 0) {
            // 通知订阅的客户端锁已释放
            publishUnlock(); 
        }
    });
}

publishUnlock 方法: 

private void publishUnlock() {
    // 发布释放事件到 Redis 频道
    commandExecutor.get(commandExecutor.writeAsync(getChannelName(), RedisCommands.PUBLISH, getChannelName(), LOCK_MESSAGE));
}

在 Redisson 的 tryLock 中,基于 Pub/Sub 机制实现了锁的高效等待:

  • 第一次尝试获取锁失败后,不是立即返回失败,而是通过订阅锁释放的事件,类似一个“观察者”监听锁的状态。
  • 收到锁释放通知后,立即尝试再次获取锁,从而避免了频繁轮询和资源浪费。

3. 超时释放的问题

1. 锁的超时释放机制背景

当线程获取锁后,如果线程意外退出或执行时间超过锁的过期时间,Redis 中的锁键会被自动删除,从而释放锁。

问题:

  • 如果业务逻辑执行时间较长,可能超过锁的过期时间,导致锁被释放,而其他线程获取到锁,导致数据不一致。
  • 如果锁没有正确释放,也会造成资源被永久占用(死锁)。

解决方案:

  • Redisson 通过看门狗机制动态延长锁的过期时间,避免锁意外释放。
  • 用户也可以手动指定锁的过期时间,避免看门狗机制。

2. 源码分析

2.1 锁的获取

Redisson 分布式锁的核心是 Redis 的键值存储。锁被表示为一个 Redis 键,其值是锁的拥有者标识(如 UUID:ThreadId),并设置过期时间。

源码位置:RedissonLock.tryAcquire 

private boolean tryAcquire(long leaseTime, TimeUnit unit) {
    long threadId = Thread.currentThread().getId();
    String lockValue = getLockValue(threadId); // 生成 UUID + ThreadId 的唯一标识

    // 通过 Redis 的 SET NX 设置锁,如果设置成功则返回 1
    RFuture<Boolean> future = commandExecutor.writeAsync(
        getRawName(),
        RedisCommands.SET,
        getRawName(),
        lockValue,
        "NX", 
        "PX", unit.toMillis(leaseTime)
    );

    return get(future);
}

逻辑分析:

  1. 调用 Redis 的 SET NX 指令:
    • NX:表示仅当键不存在时才设置值。
    • PX:设置键的过期时间(以毫秒为单位)。
  2. 如果设置成功(返回 1),表示当前线程成功获取到锁。
  3. 如果失败,表示锁已被其他线程持有。
2.2 看门狗机制

看门狗机制的作用是:在锁未被释放前,动态延长锁的过期时间,防止业务逻辑超时导致锁提前释放

TTL = -1:(用户没有指定锁的过期时间)

  • 锁的生存时间依赖于 Redisson 的看门狗机制,而不是 Redis 自己的超时机制。
  • 看门狗会定期续约锁,确保锁不会因超时被释放,直到显式调用 unlock 方法来释放锁。
@Override
public void lock() {
    lock(-1, null); // -1 表示使用默认过期时间
}

private void lock(long leaseTime, TimeUnit unit) {
    long threadId = Thread.currentThread().getId();
    try {
        // 尝试加锁
        boolean success = tryAcquire(leaseTime, unit);
        if (!success) {
            // 等待锁的释放或继续重试
            awaitLock(leaseTime, unit, threadId);
        }

        // 如果使用看门狗机制,启动续期任务
        if (leaseTime == -1) {
            scheduleWatchdogRenewal(threadId);
        }
    } catch (Exception e) {
        // 异常处理
    }
}

源码位置:RedissonLock.lock

关键逻辑:

  • 如果用户未指定锁的过期时间(leaseTime == -1),Redisson 启动看门狗机制,默认的锁超时时间为 30秒
  • 启动一个定时任务,周期性延长锁的过期时间,确保锁不会意外释放。
2.3 看门狗续期实现

源码位置:scheduleWatchdogRenewal

private void scheduleWatchdogRenewal(long threadId) {
    commandExecutor.getConnectionManager().newTimeout(timeout -> {
        // 检查锁是否仍然由当前线程持有
        if (isHeldByCurrentThread(threadId)) {
            // 发送 Redis 的 EXPIRE 命令,延长锁的过期时间
            commandExecutor.writeAsync(getRawName(), RedisCommands.PEXPIRE, getRawName(), LOCK_EXPIRATION_TIME);
            
            // 再次调度续期任务
            scheduleWatchdogRenewal(threadId);
        }
    }, LOCK_WATCHDOG_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
}

关键逻辑:

  1. 锁状态检查:

    • 调用 isHeldByCurrentThread 方法,确保当前线程仍然持有锁。
    • 如果锁已经被释放,则终止续期任务。
  2. 延长锁的过期时间:

    • 使用 Redis 的 PEXPIRE 指令,将锁的过期时间延长到默认的 30秒
    • 每次续期时,锁的过期时间会被重置。
  3. 递归调度续期任务:

    • 每隔 10秒(默认) 检查一次锁的状态,如果锁未释放,则继续延长过期时间。
2.4 手动设置锁的过期时间

如果用户明确知道业务逻辑的执行时间,可以手动设置锁的过期时间,禁用看门狗机制。

代码示例:

lock.lock(10, TimeUnit.SECONDS); // 持有锁 10 秒,10 秒后自动释放

源码位置:RedissonLock.lock 

private void lock(long leaseTime, TimeUnit unit) {
    long threadId = Thread.currentThread().getId();
    try {
        boolean success = tryAcquire(leaseTime, unit);
        if (!success) {
            awaitLock(leaseTime, unit, threadId);
        }

        // 如果用户指定了过期时间,不启动看门狗机制
        if (leaseTime > 0) {
            return;
        }

        // 启动看门狗机制
        scheduleWatchdogRenewal(threadId);
    } catch (Exception e) {
        // 异常处理
    }
}

总结

Redisson 的超时释放机制主要通过以下方式实现:

  1. 默认看门狗机制:

    • 当用户未设置锁的过期时间时,Redisson 启动看门狗任务,每隔 10秒 检查一次锁是否需要续期,延长过期时间,确保锁不会意外释放。
    • 默认过期时间为 30秒,每次续期重置为 30 秒。
  2. 手动设置过期时间:

    • 用户可以显式指定锁的持有时间,禁用看门狗机制。锁会在指定时间到期后自动释放。
  3. Redis 的关键指令:

    • SET NX PX:设置锁并指定初始过期时间。
    • PEXPIRE:动态更新锁的过期时间。

Redisson 的这一机制在避免锁超时问题的同时,也能通过手动控制灵活应对不同业务场景。

超时续约:利用watchD0g,每隔一段时间(releaseTime / 3),重置超时时间。这个超时续约只有-1才执行,也就是说自己设置超时时间是没有执行回调(可以倒回去看源码),所以这个续约是防止java代码宕机,redis还有锁的id的不是解决执行业务超时

4. 主从一致性 

Redisson的multiLock解决Redis主从关系,主节点宕机导致主从不一致,锁丢失的问题

Redisson的 MultiLock 机制可以用于解决Redis主从关系中主节点宕机导致主从不一致和锁丢失的问题。具体来说,MultiLock 可以通过组合多个锁来保证原子性,从而在多个 Redis 实例之间管理分布式锁的状态,避免在主节点宕机时锁丢失 

问题背景

在 Redis 主从架构中,当主节点宕机时,可能会导致一些分布式锁的丢失或状态不一致的问题。尤其是在多实例环境下,主节点和从节点之间的复制延迟或数据同步不及时,可能会导致某些操作无法在多个节点间正确协调,进而影响数据一致性和锁的管理。

解决方案:使用 MultiLock

Redisson 提供了 MultiLock 机制,可以让你在多个 Redis 实例之间设置多个锁。它支持多个锁对象(比如 Redis 的多个 key)在一个操作中共同参与,确保这些锁的操作具备原子性。

使用场景:
  1. 跨多个 Redis 实例的分布式锁:如果你的架构包含多个 Redis 实例(如主从架构),你可能希望在主从节点之间确保一个分布式锁不会在主节点宕机后丢失。此时,你可以使用 MultiLock 来跨多个 Redis 实例管理锁。
  2. 锁丢失防止:即使 Redis 的主节点宕机或发生故障,MultiLock 会确保在主节点和从节点之间锁的持有一致性,避免因主节点不可用导致锁状态丢失。

MultiLock 的核心思路

MultiLock 的目标是提供一个组合锁,它通过同时获取多个锁来保证操作的原子性。当多个锁被封装在 MultiLock 中时,它们的所有锁操作(如获取锁、释放锁)会以一个整体来执行,确保多个锁在一个事务中操作。

2. MultiLock 的结构

MultiLock 本质上是封装了多个 RLock 锁对象,并提供了一些操作方法来确保这些锁的原子性和一致性。

关键成员变量:

MultiLock 主要有一个 locks 集合来保存多个锁对象。

private final List<RLock> locks;

3. 获取锁 (lock 方法)

当调用 multiLock.lock() 时,它会执行以下逻辑:

  1. 遍历所有锁: 它会遍历 locks 集合中的每一个 RLock 对象,尝试获取每个锁。

  2. 获取锁: 对于每个锁对象,调用 RLocklock 方法。这是通过 Redis 的 SETNX 操作来实现的,即使得每个锁在 Redis 中以原子方式被获取。

    ​
    public void lock() {
        List<Thread> threads = new ArrayList<>();
        for (RLock lock : locks) {
            Thread thread = new Thread(() -> {
                try {
                    lock.lock();
                } catch (InterruptedException e) {
                    // 异常处理
                }
            });
            thread.start();
            threads.add(thread);
        }
        for (Thread thread : threads) {
            thread.join();
        }
    }
    
    ​
    • lock() 方法的主要职责是获取 MultiLock 所包含的所有子锁(RLock)的锁定。这是通过为每个子锁启动一个独立的线程来并行获取锁,然后等待所有线程完成锁定操作。
    • 调用 join() 方法:对于 threads 列表中的每个线程,调用 thread.join(),这会使当前线程(调用 lock() 的线程)等待直到该锁定线程完成执行。
    • 确保所有子锁都已获取:只有在所有子锁都成功获取后,lock() 方法才会返回,从而保证 MultiLock 已经成功锁定了所有相关资源。
  3. 在这个过程中,如果任何一个锁无法获取(例如超时或者其他原因),它会尝试释放所有已经获取的锁。
  4. 原子操作 为了保证多锁获取的原子性,MultiLock 必须在所有锁都成功获取后才认为操作完成。这样,只有所有锁都被成功获取,才能继续执行接下来的操作。

4. 解锁操作 (unlock 方法)

调用 multiLock.unlock() 时,会遍历所有的 RLock,释放它们所持有的锁:

public void unlock() {
    for (RLock lock : locks) {
        lock.unlock();
    }
}

 每个 RLockunlock() 会执行 Redis 的 DEL 操作,释放该锁。这保证了锁能够在多节点分布式环境下及时释放,防止死锁。

锁获取失败时的处理

如果在尝试获取锁时遇到失败(例如超时或中断)MultiLock 会进行补偿操作。假如某一个锁无法获取,MultiLock 会释放已经获取的锁,并且返回失败,避免部分锁被获取时产生不一致的状态。

锁的自动释放

Redisson 的锁是基于 Redis 的 EXPIRE 命令设置过期时间的,默认情况下,MultiLock 在获取锁时会设置一个超时时间。如果某个操作在超时后未完成,锁会被自动释放。这样可以防止由于某个节点的崩溃或死锁而导致锁无法释放。

MultiLock 是 Redisson 中的一个组合锁,用于同时操作多个分布式锁。由于这些锁可能在不同时间点被获取,为了保证锁的整体一致性,需要在所有锁被获取后重新设置每个锁的过期时间,从而确保锁的 自动释放 功能能够正确生效。

关键代码

以下是设置每个子锁过期时间的核心逻辑:

leaseTime == -1的时候回触发看门狗机制,通过看门狗机制来管理锁的续约和释放。因此不需要在所有锁被获取后重新设置每个锁的过期时间。

if (leaseTime != -1) {
    List<RFuture<Boolean>> futures = new ArrayList<>(acquiredLocks.size());
    for (RLock rLock : acquiredLocks) {
        RFuture<Boolean> future = ((RedissonLock) rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
        futures.add(future);
    }

    for (RFuture<Boolean> rFuture : futures) {
        rFuture.syncUninterruptibly();
    }
}
详细解释

1. 检查租约时间是否设置

if (leaseTime != -1) 

  • 目的:确定是否需要为锁设置租约时间。
  • 逻辑:如果 leaseTime 不等于 -1,则表示需要为锁设置一个租约时间;否则,跳过这一部分,可能意味着锁会一直持有直到显式释放(或依赖看门狗机制)。

2. 初始化 futures 列表

List<RFuture<Boolean>> futures = new ArrayList<>(acquiredLocks.size());

  • 目的:创建一个 List 来存储异步操作的结果(RFuture<Boolean>)。
  • 说明acquiredLocks 是已经成功获取的所有子锁的集合。列表的初始容量设置为 acquiredLocks.size(),以优化内存分配。

3. 为每个子锁设置租约时间(异步操作)

    for (RLock rLock : acquiredLocks) {
        RFuture<Boolean> future = ((RedissonLock) rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
        futures.add(future);
    }
  • 遍历所有已获取的子锁:对每一个 RLock(具体为 RedissonLock 实现)进行操作。
  • 类型转换:将 RLock 转换为 RedissonLock,以访问 expireAsync 方法。
  • 调用 expireAsync 方法
    • 功能:异步地为 Redis 锁设置过期时间(即租约时间)。
    • 参数
      • unit.toMillis(leaseTime):将租约时间转换为毫秒。
      • TimeUnit.MILLISECONDS:指定时间单位为毫秒。
    • 返回值RFuture<Boolean>,表示异步操作的结果,Boolean 值表示是否成功设置了过期时间。
  • 添加到 futures 列表:将每个异步操作的 RFuture 对象添加到 futures 列表中,以便后续等待其完成。

4. 等待所有异步操作完成

for (RFuture<Boolean> rFuture : futures) { rFuture.syncUninterruptibly(); }

  • 遍历所有 RFuture 对象:对 futures 列表中的每一个 RFuture 进行操作。
  • 调用 syncUninterruptibly() 方法
    • 功能:同步等待异步操作完成,且不会因为线程被中断而抛出 InterruptedException
    • 结果处理:虽然在此代码片段中没有显式处理 Boolean 结果,但调用 syncUninterruptibly() 确保所有租约时间设置操作已经完成,无论成功与否。
  • 目的:确保所有子锁的租约时间都已被设置,以维护锁的一致性和防止死锁。

为什么需要重新设置过期时间?

MultiLock 是一个组合锁,由多个子锁(RLock)组成。由于这些子锁是在不同时间点逐步获取的,它们的过期时间可能不同。若不统一重置过期时间,会导致以下问题:

  1. 过期时间不一致:第一个获取的子锁会比最后一个获取的子锁更早过期,从而导致 MultiLock 整体锁的失效时间变短。
  2. 潜在锁丢失问题:如果某个子锁率先过期,Redis 会自动释放该锁,导致 MultiLock 的保护失效。

通过重新设置所有子锁的过期时间,能够统一锁的失效时间,确保所有子锁在 leaseTime 内保持有效。

总结

MultiLock 的原理主要基于多个 RLock 锁的组合。它的工作方式如下:

组合多个子锁:

  • MultiLock 是由多个 RLock(子锁)组成的。每个子锁对应一个独立的 Redis 锁。
  • 通过将多个子锁组合在一起,MultiLock 能够实现跨多个 Redis 节点或多个资源的锁定操作。

加锁操作:

  • 当调用 MultiLock.lock() 时,它会依次尝试获取所有子锁。
  • 如果所有子锁都成功获取,锁定操作完成,持有 MultiLock
  • 如果任何一个子锁获取失败,则回滚之前成功获取的所有锁,确保不会部分持有锁。

解锁操作:

  • 当调用 MultiLock.unlock() 时,MultiLock 会依次释放所有子锁。
  • 每个子锁的释放操作是独立的,但整体上保证所有子锁最终被释放。

租约时间设置:

  • 如果指定了租约时间(leaseTime),MultiLock 会为每个子锁设置相同的超时时间。
  • 由于子锁可能在不同时间点被获取,MultiLock 会重新为每个子锁设置统一的过期时间,确保锁的整体一致性。
  • 如果没有指定租约时间,会触发看门狗机制自动为锁续约

自动释放:

  • 如果持有锁的客户端在租约时间内未释放锁,Redis 会根据 EXPIRE 删除子锁键,自动释放锁,防止死锁。

异步和并发优化:

  • MultiLock 使用异步方法(如 expireAsync)并行地为多个子锁设置过期时间,提高操作效率。
  • 在等待异步结果时,通过同步等待(syncUninterruptibly)确保所有操作完成。

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

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

相关文章

【微服务】面试 4、限流

微服务限流技术总结 一、微服务业务面试题引入 在微服务业务面试中&#xff0c;限流是重要考点&#xff0c;常与分布式事务、分布式服务接口幂等解决方案、分布式任务调度等一同被考查。面试官一般会询问项目中是否实施限流及具体做法&#xff0c;回答需涵盖限流原因、采用的方…

爬虫基础之爬取歌曲宝歌曲批量下载

声明&#xff1a;本案列仅供学习交流使用 任何用于非法用途均与本作者无关 需求分析: 网站:邓紫棋-mp3在线免费下载-歌曲宝-找歌就用歌曲宝-MP3音乐高品质在线免费下载 (gequbao.com) 爬取 歌曲名 歌曲 实现歌手名称下载所有歌曲 本案列所使用的模块 requests (发送…

树莓派-5-GPIO的应用实验之GPIO的编码方式和SDK介绍

文章目录 1 GPIO编码方式1.1 管脚信息1.2 使用场合1.3 I2C总线1.4 SPI总线2 RPI.GPIO2.1 PWM脉冲宽度调制2.2 静态函数2.2.1 函数setmode()2.2.2 函数setup()2.2.3 函数output()2.2.4 函数input()2.2.5 捕捉引脚的电平改变2.2.5.1 函数wait_for_edge()2.2.5.2 函数event_detect…

Scala分布式语言二(基础功能搭建、面向对象基础、面向对象高级、异常、集合)

章节3基础功能搭建 46.函数作为值三 package cn . itbaizhan . chapter03 // 函数作为值&#xff0c;函数也是个对象 object FunctionToTypeValue { def main ( args : Array [ String ]): Unit { //Student stu new Student() /*val a ()>{"GTJin"…

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…

支付宝租赁小程序提升租赁行业效率与用户体验

内容概要 在当今数字化的世界里&#xff0c;支付宝租赁小程序的出现构建了一种新的租赁模式&#xff0c;使得用户在使用过程中体验更加流畅。想象一下&#xff0c;你在寻找租赁服务时&#xff0c;不再需要繁琐的流程和冗长的等待&#xff0c;只需通过手机轻松点击几下&#xf…

关于使用FastGPT 摸索的QA

近期在通过fastGPT&#xff0c;创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上&#xff0c;可以定义业务逻辑&#xff0c;满足输出对话之外的需求。 在最近3个月来的摸索和实践中&#xff0c;一些基于经验的小问题点&#xff08;自己也常常…

服务器/电脑与代码仓gitlab/github免密连接

git config --global user.name "xxxx" git config --global user.email "xxxxxx163.com" #使用注册GitHub的邮箱 生成对应邮箱的密码对 ssh-keygen -t rsa -b 4096 -C "xxxxxx163.com" 把公钥id_rsa.pub拷贝到github中 Setting----->…

【C语言系列】函数递归

函数递归 一、递归是什么&#xff1f;1.1尾递归 二、递归的限制条件三、递归举例3.1举例一&#xff1a;求n的阶乘3.2举例二&#xff1a;顺序打印一个整数的每一位 四、递归与迭代4.1举例三&#xff1a;求第n个斐波那契数 五、拓展学习青蛙跳台问题 一、递归是什么&#xff1f; …

springboot 默认的 mysql 驱动版本

本案例以 springboot 3.1.12 版本为例 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.12</version><relativePath/> </parent> 点击 spring-…

[QCustomPlot] 交互示例 Interaction Example

本文是官方例子的分析: Interaction Example 推荐笔记: qcustomplot使用教程–基本绘图 推荐笔记: 4.QCustomPlot使用-坐标轴常用属性 官方例子需要用到很多槽函数, 这里先一次性列举, 自行加入到qt的.h中.下面开始从简单的开始一个个分析. void qcustomplot_main_init(void); …

openMetaData docker方式安装部署记录

OpenMetadata一站式元数据管理平台&#xff0c;是一款功能强大的开源元数据管理平台&#xff0c;旨在帮助企业更好地发现、理解和管理其数据资产。它提供了一套全面的工具和功能&#xff0c;涵盖了数据发现、数据血缘、数据质量、数据探查、数据治理和团队协作等多个方面。 那…

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景 实现效果 该案例实现了使用Three.js创建一个带有聚光灯和旋转立方体的3D场景。 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; THREE.WebGLRenderer 是 Three.js 中用于将场景渲染为 WebGL 内容的核…

本地视频进度加入笔记+根据进度快速锁定视频位置

本地视频进度记录快速回溯 引言 在学习的过程中, 如果我们想快速记录当前看视频的位置, 后续回溯查找就会非常方便了。 实现效果 进度记录 通过按下快捷键ctrlaltu&#xff0c; 快速记录当前视频的进度信息,然后复制到typora软件内 快速回溯 在typora软件内, 选中视频索引…

Spring Boot 支持哪些日志框架

Spring Boot 支持多种日志框架&#xff0c;主要包括以下几种&#xff1a; SLF4J (Simple Logging Facade for Java) Logback&#xff08;默认&#xff09;Log4j 2Java Util Logging (JUL) 其中&#xff0c;Spring Boot 默认使用 SLF4J 和 Logback 作为日志框架。如果你需要使…

快速导入请求到postman

1.确定请求&#xff0c;右键复制为cURL(bash) 2.postman菜单栏Import-Raw text&#xff0c;粘贴复制的内容保存&#xff0c;请求添加成功

Golang的网络流量分配策略

## 1. Golang中的网络流量分配策略 简介 在Golang中&#xff0c;网络流量分配策略是指如何有效地管理和优化网络请求的分配&#xff0c;以提高系统的性能和稳定性。优秀的网络流量分配策略能够使系统更好地应对高并发和大流量的情况&#xff0c;同时有效地避免网络拥堵和性能瓶…

【硬件介绍】Type-C接口详解

一、Type-C接口概述 Type-C接口特点&#xff1a;以其独特的扁头设计和无需区分正反两面的便捷性而广受欢迎。这种设计大大提高了用户的使用体验&#xff0c;避免了传统USB接口需要多次尝试才能正确插入的问题。Type-C接口内部结构&#xff1a;内部上下两排引脚的设计虽然可能不…

二、BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

Android车机DIY开发之软件篇(九)默认应用和服务修改

Android车机DIY开发之软件篇(九)默认应用和服务修改 默认应用位置 ~/packages/apps/Car 增加APP 1.增加 XXXX.app 和Android.mk 2. 修改~/build/make/target/product/handheld_system_ext.mk 默认服务位置 ~/frameworks/base/services/java/com/android/server 查看服务列…