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

【微服务】SpringBoot 整合 Lock4j 分布式锁使用详解

目录

一、前言

二、Lock4j 概述

2.1 Lock4j 介绍

2.1.1 Lock4j 是什么

2.1.2 Lock4j 主要特征

2.1.3 Lock4j 技术特点

2.2 Lock4j 支持的锁类型

2.3 Lock4j 工作原理

2.4 Lock4j 应用场景

三、springboot 整合 lock4j

3.1 前置准备

3.1. 1 导入依赖

3.2 基于Redis实现分布式锁

3.2.1 添加配置文件信息

3.2.2 添加测试接口

3.2.3 效果测试

3.3 基于Redission 实现

3.4 基于Zookeeper实现

3.4.1 启动zk服务

3.4.2 导入下面的依赖

3.4.3 添加配置信息

3.4.4 添加测试接口

3.4.5 模拟并发测试

四、Lock4j 功能扩展

4.1 自定义执行器作用

4.1.1 支持不同的分布式锁实现

4.1.2 定制锁的获取和释放逻辑

4.1.3 适配特殊业务需求

4.1.4 性能优化

4.1.5 增强可靠性

4.1.6 统一锁管理

4.2 自定义执行器使用

4.2.1 基于zk的自定义执行器

4.2.2 基于Redis的自定义执行器

4.2.3 自定义锁的key生成策略

五、写在文末


一、前言

在日常使用springboot框架进行微服务开发过程中,遇到需要控制并发造成的问题时,比较常用的做法是使用分布式锁进行控制,基于分布式锁的实现,到目前为止也有很多开源实现,使用比较多的像基于redis的分布式锁,基于zookeeper的分布式锁,本文再来介绍另一种比较高效的分布式锁实现,即Lock4j ,将通过案例演示下如何使用lock4j做分布式锁。

二、Lock4j 概述

2.1 Lock4j 介绍

2.1.1 Lock4j 是什么

Lock4j 是一个基于 Spring Boot 的分布式锁框架,旨在简化分布式系统中锁的实现和管理。项目入口:lock4j: 基于Spring AOP 的声明式和编程式分布式锁,支持RedisTemplate、Redisson、Zookeeper

 

2.1.2 Lock4j 主要特征

Lock4j是一个轻量级的分布式锁框架,它支持多种锁实现,包括Redis、Zookeeper等,并通过Spring AOP进行集成,使得开发者可以轻松地在Spring应用中使用分布式锁。它的设计目标是简单易用,同时提供高性能和高可靠性。具体来说,包括下面的主要特征:

  1. 注解驱动:通过简单的注解即可实现方法级别的锁控制

  2. 多种锁实现:支持多种底层锁实现方式

  3. 可扩展:支持自定义锁的实现

  4. 与Spring生态集成:无缝集成Spring框架

2.1.3 Lock4j 技术特点

Lock4j 作为一款上手简单,使用高效的分布式缓存技术框架,具备如下特点和优势:

  • 使用高效:

    • Lock4j采用了高效的锁机制,能够在高并发场景下保持稳定的性能。其底层实现充分利用了Redis和Zookeeper等存储系统的特性,确保锁操作的快速响应。

  • 简单易用:

    • 通过简单的API和Spring AOP集成,开发者可以快速上手并实现分布式锁。Lock4j提供了详细的文档和示例代码,帮助开发者快速理解和使用。

  • 锁支持类型丰富:

    • 支持多种底层存储,满足不同应用场景的需求。无论是高性能的Redis,还是强一致性的Zookeeper,Lock4j都能提供相应的支持。

  • 提供监控和日志:

    • 提供丰富的监控和日志功能,帮助开发者了解锁的使用情况和性能表现。通过监控界面,开发者可以实时查看锁的状态和性能指标。

2.2 Lock4j 支持的锁类型

Lock4j支持多种类型的锁,通过底层存储(如Redis或Zookeeper)来实现分布式锁。支持的锁类型包括:

  • Redis锁:基于Redis实现的分布式锁

  • Zookeeper锁:基于Zookeeper实现的分布式锁

  • 数据库锁:基于数据库实现的锁

  • 内存锁:本地JVM锁(适用于单机环境)

2.3 Lock4j 工作原理

什么是锁?锁是一种同步机制,用于控制多线程对共享资源的访问。在分布式系统中,锁的实现更加复杂,因为需要在多个节点之间进行协调。Lock4j 作为分布式锁框架,其核心原理是通过协调多个分布式节点对共享资源的访问,确保在分布式环境下同一时间只有一个节点能够执行受保护的代码块。Lock4j通过底层存储(如Redis、Zookeeper等)来实现分布式锁。不同的存储组件在具体的实现上稍有差别,但是其核心原理是类似的,下面是Lock4j 的基本工作原理:

  • 获取锁:

    • 当一个节点需要访问共享资源时,它会向底层存储发送请求以获取锁。请求中包含锁的唯一标识和节点信息。

  • 锁的持有:

    • 如果锁可用,节点将持有该锁,并可以安全地访问共享资源。持有锁的节点需要定期向存储系统发送心跳信号,以保持锁的有效性。

  • 释放锁:

    • 访问完成后,节点会释放锁,使得其他节点可以获取锁并访问资源。释放锁时,节点需要确保锁的状态已更新,以避免其他节点误认为锁仍然被持有。

