spring boot 利用redisson实现redis的分布式锁

原文:http://liaoke0123.iteye.com/blog/2375469

利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的。

redis官方推荐的分布式锁实现为redisson http://ifeve.com/redis-lock/

以下为spring boot实现分布式锁的步骤

项目pom中需要添加官方依赖 我是1.8JDK固为

Pom代码  

  1. <!-- redisson -->
  2. <dependency>
  3. <groupId>org.redisson</groupId>
  4. <artifactId>redisson</artifactId>
  5. <version>3.4.2</version>
  6. </dependency>

定义一个分布式锁的回调类

Java代码  

  1. package com.example.demo.redis2;
  2. /**
  3. * 分布式锁回调接口
  4. *
  5. * @author lk
  6. */
  7. public interface DistributedLockCallback<T> {
  8. /**
  9. * 调用者必须在此方法中实现需要加分布式锁的业务逻辑
  10. *
  11. * @return
  12. */
  13. public T process();
  14. /**
  15. * 得到分布式锁名称
  16. *
  17. * @return
  18. */
  19. public String getLockName();
  20. }

分布式锁操作模板

Java代码  

  1. package com.example.demo.redis2;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 分布式锁操作模板
  5. *
  6. * @author lk
  7. */
  8. public interface DistributedLockTemplate {
  9. /**
  10. * 使用分布式锁,使用锁默认超时时间。
  11. *
  12. * @param callback
  13. * @return
  14. */
  15. public <T> T lock(DistributedLockCallback<T> callback);
  16. /**
  17. * 使用分布式锁。自定义锁的超时时间
  18. *
  19. * @param callback
  20. * @param leaseTime 锁超时时间。超时后自动释放锁。
  21. * @param timeUnit
  22. * @return
  23. */
  24. public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit);
  25. }

使用redisson最简单的Single instance mode实现分布式锁模板接口

Java代码  

  1. package com.example.demo.redis2;
  2. import org.redisson.api.RLock;
  3. import org.redisson.api.RedissonClient;
  4. import java.util.concurrent.TimeUnit;
  5. /**
  6. * Single Instance mode 分布式锁模板
  7. *
  8. * @author lk
  9. */
  10. public class SingleDistributedLockTemplate implements DistributedLockTemplate {
  11. private static final long     DEFAULT_TIMEOUT   = 5;
  12. private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
  13. private RedissonClient    redisson;
  14. public SingleDistributedLockTemplate() {
  15. }
  16. public SingleDistributedLockTemplate(RedissonClient redisson) {
  17. this.redisson = redisson;
  18. }
  19. @Override
  20. public <T> T lock(DistributedLockCallback<T> callback) {
  21. return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
  22. }
  23. @Override
  24. public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) {
  25. RLock lock = null;
  26. try {
  27. lock = redisson.getLock(callback.getLockName());
  28. lock.lock(leaseTime, timeUnit);
  29. return callback.process();
  30. } finally {
  31. if (lock != null) {
  32. lock.unlock();
  33. }
  34. }
  35. }
  36. public void setRedisson(RedissonClient redisson) {
  37. this.redisson = redisson;
  38. }
  39. }

创建可以被spring管理的 Bean

Java代码  

  1. package com.example.demo.redis2;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import javax.annotation.PostConstruct;
  5. import javax.annotation.PreDestroy;
  6. import org.apache.log4j.Logger;
  7. import org.redisson.Redisson;
  8. import org.redisson.api.RedissonClient;
  9. import org.redisson.config.Config;
  10. import org.springframework.beans.factory.FactoryBean;
  11. /**
  12. * 创建分布式锁模板实例的工厂Bean
  13. *
  14. * @author lk
  15. */
  16. public class DistributedLockFactoryBean implements FactoryBean<DistributedLockTemplate> {
  17. private Logger                  logger = Logger.getLogger(DistributedLockFactoryBean.class);
  18. private LockInstanceMode        mode;
  19. private DistributedLockTemplate distributedLockTemplate;
  20. private RedissonClient redisson;
  21. @PostConstruct
  22. public void init() {
  23. String ip = "127.0.0.1";
  24. String port = "6379";
  25. Config config=new Config();
  26. config.useSingleServer().setAddress(ip+":"+port);
  27. redisson=Redisson.create(config);
  28. System.out.println("成功连接Redis Server"+"\t"+"连接"+ip+":"+port+"服务器");
  29. }
  30. @PreDestroy
  31. public void destroy() {
  32. logger.debug("销毁分布式锁模板");
  33. redisson.shutdown();
  34. }
  35. @Override
  36. public DistributedLockTemplate getObject() throws Exception {
  37. switch (mode) {
  38. case SINGLE:
  39. distributedLockTemplate = new SingleDistributedLockTemplate(redisson);
  40. break;
  41. }
  42. return distributedLockTemplate;
  43. }
  44. @Override
  45. public Class<?> getObjectType() {
  46. return DistributedLockTemplate.class;
  47. }
  48. @Override
  49. public boolean isSingleton() {
  50. return true;
  51. }
  52. public void setMode(String mode) {
  53. if (mode==null||mode.length()<=0||mode.equals("")) {
  54. throw new IllegalArgumentException("未找到dlm.redisson.mode配置项");
  55. }
  56. this.mode = LockInstanceMode.parse(mode);
  57. if (this.mode == null) {
  58. throw new IllegalArgumentException("不支持的分布式锁模式");
  59. }
  60. }
  61. private enum LockInstanceMode {
  62. SINGLE;
  63. public static LockInstanceMode parse(String name) {
  64. for (LockInstanceMode modeIns : LockInstanceMode.values()) {
  65. if (modeIns.name().equals(name.toUpperCase())) {
  66. return modeIns;
  67. }
  68. }
  69. return null;
  70. }
  71. }
  72. }

