利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的。
redis官方推荐的分布式锁实现为redisson http://ifeve.com/redis-lock/
以下为spring boot实现分布式锁的步骤
项目pom中需要添加官方依赖 我是1.8JDK固为
<!-- redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.4.2</version> </dependency>
定义一个分布式锁的回调类
package com.example.demo.redis2; /** * 分布式锁回调接口 * * @author lk */ public interface DistributedLockCallback<T> { /** * 调用者必须在此方法中实现需要加分布式锁的业务逻辑 * * @return */ public T process(); /** * 得到分布式锁名称 * * @return */ public String getLockName(); }
分布式锁操作模板
package com.example.demo.redis2; import java.util.concurrent.TimeUnit; /** * 分布式锁操作模板 * * @author lk */ public interface DistributedLockTemplate { /** * 使用分布式锁,使用锁默认超时时间。 * * @param callback * @return */ public <T> T lock(DistributedLockCallback<T> callback); /** * 使用分布式锁。自定义锁的超时时间 * * @param callback * @param leaseTime 锁超时时间。超时后自动释放锁。 * @param timeUnit * @return */ public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit); }
使用redisson最简单的Single instance mode实现分布式锁模板接口
package com.example.demo.redis2; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import java.util.concurrent.TimeUnit; /** * Single Instance mode 分布式锁模板 * * @author lk */ public class SingleDistributedLockTemplate implements DistributedLockTemplate { private static final long DEFAULT_TIMEOUT = 5; private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS; private RedissonClient redisson; public SingleDistributedLockTemplate() { } public SingleDistributedLockTemplate(RedissonClient redisson) { this.redisson = redisson; } @Override public <T> T lock(DistributedLockCallback<T> callback) { return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT); } @Override public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) { RLock lock = null; try { lock = redisson.getLock(callback.getLockName()); lock.lock(leaseTime, timeUnit); return callback.process(); } finally { if (lock != null) { lock.unlock(); } } } public void setRedisson(RedissonClient redisson) { this.redisson = redisson; } }
创建可以被spring管理的 Bean
package com.example.demo.redis2; import java.io.IOException; import java.io.InputStream; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.log4j.Logger; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.FactoryBean; /** * 创建分布式锁模板实例的工厂Bean * * @author lk */ public class DistributedLockFactoryBean implements FactoryBean<DistributedLockTemplate> { private Logger logger = Logger.getLogger(DistributedLockFactoryBean.class); private LockInstanceMode mode; private DistributedLockTemplate distributedLockTemplate; private RedissonClient redisson; @PostConstruct public void init() { String ip = "127.0.0.1"; String port = "6379"; Config config=new Config(); config.useSingleServer().setAddress(ip+":"+port); redisson=Redisson.create(config); System.out.println("成功连接Redis Server"+"\t"+"连接"+ip+":"+port+"服务器"); } @PreDestroy public void destroy() { logger.debug("销毁分布式锁模板"); redisson.shutdown(); } @Override public DistributedLockTemplate getObject() throws Exception { switch (mode) { case SINGLE: distributedLockTemplate = new SingleDistributedLockTemplate(redisson); break; } return distributedLockTemplate; } @Override public Class<?> getObjectType() { return DistributedLockTemplate.class; } @Override public boolean isSingleton() { return true; } public void setMode(String mode) { if (mode==null||mode.length()<=0||mode.equals("")) { throw new IllegalArgumentException("未找到dlm.redisson.mode配置项"); } this.mode = LockInstanceMode.parse(mode); if (this.mode == null) { throw new IllegalArgumentException("不支持的分布式锁模式"); } } private enum LockInstanceMode { SINGLE; public static LockInstanceMode parse(String name) { for (LockInstanceMode modeIns : LockInstanceMode.values()) { if (modeIns.name().equals(name.toUpperCase())) { return modeIns; } } return null; } } }
配置进spring boot中
package com.example.demo.redis2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by LiaoKe on 2017/5/22. */ @Configuration public class BeanConfig { @Bean public DistributedLockFactoryBean distributeLockTemplate(){ DistributedLockFactoryBean d = new DistributedLockFactoryBean(); d.setMode("SINGLE"); return d; } }
目前为止已经可以使用。
为了验证锁是否成功,我做了如下例子。
首先建立了一个数据库实体(使用的JPA),模拟被购买的商品数量,当被购买后,num+1
在高并发环境下,这必定会有问题,因为在查询之后的设值,存在对同一数据库源的操作。
package com.example.demo.redis2.entity; import org.hibernate.annotations.GenericGenerator; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 测试类实体 * Created by LiaoKe on 2017/5/22. */ @Entity public class TestEntity { @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(name = "system-uuid", strategy = "uuid") private String id; private Integer num; public String getId() { return id; } public void setId(String id) { this.id = id; } public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } }
具体数据库操作,加锁和不加锁的操作,要注意我使用了@Async,异步任务注解,我没有配置线程池信息,使用的默认线程池。
package com.example.demo.redis2.service; import com.example.demo.redis2.DistributedLockCallback; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.dao.TestEntityRepository; import com.example.demo.redis2.entity.TestEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * Created by LiaoKe on 2017/5/22. */ @Service public class AsyncService { @Resource TestEntityRepository ts; @Resource DistributedLockTemplate distributedLockTemplate; /** * 加锁 */ @Async public void addAsync(){ distributedLockTemplate.lock(new DistributedLockCallback<Object>(){ @Override public Object process() { add(); return null; } @Override public String getLockName() { return "MyLock"; } }); } /** * 未加锁 */ @Async public void addNoAsync(){ add(); } /** * 测试异步方法 * 在不加分布式锁的情况下 * num数目会混乱 */ @Async private void add(){ if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()+1); ts.saveAndFlush(dbt); } } }
最后为了测试简单跑了两个接口
package com.example.demo; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.service.AsyncService; import oracle.jrockit.jfr.StringConstantPool; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @SpringBootApplication @RestController @EnableAsync public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Resource AsyncService as; @GetMapping("") public void test(){ for(int i = 0 ;i<10000;i++){ as.addNoAsync(); } } @GetMapping("lock") public void test2(){ for(int i = 0 ;i<10000;i++){ as.addAsync(); } } }
访问localhost:8888 及 localhost:8888/lock
在不加锁的情况下
数据库已经爆炸了
最后得到的数据奇奇怪怪
使用加锁后的访问
可以看到库存增加绝对正确。
此处并未使用任何数据库锁,并且基于redis,可在不同的网络节点实现上锁。
这只是简单的实现,在真正的生产环境中,还要注意许多问题,超时和放锁时机需要好好研究,在此不便贴真正项目代码。
参考博客:http://layznet.iteye.com/blog/2307179 感谢作者
相关推荐
本篇文章将详细探讨如何使用Redisson实现Redis分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...
NULL 博文链接:https://liaoke0123.iteye.com/blog/2375556
Java开发基于SpringBoot+WebSocket+Redis分布式即时通讯群聊系统。一个基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统。适用于直播间聊天、游戏内聊天、客服聊天等临时性群聊场景。 ...
3. **简单的API**:Redis提供了丰富的命令集,如`SETNX`、`EXPIRE`等,方便实现锁的逻辑。 要在SpringBoot中集成Redis,你需要先在项目中添加对应的依赖。SpringBoot提供了对Redis的自动配置支持,只需在`pom.xml`...
RedLock是Redisson中的一个特性,它由Redis社区提出,旨在提供一种更健壮的分布式锁实现,比单节点Redis的`setnx`命令更可靠。 RedLock的实现基于以下假设: 1. 时钟偏移:所有参与的Redis服务器的时钟大致同步。 2...
Spring Boot+Redis 分布式锁:模拟抢单
本次文档将围绕Spring Boot结合Redis实现分布式锁的机制进行详细解读,并通过模拟抢单的场景来展示其实际运用。 首先,了解Redis的分布式锁是如何运作的至关重要。在Redis的诸多操作中,`set`命令的`nx`选项(即`...
本篇文章将详细讲解如何利用 Spring Boot 结合 Redis 实现分布式锁。 首先,理解分布式锁的基本概念。分布式锁是在分布式系统中用于控制并发访问的机制,它允许只有一台机器或一个线程在任何时候持有锁,从而确保对...
Redis分布式锁利用Redis的SETNX命令实现分布式锁,确保在分布式环境下对资源的互斥访问。 Redisson分布式锁使用Redisson库提供的分布式锁实现,提供更高级的功能,如锁的自动续期和可重入锁。 2. 锁的续期和释放...
基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息...
http://localhost:8080/ass/getUser 测试是否登录 http://localhost:8080/any/user/passLogin 登录测试 http://localhost:8080/lock redis分布式锁测试
7. **Spring Boot Starter**:对于Spring Boot应用,工具包提供的Starter可以通过简单的配置,自动将Redis分布式锁集成到应用中,减少手动配置的工作量。 在使用这个工具包时,开发者需要注意以下几点: - **正确...
项目为spring boot实现,maven生成jar包能直接运行 ...三种方式实现redis分布式锁 1.redis incr计数器实现 2.redis setIfAbsent 3.redisson 博客:https://blog.csdn.net/u011974797/article/details/81238079
Redisson实现分布式锁 有关Redisson实现分布式锁前面写了三篇博客作为该项目落地的铺垫。 1、 2、 3、 该项目可以直接运用于实际开发中,作为分布式锁使用。 一、项目概述 1、技术架构 项目总体技术选型 SpringBoot...
在SpringBoot应用中集成Redis分布式锁,可以创建一个`RedisLock`工具类,包含加锁和解锁的方法。加锁操作需要确保原子性,即同时设置键和过期时间,这在SpringBoot 2.x版本以上可以通过`opsForValue().setIfAbsent()...
读书笔记:架构实战篇十八Spring Boot Redis实现分布式锁
通过上述步骤,你可以成功地在Spring Boot项目中集成Redis和Redission,实现高效、可靠的分布式缓存方案。记得在实际开发中根据业务需求选择合适的数据结构和功能,以达到最佳性能和可维护性。同时,定期检查和优化...
本篇将深入探讨如何将RedisTemplate封装成RedisUtils工具类,并实现分布式锁功能。 首先,我们需要引入Spring Data Redis的依赖库,这通常在项目的pom.xml或build.gradle文件中完成。添加对应版本的Redis连接器和...
总结起来,这个项目展示了如何利用Spring Boot的便捷性、JPA的ORM能力以及Redis的高速缓存特性,构建一个高效且可扩展的分布式系统。通过学习和实践这个项目,开发者可以深入理解这三种技术的协同工作方式,提升自己...