2.4 Lock4j 应用场景

Lock4j 作为分布式锁框架,适用于各种需要协调分布式系统资源访问的场景。下面列举了几种常用的应用场景:

  • 防止重复提交/重复请求处理

    • 典型场景

      • 用户快速多次点击提交按钮

      • 消息队列消费者重复消费同一条消息

      • 定时任务重复执行

  • 分布式锁并发控制

    • 典型场景

      • 全局配置更新

      • 账户余额变更

      • 库存扣减(防止超卖)

  • 定时任务防重复执行

    • 典型场景

      • 分布式环境下多个实例的定时任务

      • 长时间执行的批处理任务

  • 分布式缓存

    • 典型场景

      • 缓存击穿保护

      • 缓存一致性维护

  • 关键业务流程串行化处理

    • 典型场景

      • 支付订单处理

      • 文件导入导出

      • 数据迁移任务

  • 分布式文件系统

    • 在分布式文件系统中,多个节点可能会同时访问和修改文件。

      • 通过使用Lock4j,可确保文件的一致性,避免数据损坏。例如,在云存储系统中,多个节点可能会同时上传和下载文件,通过分布式锁可以确保文件数据的完整性。

三、springboot 整合 lock4j

3.1 前置准备

3.1. 1 导入依赖

创建一个springboot工程,导入下面的依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.4</version><relativePath/>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><!-- 使用lock4j实现分布式锁   https://gitee.com/baomidou/lock4j/--><dependency><groupId>com.baomidou</groupId><artifactId>lock4j-core</artifactId><version>2.2.5</version></dependency><!--添加开源分布式锁Lock4j--><!--若使用redisTemplate作为分布式锁底层,则需要引入--><dependency><groupId>com.baomidou</groupId><artifactId>lock4j-redis-template-spring-boot-starter</artifactId><version>2.2.5</version></dependency></dependencies>

3.2 基于Redis实现分布式锁

基于Redis的实现是很常用的整合使用方式,当使用 Redis 作为底层存储时,Lock4j 主要依赖 Redis 的以下特性:

  • SETNX 命令(或 SET 命令的 NX 选项):原子性地设置键值,只有键不存在时才会设置成功

  • 过期时间:避免死锁

  • Lua 脚本:保证解锁操作的原子性

下面看具体的整合和使用过程。

3.2.1 添加配置文件信息

在工程的配置文件中添加下面的信息

  • 需要注意的是,配置文件中设置的lock4j的几个核心参数信息是全局生效的,比如acquire-timeout,这里全局设置的是3秒,但如果你在注解中再次配置了,则会覆盖配置文件中的这个值;

server:port: 8082lock4j:type: redisacquire-timeout: 3000  # 获取锁超时时间(毫秒)expire: 30000   # 锁过期时间(毫秒)retry-interval: 100  # 获取锁失败重试间隔(毫秒)#redis的链接配置信息redis:host: localhostdatabase: 1port: 6379

3.2.2 添加测试接口

在工程中增加如下接口,方便测试