配置进spring boot中

Java代码  

  1. package com.example.demo.redis2;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. /**
  5. * Created by LiaoKe on 2017/5/22.
  6. */
  7. @Configuration
  8. public class BeanConfig {
  9. @Bean
  10. public DistributedLockFactoryBean distributeLockTemplate(){
  11. DistributedLockFactoryBean d  = new DistributedLockFactoryBean();
  12. d.setMode("SINGLE");
  13. return d;
  14. }
  15. }

目前为止已经可以使用。

为了验证锁是否成功,我做了如下例子。

首先建立了一个数据库实体(使用的JPA),模拟被购买的商品数量,当被购买后,num+1

在高并发环境下,这必定会有问题,因为在查询之后的设值,存在对同一数据库源的操作。

Java代码  

  1. package com.example.demo.redis2.entity;
  2. import org.hibernate.annotations.GenericGenerator;
  3. import javax.persistence.Entity;
  4. import javax.persistence.GeneratedValue;
  5. import javax.persistence.Id;
  6. /**
  7. * 测试类实体
  8. * Created by LiaoKe on 2017/5/22.
  9. */
  10. @Entity
  11. public class TestEntity {
  12. @Id
  13. @GeneratedValue(generator = "system-uuid")
  14. @GenericGenerator(name = "system-uuid", strategy = "uuid")
  15. private String id;
  16. private Integer num;
  17. public String getId() {
  18. return id;
  19. }
  20. public void setId(String id) {
  21. this.id = id;
  22. }
  23. public Integer getNum() {
  24. return num;
  25. }
  26. public void setNum(Integer num) {
  27. this.num = num;
  28. }
  29. }

具体数据库操作,加锁和不加锁的操作,要注意我使用了@Async,异步任务注解,我没有配置线程池信息,使用的默认线程池。

Java代码  

  1. package com.example.demo.redis2.service;
  2. import com.example.demo.redis2.DistributedLockCallback;
  3. import com.example.demo.redis2.DistributedLockTemplate;
  4. import com.example.demo.redis2.dao.TestEntityRepository;
  5. import com.example.demo.redis2.entity.TestEntity;
  6. import org.springframework.scheduling.annotation.Async;
  7. import org.springframework.stereotype.Service;
  8. import javax.annotation.Resource;
  9. /**
  10. * Created by LiaoKe on 2017/5/22.
  11. */
  12. @Service
  13. public class AsyncService {
  14. @Resource
  15. TestEntityRepository ts;
  16. @Resource
  17. DistributedLockTemplate distributedLockTemplate;
  18. /**
  19. * 加锁
  20. */
  21. @Async
  22. public void addAsync(){
  23. distributedLockTemplate.lock(new DistributedLockCallback<Object>(){
  24. @Override
  25. public Object process() {
  26. add();
  27. return null;
  28. }
  29. @Override
  30. public String getLockName() {
  31. return "MyLock";
  32. }
  33. });
  34. }
  35. /**
  36. * 未加锁
  37. */
  38. @Async
  39. public void addNoAsync(){
  40. add();
  41. }
  42. /**
  43. * 测试异步方法
  44. * 在不加分布式锁的情况下
  45. * num数目会混乱
  46. */
  47. @Async
  48. private void add(){
  49. if(ts.findAll().size()==0){
  50. TestEntity  t  =  new TestEntity();
  51. t.setNum(1);
  52. ts.saveAndFlush(t);
  53. }else{
  54. TestEntity dbt = ts.findAll().get(0);
  55. dbt.setNum(dbt.getNum()+1);
  56. ts.saveAndFlush(dbt);
  57. }
  58. }
  59. }

最后为了测试简单跑了两个接口

