# distributed-lock **Repository Path**: yangspgao/distributed-lock ## Basic Information - **Project Name**: distributed-lock - **Description**: 分布式锁 及 防重复提交 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 10 - **Created**: 2018-05-23 - **Last Updated**: 2024-11-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # distributed-lock #### 项目介绍 A:分布式锁 B:防重复提交 一个通过redis 或 zookeeper 或 Ehcache 等集中式存储而实现的弱一致性分布式锁;以及在此基础上实现的防重复提交功能; 通过@EnableDistributedLock 注解开启分布式锁 通过@EnableResubmit 注解开启防重复提交功能 目前暂时只支持redis 和 Ehcache(非分布式); 后期可扩展至zookeeper; 弱一致性:依赖redis服务器的稳定性,如果加锁成功,对应reids服务器down掉,又重启,就有可能导致锁失效; #### 使用范围 目前只支持spring boot项目 #### 软件架构 #### 安装教程 1. 下载源代码 2. 通过maven引入依赖jar包 #### 使用说明 0. 依赖 项目引用redis,并创建redis对应的bean:RedisTemplate或JedisCluster 任一bean即可 1. 分布式锁 A: SpringBoot main class中引入注解:@EnableDistributedLock ``` @SpringBootApplication @EnableDistributedLock //启动分布式锁(前提:项目中已经存在RedisTemplate或JedisCluster或org.ehcache.Manager 对应的bean) public class App { public static void main( String[] args ) { //启动项目 SpringApplication.run(App.class,args); } } ``` B: 业务代码 ``` //业务bean中引用lockManager @Autowired LockManager lockManager; //业务代码中使用lock(多例:一个lock只允许tryLock一次) DistributedLock lock = lockManager.getLock(); try{ boolean locked = lock.tryLock(key,50); //key:锁(如:业务中的订单号); 50:过期时间(秒),本锁最长锁这么久,如果时间内没有解锁,则自动解锁 if(locked){ //... 业务 } }finaly{ boolean unlocked = lock.unlock(); } ``` 2. 防重复提交 A: SpringBoot main class中引入注解:@EnableResubmit ``` @SpringBootApplication @EnableResubmit(defaultResolver = HttpStatusResolver.class) //开户防重复提交(默认已经开户分布锁);defaultResolver指定了加锁失败后如何处理;如果不指定默认抛出异常 public class App { public static void main( String[] args ) { //启动项目 SpringApplication.run(App.class,args); } } ``` B: 业务代码 业务方法上,使用@Resubmit注解;lockey:可以通过 spEL表达式获取方法参数中的值做为锁; expireSeconds 为锁过期时间,默认5秒; ``` @Resubmit(lockPrefix = "resubmit:pre:" ,lockKey = "#name",expireSeconds = 5) public String resubmit(String name){ return "name"; } ``` C:重复提交处理 aop拦截到重复提交后,通过ResubmitResolver进行处理; 默认会创建三个Resolver bean: - ExceptionResolver 发现重复提交,直接抛出异常 - HttpStatusResolver http请求:发现重复提交,返回423 http状态码; - PrintResolver; 发现重复提交,仅仅打印日志;仅为测试,无业务意义; 默认使用的Resolver,可能通过@EnableResubmit(defaultResolver = {PrintResolver.class})指定 自定义Resolver: 实际业务中,除了使用统一的默认Resolver外,部分业务可能需要特殊的Resovler,此时可以由业务方开发自己的,实现了ResubmitResolver接口的Resolver; - 配置业务方自己实现的Resolver为spring Bean - 通过注解@Resubmit中的resolverBeanName 指定业务方自己的Resolver bean name ``` @Resubmit(lockKey = "#name",resolver = PrintResolver.class) ``` 3. 特殊要求 ehcache做为锁缓存时,是非分布式锁,除非使用ehcache搭建分布式缓存; 主要用于测试和小项目; ehcache要求:创建一个Key class = String/Object/Serializable, value class = EhcacheValue,过期策略=EhcacheValueExpiryPolicy的缓存; 如: ```$java CacheConfiguration distributedLockCfg = CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, //key对应的类型 EhcacheValue.class, //value对应的类型 ResourcePoolsBuilder.newResourcePoolsBuilder() //存储池配置(内存--磁盘) .heap(1000L,EntryUnit.ENTRIES)//堆内存大小(条数) .offheap(10L,MemoryUnit.MB) //堆外内存(容量) .disk(50L,MemoryUnit.MB) //磁盘 ) //过期策略配置 .withExpiry(new EhcacheValueExpiryPolicy(Duration.ofSeconds(5),null,null)) .build(); // 2: CacheManager管理缓存 org.ehcache.CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() // 硬盘持久化地址 .with(CacheManagerBuilder.persistence(cachePath)) // 设置一个默认缓存配置 //.withCache("defaultCache", defaultConfig) //设置一个分布式锁的cache(过期时间由元素值决定) .withCache("distributedLockCache",distributedLockCfg) //创建之后立即初始化 .build(true); ``` #### 参与贡献