package com.congge.web;import com.baomidou.lock.annotation.Lock4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LockController {//localhost:8082/testLock@Lock4j(keys = {"'key'"}, acquireTimeout = 1000, expire = 10000)@GetMapping("/testLock")public Object testLock(){long threadId = Thread.currentThread().getId();System.out.println(threadId + " : 获取到了锁,准备执行业务逻辑");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "testLock";}}

在上面这段代码中,在接口上面增加了一个 @Lock4j的注解,使用该注解即可实现分布式锁的功能,并且设置了注解中其他的参数,其核心参数包括:

  • key:锁的键,支持SpEL表达式

  • expire:锁的过期时间(毫秒)

  • timeout:获取锁的超时时间(毫秒)

  • retry:获取锁失败后的重试间隔(毫秒)

3.2.3 效果测试

在postman中模拟一下多线程的并发测试

然后通过控制台的结果输出不难看出,多个线程请求过来的时候,由于分布式锁的存在,所以未获取到锁的请求将会进行排队,等待前面的线程释放锁,拿到锁之后才能执行

3.3 基于Redission 实现

使用Redission 的实现方式与Redis的实现差不多,首先需要导入下面的依赖,配置文件信息和接口代码不用动,然后启动工程后再次测试,可以得到相同的效果。

         <dependency><groupId>com.baomidou</groupId><artifactId>lock4j-redisson-spring-boot-starter</artifactId><version>2.2.5</version></dependency>

3.4 基于Zookeeper实现

基于 Zookeeper 的实现主要是利用了zk的临时顺序节点来实现,还记得在使用zk实现分布式锁的机制吗,主要流程如下:

  1. 在指定路径下创建临时顺序节点

  2. 判断当前节点是否为最小序号节点

  3. 如果是则获取锁,否则监听前一个节点的删除事件

下面来看如何在代码中集成和使用。

3.4.1 启动zk服务

为了在代码中集成并使用zookeeper,需要本地或服务器启动一个zookeeper服务,这里直接在本机启动

3.4.2 导入下面的依赖

在pom文件中导入下面的依赖

<dependency><groupId>com.baomidou</groupId><artifactId>lock4j-zookeeper-spring-boot-starter</artifactId><version>2.2.5</version>
</dependency>

3.4.3 添加配置信息

在配置文件中添加下面的配置信息

#使用redis作为分布式锁
spring:coordinate:zookeeper:zkServers: 127.0.0.1:2181

3.4.4 添加测试接口

增加一个测试接口方便测试看效果

//localhost:8082/testZkLock
@Lock4j(keys = {"'key'"}, acquireTimeout = 1000, expire = 10000, executor = ZookeeperLockExecutor.class)
@GetMapping("/testZkLock")
public Object testZkLock(){long threadId = Thread.currentThread().getId();System.out.println(threadId + " : 获取到了zk的锁,准备执行业务逻辑");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "testLock";
}

3.4.5 模拟并发测试

在postman中,使用并发调用接口的方式进行测试

通过控制台输出效果可以看到,有了分布式锁的控制,可以确保请求的有序执行

四、Lock4j 功能扩展

如果默认的配置还不能满足实际的需求场景时,还可以使用lock4j提供的一些扩展点做补充,下面介绍几个点。

4.1 自定义执行器作用

lock4j 的自定义执行器主要用于扩展和定制分布式锁的实现方式

4.1.1 支持不同的分布式锁实现

作用:允许集成各种分布式锁技术,而不仅限于框架默认提供的几种。

典型场景

  • 当项目使用非主流分布式协调服务时(如非Redis、Zookeeper等)

  • 需要接入公司自研的分布式锁服务

  • 使用云服务商特有的分布式锁服务(如AWS DynamoDB Lock Client)

4.1.2 定制锁的获取和释放逻辑

作用:完全控制锁的获取和释放过程,实现特殊业务需求。

典型场景

  • 需要实现特定等待策略(如指数退避)

  • 需要添加额外的锁校验逻辑(如业务状态检查)

  • 需要记录详细的锁竞争指标和日志

4.1.3 适配特殊业务需求

作用:解决标准分布式锁无法满足的特殊业务场景。

典型场景

  • 需要实现租约机制(lease-based locking)

  • 需要支持不同级别的锁(如读锁/写锁)

  • 需要实现锁的自动续期功能

4.1.4 性能优化

作用:针对特定环境优化锁的性能表现。

典型场景

  • 针对高并发场景优化锁实现

  • 减少网络往返次数

  • 实现本地缓存加速

4.1.5 增强可靠性

作用:提供更健壮的锁机制,防止极端情况下的问题。

典型场景

  • 处理时钟漂移问题

  • 实现更安全的锁释放机制

  • 防止锁过期但业务未完成的情况

4.1.6 统一锁管理

作用:在异构环境中提供一致的锁管理接口。

典型场景

  • 混合使用多种锁实现(如部分用Redis,部分用数据库)

  • 需要统一的监控和管理界面

  • 实现锁的降级策略

4.2 自定义执行器使用

接下来通过两个案例演示下如何使用自定义执行器

4.2.1 基于zk的自定义执行器

添加一个自定义类,继承抽象类AbstractLockExecutor并重写内部的几个核心方法,参考下面的代码

package com.congge.config;import com.baomidou.lock.executor.AbstractLockExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Slf4j
@RequiredArgsConstructor
/*** 基于zk的分布式锁执行器 , 使用CuratorFramework作为ZooKeeper客户端,实现锁的获取和释放。*/
@Component
public class MyZookeeperLockExecutor extends AbstractLockExecutor<InterProcessMutex> {private final CuratorFramework curatorFramework;/*** 尝试获取分布式锁** @param lockKey 锁的关键字,用于在ZooKeeper中创建锁节点的路径。* @param lockValue 锁的值,可以用于进一步标识锁。* @param expire 锁的过期时间,未使用。* @param acquireTimeout 获取锁的超时时间。* @return 如果成功获取锁,返回InterProcessMutex实例;否则返回null。*/@Overridepublic InterProcessMutex acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {System.out.println("进入了zk的自定义锁执行器");// 检查CuratorFramework实例是否已启动if (!CuratorFrameworkState.STARTED.equals(curatorFramework.getState())) {log.warn("instance must be started before calling this method");return null;}// 构建锁节点的路径String nodePath = "/curator/lock4j/%s";try {// 创建InterProcessMutex实例,并尝试获取锁InterProcessMutex mutex = new InterProcessMutex(curatorFramework, String.format(nodePath, lockKey));final boolean locked = mutex.acquire(acquireTimeout, TimeUnit.MILLISECONDS);// 根据获取锁的结果,返回相应的锁实例或nullreturn obtainLockInstance(locked, mutex);} catch (Exception e) {// 获取锁过程中发生异常,返回nullreturn null;}}/*** 释放分布式锁。** @param key 锁的关键字,与获取锁时使用的key相同。* @param value 锁的值,与获取锁时使用的value相同。* @param lockInstance 锁实例,用于释放锁。* @return 如果成功释放锁,返回true;否则返回false。*/@Overridepublic boolean releaseLock(String key, String value, InterProcessMutex lockInstance) {System.out.println("进入了zk的自定义锁执行器释放锁");try {// 直接释放锁lockInstance.release();} catch (Exception e) {// 释放锁过程中发生异常,记录日志并返回falselog.warn("zookeeper lock release error", e);return false;}// 成功释放锁,返回truereturn true;}
}

添加如下测试接口,在自定义注解中指定executor 为自定义的类即可

//localhost:8082/testZkLock
//@Lock4j(keys = {"'key'"}, acquireTimeout = 1000, expire = 10000, executor = ZookeeperLockExecutor.class)
@Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor  = MyZookeeperLockExecutor.class)
@GetMapping("/testZkLock")
public Object testZkLock(){long threadId = Thread.currentThread().getId();System.out.println(threadId + " : 获取到了zk的锁,准备执行业务逻辑");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "testLock";
}

启动工程后,使用接口工具模拟并发测试

通过控制台输出日志可以看到获取锁的逻辑走到了自定义的zk执行器里面了

4.2.2 基于Redis的自定义执行器

同样,如果要使用基于Redis的自定义执行器,也需要首先自定义一个类继承AbstractLockExecutor抽象类,参考下面的代码,结合代码理解

package com.congge.config;import com.baomidou.lock.executor.AbstractLockExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.util.Collections;/*** Redis模板锁执行器,实现基于Redis的分布式锁,使用StringRedisTemplate和Lua脚本实现锁的获取和释放,提高锁操作的原子性*/
@Slf4j
@RequiredArgsConstructor
@Component
public class MyRedisTemplateLockExecutor extends AbstractLockExecutor<String> {/*** 获取锁的Lua脚本。* 该脚本用于尝试以NX选项设置键值对,实现锁的获取。*/private static final RedisScript<String> SCRIPT_LOCK = new DefaultRedisScript<>("return redis.call('set',KEYS[1]," +"ARGV[1],'NX','PX',ARGV[2])", String.class);/*** 释放锁的Lua脚本* 首先验证锁是否由当前持有者释放,如果是,则删除锁*/private static final RedisScript<String> SCRIPT_UNLOCK = new DefaultRedisScript<>("if redis.call('get',KEYS[1]) " +"== ARGV[1] then return tostring(redis.call('del', KEYS[1])==1) else return 'false' end",String.class);/*** 表示锁获取成功的固定字符串。*/private static final String LOCK_SUCCESS = "OK";/*** Redis字符串模板,用于执行Redis操作。*/private final StringRedisTemplate redisTemplate;/*** 尝试获取锁* 使用Lua脚本在Redis中执行SET命令,尝试获取锁** @param lockKey        键* @param lockValue      值,唯一的标识* @param expire         锁的期时间,单位为毫秒* @param acquireTimeout 获取锁的超时时间,单位为毫秒* @return 一个表示锁实例的字符串,如果获取锁失败,则为null*/@Overridepublic String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {System.out.println("尝试获取redis的锁");String lock = redisTemplate.execute(SCRIPT_LOCK,redisTemplate.getStringSerializer(),redisTemplate.getStringSerializer(),Collections.singletonList(lockKey),lockValue, String.valueOf(expire));final boolean locked = LOCK_SUCCESS.equals(lock);return obtainLockInstance(locked, lock);}/*** 释放锁* 使用Lua脚本在Redis中执行验证和删除操作,以确保只有锁的持有者才能释放锁** @param key          键* @param value        值,用于验证锁的所有权。* @param lockInstance 表示锁实例的字符串* @return 表示释放锁是否成功的布尔值*/@Overridepublic boolean releaseLock(String key, String value, String lockInstance) {System.out.println("释放锁");String releaseResult = redisTemplate.execute(SCRIPT_UNLOCK,redisTemplate.getStringSerializer(),redisTemplate.getStringSerializer(),Collections.singletonList(key), value);return Boolean.parseBoolean(releaseResult);}
}

再添加一个测试接口,如下:

//localhost:8082/testRedisLock
@Lock4j(keys = {"'key'"}, acquireTimeout = 1000, expire = 10000, executor  = MyRedisTemplateLockExecutor.class)
@GetMapping("/testRedisLock")
public Object testRedisLock(){long threadId = Thread.currentThread().getId();System.out.println(threadId + " : 获取到了redis的锁,准备执行业务逻辑");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "testLock";
}

使用接口工具模拟一下并发请求,如下效果

通过控制台的输出可以看到自定义的执行器生效了

4.2.3 自定义锁的key生成策略

默认情况下,如果不做任何的设置,以redis为例,加锁时生成的key的前缀统一为:lock4j,这个在配置文件中也可以做如下显示的设置

加锁过程中,可以看到如下效果,key的格式为 lock4j开头

某些情况下,如果你想定制自己的key的生成策略,可以通过自定义一个类并实现LockKeyBuilder这个接口,如下:

package com.congge.config;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;import com.baomidou.lock.LockKeyBuilder;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;@Component
public class DefaultLockKeyBuilder implements LockKeyBuilder {private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();private static final ExpressionParser PARSER = new SpelExpressionParser();private BeanResolver beanResolver;public DefaultLockKeyBuilder(BeanFactory beanFactory) {this.beanResolver = new BeanFactoryResolver(beanFactory);}public String buildKey(MethodInvocation invocation, String[] definitionKeys) {System.out.println("开始构建自定义的key");Method method = invocation.getMethod();return definitionKeys.length <= 1 && "".equals(definitionKeys[0]) ? "" : this.getSpelDefinitionKey(definitionKeys, method, invocation.getArguments());}protected String getSpelDefinitionKey(String[] definitionKeys, Method method, Object[] parameterValues) {StandardEvaluationContext context = new MethodBasedEvaluationContext((Object)null, method, parameterValues, NAME_DISCOVERER);context.setBeanResolver(this.beanResolver);List<String> definitionKeyList = new ArrayList(definitionKeys.length);String[] var6 = definitionKeys;int var7 = definitionKeys.length;for(int var8 = 0; var8 < var7; ++var8) {String definitionKey = var6[var8];if (definitionKey != null && !definitionKey.isEmpty()) {String key = (String)PARSER.parseExpression(definitionKey).getValue(context, String.class);definitionKeyList.add(key);}}return StringUtils.collectionToDelimitedString(definitionKeyList, ".", "", "");}
}

在buildkey的方法里面,你就可以根据自己的需求自定义key的生成策略了,再次请求接口,可以看到代码已经走到这里了

五、写在文末

本文详细的介绍了分布式锁技术组件Lock4j的使用,并通过案例代码演示了相关功能的使用,希望对看到的同学有用哦,本篇到此结束,感谢观看。

相关文章:

【微服务】SpringBoot 整合 Lock4j 分布式锁使用详解

目录 一、前言 二、Lock4j 概述 2.1 Lock4j 介绍 2.1.1 Lock4j 是什么 2.1.2 Lock4j 主要特征 2.1.3 Lock4j 技术特点 2.2 Lock4j 支持的锁类型 2.3 Lock4j 工作原理 2.4 Lock4j 应用场景 三、springboot 整合 lock4j 3.1 前置准备 3.1. 1 导入依赖 3.2 基于Redis…...

如何将前端组件封装并发布到npm的步骤详解

以下是封装前端组件并发布至npm仓库的完整步骤指南,结合多个最佳实践和常见问题解决方案: 一、环境准备与项目初始化 创建项目结构 • 使用Vue CLI或Create React App初始化项目: vue create my-component-lib # Vue npx create-react-app my-component-lib --template ty…...

【QT】QWidget 概述与核心属性(API)

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Qt 目录 一&#xff1a;&#x1f525; 控件概述 &#x1f98b; 控件体系的发展阶段 二&#xff1a;&#x1f525; QWidget 核心属性 &#x1f98b; 核心属性概览&#x1f98b; 用件可用&#xff08…...

vue + uniapp 实现仿百度地图/高德地图/美团/支付宝 滑动面板 纯css 实现

概要 使用百度地图、各种单车APP时&#xff0c;对地图上的滑动面板很感兴趣&#xff0c;于是根据自己的理解实现了一下 之前用的js实现&#xff0c;滑动的时候没有原生好 这一次用的css实现 代码 <template><view class"container"><mapstyle"…...

124. 二叉树中的最大路径和

https://leetcode.cn/problems/binary-tree-maximum-path-sum/description/?envTypestudy-plan-v2&envIdtop-interview-150对于这题我开始的思路是路径我们可以看作是一条线&#xff0c;我们确定一个点后可以往两侧延伸&#xff08;就是左右子树的方向&#xff09;&#x…...

spark运行架构

运行架构&#xff1a;Spark采用master - slave结构&#xff0c;Driver作为master负责作业任务调度&#xff0c;Executor作为slave负责实际执行任务。 核心组件&#xff1a; Driver&#xff1a;执行Spark任务的main方法&#xff0c;负责将用户程序转化为作业、调度任务、跟踪E…...

开源的7B参数OCR视觉大模型:RolmOCR

1. 背景介绍 早些时候&#xff0c;Allen Institute for AI 发布了 olmOCR&#xff0c;这是一个基于 Qwen2-VL-7B 视觉语言模型&#xff08;VLM&#xff09;的开源工具&#xff0c;用于处理 PDF 和其他复杂文档的 OCR&#xff08;光学字符识别&#xff09;。开发团队对该工具的…...

Http代理服务器选型与搭建

代理服务器选型-Squid 缓存加速 缓存频繁访问的网页、图片等静态资源&#xff0c;减少对原始服务器的重复请求&#xff0c;提升响应速度支持HTTP、HTTPS、FTP等协议&#xff0c;通过本地缓存直接响应客户端请求 访问控制 基于ACL&#xff08;访问控制列表&#xff09;实现精细…...

如何实现Microsoft Word (.docx) 格式到 FastReport .NET (.frx) 文件的转换

现代数据处理技术和文档工作流自动化需要集成各种文件格式&#xff0c;以确保软件产品之间的无缝交互。Microsoft Word 凭借其丰富的功能&#xff0c;已成为最受欢迎的文本编辑器之一&#xff0c;适用于各种任务。 有时&#xff0c;您可能需要将这些文档转换为其他应用程序特定…...

雷电多开器自动化运行、自动登录APP刷新日用户活跃量

文章目录 简介接单价格代码对爬虫、逆向感兴趣的同学可以查看文章,一对一小班教学(系统理论和实战教程)、提供接单兼职渠道:https://blog.csdn.net/weixin_35770067/article/details/142514698 简介 客户有一个APP,需要在雷电模拟器每天自动运行APP,每台模拟器设置不同的I…...

Dify教程01-Dify是什么、应用场景、如何安装

Dify教程01-Dify是什么、应用场景、如何安装 大家好&#xff0c;我是星哥&#xff0c;上篇文章讲了Coze、Dify、FastGPT、MaxKB 对比&#xff0c;今天就来学习如何搭建Dify。 Dify是什么 **Dify 是一款开源的大语言模型(LLM) 应用开发平台。**它融合了后端即服务&#xff08…...

《深入探秘:分布式软总线自发现、自组网技术原理》

在当今数字化浪潮中&#xff0c;分布式系统的发展日新月异&#xff0c;而分布式软总线作为实现设备高效互联的关键技术&#xff0c;其自发现与自组网功能宛如打开智能世界大门的钥匙&#xff0c;为多设备协同工作奠定了坚实基础。 分布式软总线的重要地位 分布式软总线是构建…...

spring扫描自定义注解注册bean

前言 我们知道&#xff0c;在spring中&#xff0c;我们只需要加上注解Component&#xff0c;就可以自动注入到spring容器中&#xff0c;如果我们自定义注解&#xff0c;怎么让spring识别到&#xff0c;注入到容器中呢&#xff0c;下面我们来看看。 基础使用 自定义注解 Tar…...

【RL系列】StepFun之Open-Reasoner-Zero

1. 简介 开源了一个大规模RL训练框架之Open-Reasoner-Zero&#xff0c;仅使用vanilla PPO&#xff0c;GAE中参数 λ 1 , γ 1 \lambda 1, \gamma 1 λ1,γ1&#xff0c;rule-based reward&#xff0c;不需要KL regularization就可以增加response length和benchmark上的指标。…...

括号匹配问题--栈

括号匹配问题 栈的应用代码概览栈操作函数详解1.初始化栈&#xff08;stackInit&#xff09;2.向栈中压入元素&#xff08;stackpush&#xff09;3.获取栈顶元素&#xff08;stacktop&#xff09;4.弹出栈顶元素&#xff08;stackpop&#xff09;5.销毁栈&#xff08;stackdest…...

LangChain4j(7):Springboot集成LangChain4j实现知识库RAG

我们之前的直接整合进SpringBoot进行实战&#xff0c;最终其实还会将查询到的内容&#xff0c;和对话上下文组合起来&#xff0c;发给LLM为我们组织语言进行回答: 配置一个Content Retriever 内容检索器&#xff0c;提供向量数据库和向量模型及其他参数将内容检索器绑定到AiSe…...

企业使用Excel开展数据分析限制和建议完整版

Excel作为企业数据分析的常用工具&#xff0c;虽然功能强大&#xff0c;但也存在一些限制和使用时的注意事项。以下是综合整理的关键点&#xff1a; 一、Excel在企业数据分析中的限制 数据处理规模有限 Excel的行列限制&#xff08;如Excel 2019及之前版本最多支持1,048,576行…...

41、web前端开发之Vue3保姆教程(五 实战案例)

一、项目简介和需求概述 1、项目目标 1.能够基于Vue3创建项目 2.能够基本Vue3相关的技术栈进行项目开发 3.能够使用Vue的第三方组件进行项目开发 4.能够理解前后端分离的开发模式 2、项目概述 使用Vue3结合ElementPlus,ECharts工具实现后台管理系统页面,包含登录功能,…...

Quill富文本编辑器支持自定义字体(包括新旧两个版本,支持Windings 2字体)

文章目录 1 新版&#xff08;Quill2 以上版本&#xff09;2 旧版&#xff08;Quill1版本&#xff09; 1 新版&#xff08;Quill2 以上版本&#xff09; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta n…...

Flutter命令行打包打不出ipa报错

Flutter打包ipa报错解决方案 在Flutter开发中&#xff0c;打包iOS应用时可能会遇到以下错误&#xff1a; error: exportArchive: The data couldn’t be read because it isn’ in the correct format. 或者 Encountered error while creating the IPA: error: exportArchive…...

UV安装与使用

1. 概述 GitHub&#xff1a;astral-sh/uv: An extremely fast Python package and project manager, written in Rust. 官网&#xff1a;uv An extremely fast Python package and project manager, written in Rust. 效率神器&#xff0c;基于Rust实现&#xff0c;比传统工具快…...

SQL练习题

数据表介绍 –1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别–2.课程表 Course(CId,Cname,TId) --CId 课程编号,Cname 课程名称,TId 教师编号–3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名–4.成绩表…...

Rust Command无法执行*拓展解决办法

async fn run_cmd_async_out<I, S>(cmd: &str, args: I, timeout_s: u64, with_http_proxy: bool) -> Result<String> whereI: IntoIterator<Item S>,S: AsRef<OsStr>, {let mut cmd tokio::process::Command::new(cmd);// 让 sh 来运行命令&…...

利用Hadoop MapReduce实现流量统计分析

在现代大数据时代&#xff0c;处理和分析海量数据是一项常见的任务。Hadoop MapReduce提供了一种高效的方式来处理分布式数据集。本文将通过一个具体的示例——流量统计分析&#xff0c;来展示如何使用Hadoop MapReduce进行数据处理。 项目背景 在电信行业中&#xff0c;对用…...

Spring Boot应用程序接入ELK-003

Spring Boot应用程序接入ELK 一、项目依赖集成 在将Spring Boot应用程序接入ELK日志搜索引擎时&#xff0c;首先要在项目中集成相关依赖&#xff1a; &#xff08;一&#xff09;Logstash依赖 <dependency><groupId>net.logstash.logback</groupId><a…...

spark(一)

本节课围绕Spark Core展开深入学习&#xff0c;了解了Spark的运行架构、核心组件、核心概念以及提交流程&#xff0c;明晰其整体运行机制与各部分协作逻辑。重点聚焦于两个核心组件&#xff1b;对RDD相关概念进行了细致学习&#xff0c;包括其核心属性、执行原理、序列化方式、…...

绿电直供零碳园区:如何用清洁能源重塑企业竞争力?

引言 在全球积极应对气候变化的大背景下&#xff0c;“双碳” 目标已成为世界各国实现可持续发展的关键战略方向。我国也明确提出要在 2030 年前实现碳达峰&#xff0c;2060 年前实现碳中和&#xff0c;这一宏伟目标的提出&#xff0c;对各行各业都产生了深远影响&#xff0c;…...

国家科技奖项目答辩ppt设计_科技进步奖PPT制作_技术发明奖ppt美化_自然科学奖ppt模板

国家科学技术奖 为了奖励在科学技术进步活动中做出突出贡献的公民、组织&#xff0c;调动科学技术工作者的积极性和创造性&#xff0c;加速科学技术事业的发展&#xff0c;提高综合国力而设立的一系列奖项。每两三年评选一次。 科技奖ppt案例 WordinPPT / 持续为双一流高校、…...

LLM应用实战2-理解Tokens

文章目录 基本定义Tokenization 的作用主流 Tokenization 算法示例示例GPT-4o&GPT-4o miniGPT-3.5 & GPT-4 基本定义 Tokens 是大型语言模型&#xff08;LLM&#xff09;处理文本或代码的最小语义单元&#xff0c;可包含以下形式&#xff1a; 字符&#xff08;如英文…...

【Java面试系列】Spring Boot微服务架构下的分布式事务处理与性能优化详解 - 3-5年Java开发必备知识

【Java面试系列】Spring Boot微服务架构下的分布式事务处理与性能优化详解 - 3-5年Java开发必备知识 引言 在当今的微服务架构中&#xff0c;分布式事务处理和性能优化是面试中经常被问及的高频话题。随着系统规模的扩大&#xff0c;如何保证数据一致性和系统性能成为了开发者…...

NO.80十六届蓝桥杯备战|数据结构-字符串哈希|兔子与兔子(C++)

回忆&#xff1a;哈希函数与哈希冲突 哈希函数&#xff1a;将关键字映射成对应的地址的函数&#xff0c;记为 Hash(key) Addr 。哈希冲突&#xff1a;哈希函数可能会把两个或两个以上的不同关键字映射到同⼀地址&#xff0c;这种情况称为哈希冲突。 字符串哈希 定义⼀个把字…...

Spring MVC 请求类型注解详解

Spring MVC 请求类型注解详解 1. 核心注解分类 Spring MVC 中的请求处理注解分为以下几类&#xff1a; 类别注解示例作用范围方法级注解RequestMapping, GetMapping 等方法级别参数级注解RequestParam, RequestBody方法参数模型/会话注解ModelAttribute, SessionAttributes方…...

RabbitMQ的死信队列和ttl

TTL ttl即过期时间&#xff0c;rbbitmq可以对队列和消息设置过期时间&#xff0c;当消息到存活时间之后&#xff0c;还没有被消费&#xff0c;就会被自动清除 例如&#xff1a;在网上购物&#xff0c;经常会遇到一个场景&#xff0c;当下单超过24小时还未付款&#xff0c;订单…...

[特殊字符] Hyperlane:Rust 高性能 HTTP 服务器库,开启 Web 服务新纪元!

&#x1f680; Hyperlane&#xff1a;Rust 高性能 HTTP 服务器库&#xff0c;开启 Web 服务新纪元&#xff01; &#x1f31f; 什么是 Hyperlane&#xff1f; Hyperlane 是一个基于 Rust 语言开发的轻量级、高性能 HTTP 服务器库&#xff0c;专为简化网络服务开发而设计。它支…...

【后端开发】Spring MVC-常见使用、Cookie、Session

文章目录 代码总结初始化传递参数单参数多参数 传递对象后端参数重命名&#xff08;后端参数映射&#xff09;必传参数设置非必传参数 传递数组传递集合传递JSON数据JSON语法JSON格式转换JSON优点传递JSON对象 获取URL中参数传递文件 Cookie与SessionCookieCookie机制 SessionC…...

Element Plus 去掉表格外边框

使用el-table组件拖拽时&#xff0c; 想使用自定义样式进行拖拽, 想去掉外边框&#xff0c; 并在表头加入竖杠样式 css代码&#xff1a; <style lang"less" scoped>// 表格右边框线 .el-table--border::after {width: 0; }// 表格上边框线 :deep(.el-table__i…...

安全厂商安全理念分析

奇安信&#xff08;toB企业安全&#xff09; 安全理念&#xff1a;率先提出 “内生安全” 理念。即把安全能力内置到信息化环境中&#xff0c;通过信息化系统和安全系统的聚合、业务数据和安全数据的聚合、IT 人才和安全人才的聚合&#xff0c;让安全系统像人的免疫系统一样&a…...

GaussDB Plan Hint调优实战:从执行计划控制到性能优化

GaussDB Plan Hint调优实战&#xff1a;从执行计划控制到性能优化 一、GaussDB Plan Hint核心价值 执行计划控制原理 mermaid graph TD A[SQL提交] --> B(优化器决策) B --> C{使用Hint?} C -->|是| D[强制指定执行路径] C -->|否| E[自动生成最优计划] D --&g…...

【力扣hot100题】(078)跳跃游戏Ⅱ

好难啊&#xff0c;我愿称之为跳崖游戏。 依旧用了两种方法&#xff0c;一种是我一开始想到的&#xff0c;一种是看答案学会的。 我自己用的方法是动态规划&#xff0c;维护一个数组记录到该位置的最少步长&#xff0c;每遍历到一个位置就嵌套循环遍历这个位置能到达的位置&a…...

基于 DeepSeek API 实现一个简单的数据分析 Agent

写在前面 本文将带你一步步了解: 什么是(简单的)数据分析 Agent?为什么使用 LLM 进行数据分析?如何利用 DeepSeek API 的能力?设计并实现一个基于 Python 和 Pandas 的基础数据分析 Agent。探讨其局限性、安全考量及未来方向。我们的目标是构建一个简单的 Agent,它能理…...

VUE3+TS+elementplus+Django+MySQL实现从前端增加数据存入数据库,并显示在前端界面上

一、前言 前面实现了从数据库读取数据&#xff0c;显示在前端界面上VUE3TSelementplusDjangoMySQL实现从数据库读取数据&#xff0c;显示在前端界面上&#xff0c;以及使用VUE3TSelementplus创建一个增加按钮。今天通过在前端的增加功能&#xff0c;新增数据&#xff0c;传到后…...

Django 创建CSV文件

Django使用Python内置的CSV库来创建动态的CSV&#xff08;逗号分隔值&#xff09;文件。我们可以在项目的视图文件中使用这个库。 让我们来看一个例子&#xff0c;这里我们有一个Django项目&#xff0c;我们正在实现这个功能。创建一个视图函数 getfile() 。 Django CSV例子 …...

最新版RubyMine超详细图文安装教程,带补丁包(2025最新版保姆级教程)

目录 前言 一、RubyMine最新版下载 二、RubyMine安装 三、RubyMine补丁 四、运行RubyMine 前言 RubyMine是由JetBrains开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为Ruby和Ruby on Rails开发者设计&#xff0c;提供智能代码补全、调试、测试、版本控制集…...

spring之JdbcTemplate、GoF之代理模式、面向切面编程AOP

一、JdbcTemplate JdbcTemplate是Spring提供的一个JDBC模板类&#xff0c;是对JDBC的封装&#xff0c;简化JDBC代码。 当然&#xff0c;你也可以不用&#xff0c;可以让Spring集成其它的ORM框架&#xff0c;例如&#xff1a;MyBatis、Hibernate等。 接下来我们简单来学习一下&…...

【QT】QT中的文件IO

QT中的文件IO 一、有关文件IO的类二、步骤1、定义QFile的对象,与要读写的文件绑定在一起2、打开文件3、读写文件1&#xff09;读取文件2&#xff09;写入文件 4、关闭文件5、示例代码&#xff1a; 三、QString和QByteArray之间的转换1、方法2、示例代码&#xff1a; 四、QFileI…...

linux安装mysql常出现的问题

wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm rpm -ivh mysql-community-release-el7-5.noarch.rpm yum update yum install mysql-server 权限设置&#xff1a; chown -R mysql:mysql /var/lib/mysql/ 初始化 MySQL&#xff1a; mysqld --initiali…...

ArcGIS Engine开发教程--从零搭建GIS桌面应用

目录 一、ArcGIS Engine简介 1.1 什么是ArcGIS Engine&#xff1f; 1.2 应用场景 二、环境搭建 2.1 安装准备 2.2 配置项目 三、核心对象与基础概念 3.1 核心组件 3.2 接口编程 四、实战&#xff1a;开发简易地图查看器 4.1 加载地图文档 4.2 添加矢量图层 4.3 实…...

DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

Conda使用方法详解

Conda是一个开源的包管理和环境管理系统&#xff0c;主要用于Python/R等科学计算领域&#xff0c;可以轻松管理不同项目的依赖关系。以下是Conda的详细使用方法&#xff1a; 一、安装与配置 1.安装Miniconda/Anaconda Miniconda是精简版&#xff0c;只包含conda和Python Ana…...

CausalML 基于机器学习算法的因果推理方法

CausalML 是一个 Python 包&#xff0c;它使用基于最新研究的机器学习算法提供一套提升建模和因果推理方法。它提供了一个标准界面&#xff0c;允许用户从实验或观察数据中估计条件平均处理效应 &#xff08;CATE&#xff09;&#xff0c;也称为个体治疗效应 &#xff08;ITE&a…...