Java代码  

  1. package com.example.demo;
  2. import com.example.demo.redis2.DistributedLockTemplate;
  3. import com.example.demo.redis2.service.AsyncService;
  4. import oracle.jrockit.jfr.StringConstantPool;
  5. import org.springframework.boot.SpringApplication;
  6. import org.springframework.boot.autoconfigure.SpringBootApplication;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.scheduling.annotation.EnableAsync;
  9. import org.springframework.stereotype.Controller;
  10. import org.springframework.web.bind.annotation.GetMapping;
  11. import org.springframework.web.bind.annotation.RestController;
  12. import javax.annotation.Resource;
  13. @SpringBootApplication
  14. @RestController
  15. @EnableAsync
  16. public class DemoApplication {
  17. public static void main(String[] args) {
  18. SpringApplication.run(DemoApplication.class, args);
  19. }
  20. @Resource
  21. AsyncService as;
  22. @GetMapping("")
  23. public void test(){
  24. for(int i = 0 ;i<10000;i++){
  25. as.addNoAsync();
  26. }
  27. }
  28. @GetMapping("lock")
  29. public void  test2(){
  30. for(int i = 0 ;i<10000;i++){
  31. as.addAsync();
  32. }
  33. }
  34. }

访问localhost:8888 及 localhost:8888/lock

在不加锁的情况下


 数据库已经爆炸了

最后得到的数据奇奇怪怪

使用加锁后的访问


 可以看到库存增加绝对正确。

此处并未使用任何数据库锁,并且基于redis,可在不同的网络节点实现上锁。

这只是简单的实现,在真正的生产环境中,还要注意许多问题,超时和放锁时机需要好好研究,在此不便贴真正项目代码。

参考博客:http://layznet.iteye.com/blog/2307179   感谢作者

原文地址:https://www.cnblogs.com/shihaiming/p/8600939.html

时间: 2025-01-17 19:08:13

spring boot 利用redisson实现redis的分布式锁的相关文章

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

利用consul在spring boot中实现最简单的分布式锁

因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了. 这里列举一个最简单的场景,假如有一个智能售货机,由于机器本身的原因不能同一台机器不能同时出两个商品,这就要求在在出货流程前针对同一台机器在同一时刻出现并发 创建订单时只能有一笔订单创建成功,但是订单服务是多节点部署的,所以就不得不用到分布式锁了. 以上只是一种简单的业务场景,在各种大型互联网实际应用中,需要分布式锁的业务场景会更多,综合比较了业界基于各种

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决

Spring boot配置多个Redis数据源操作实例

原文:https://www.jianshu.com/p/c79b65b253fa Spring boot配置多个Redis数据源操作实例 在SpringBoot是项目中整合了两个Redis的操作实例,可以增加多个: 一般在一个微服务生态群中是不会出现多个Redis中间件的,所以这种场景很少见,但也不可避免,但是不建议使用,个人建议,勿喷. 基于Maven3.0搭建,spring1.5.9.RELEASE和JDK1.8 1.新建SpringBoot项目,添加依赖 <dependency> &l

利用redis实现分布式锁 - waen - 博客园

原文:利用redis实现分布式锁 - waen - 博客园 利用数据库触发器实现定期自动增量更新缓存 原文地址:https://www.cnblogs.com/lonelyxmas/p/10434810.html

在Spring Boot项目中使用Redis集群

Redis安装 Mac 系统安装Redis brew方式安装 在命令汗执行命令 brew install redis 安装完成之后的提示信息 ==> Downloading https://homebrew.bintray.com/bottles/redis-5.0.2.mojave.bottle.tar.gz ######################################################################## 100.0% ==> Pouring

利用Redis实现分布式锁

写在最前面 我在之前总结幂等性的时候,写过一种分布式锁的实现,可惜当时没有真正应用过,着实的心虚啊.正好这段时间对这部分实践了一下,也算是对之前填坑了. 分布式锁按照网上的结论,大致分为三种:1.数据库乐观锁: 2.基于Redis的分布式锁:3..基于ZooKeeper的分布式锁: 关于乐观锁的实现其实在之前已经讲的很清楚了,有兴趣的移步:使用mysql乐观锁解决并发问题 .今天先简单总结下redis的实现方法,后面详细研究过ZooKeeper的实现原理后再具体说说ZooKeeper的实现. 为

基于redis的分布式锁(不适合用于生产环境)

基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境,另一个是集群环境下的Redis锁实现.在介绍分布式锁的实现之前,先来了解下分布式锁的一些信息. 2 分布式锁 2.1 什么是分布式锁? 分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥

基于redis的分布式锁的分析与实践

转:https://my.oschina.net/wnjustdoit/blog/1606215 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于线程之间是否相互阻塞. 那么,本文主要来讨论基于redis的分布式锁算法问题. 从2.6.12版本开始,redis为SET命令增加了一系列选项(SET key value [EX seconds] [PX