zoukankan      html  css  js  c++  java
  • 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.  * 
    6.  * @author lk 
    7.  */  
    8. public interface DistributedLockCallback<T> {  
    9.   
    10.     /** 
    11.      * 调用者必须在此方法中实现需要加分布式锁的业务逻辑 
    12.      * 
    13.      * @return 
    14.      */  
    15.     public T process();  
    16.   
    17.     /** 
    18.      * 得到分布式锁名称 
    19.      * 
    20.      * @return 
    21.      */  
    22.     public String getLockName();  
    23. }  

     分布式锁操作模板

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

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

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

    创建可以被spring管理的 Bean

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

     配置进spring boot中

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

    目前为止已经可以使用。

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

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

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

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

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

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

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

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

     访问localhost:8888 及 localhost:8888/lock

    在不加锁的情况下



     数据库已经爆炸了

    最后得到的数据奇奇怪怪



     

    使用加锁后的访问



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

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

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

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

  • 相关阅读:
    1. Deep Q-Learning
    Ⅶ. Policy Gradient Methods
    Ⅴ. Temporal-Difference Learning
    idea在service窗口中显示多个服务
    pycharm下运行flask框架的脚本时报错
    windows下部署 flask (win10+flask+nginx)
    pip install selenium报错解决方法
    pip 及 selenium安装命令
    动作捕捉系统用于模仿学习
    柔性微创手术机器人性能实验验证
  • 原文地址:https://www.cnblogs.com/shihaiming/p/8600939.html
Copyright © 2011-2022 走看看