精华内容
下载资源
问答
  • 分布式锁redssion

    2020-03-30 15:44:12
    分布式锁redssion 集成方式 大致使用 代码分析 获取锁 调用getLock()方法后实际返回一个RedissonLock对象 加锁 在RedissonLock对象的lock()方法主要调用tryAcquire()方法,由于leaseTime == -1,于是...

    分布式锁redssion

    集成方式

    大致使用

    代码分析

    • 获取锁

      调用getLock()方法后实际返回一个RedissonLock对象

    • 加锁

      在RedissonLock对象的lock()方法主要调用tryAcquire()方法,由于leaseTime == -1,于是走tryLockInnerAsync()方法,

    • 加锁细节

      结合上面的参数声明,我们可以知道,这里KEYS[1]就是getName(),ARGV[2]是getLockName(threadId),假设前面获取锁时传的name是“anyLock”,假设调用的线程ID是Thread-1,假设成员变量UUID类型的id是85b196ce-e6f2-42ff-b3d7-6615b6748b5d:65那么KEYS[1]=anyLock,ARGV[2]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1 ,因此,这段脚本的意思是1、判断有没有一个叫“anyLock”的key2、如果没有,则在其下设置一个字段为“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”,值为“1”的键值对 ,并设置它的过期时间3、如果存在,则进一步判断“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”是否存在,若存在,则其值加1,并重新设置过期时间4、返回“anyLock”的生存时间(毫秒)

    • 加锁redis结构

      这里用的数据结构是hash,hash的结构是: key  字段1  值1 字段2  值2  。。。用在锁这个场景下,key就表示锁的名称,也可以理解为临界资源,字段就表示当前获得锁的线程所有竞争这把锁的线程都要判断在这个key下有没有自己线程的字段,如果没有则不能获得锁,如果有,则相当于重入,字段值加1(次数)

    • 解锁

      我们还是假设name=anyLock,假设线程ID是Thread-1,同理,我们可以知道KEYS[1]是getName(),即KEYS[1]=anyLock,KEYS[2]是getChannelName(),即KEYS[2]=redisson_lock__channel:{anyLock},ARGV[1]是LockPubSub.unlockMessage,即ARGV[1]=0,ARGV[2]是生存时间,ARGV[3]是getLockName(threadId),即ARGV[3]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1,因此,上面脚本的意思是:1、判断是否存在一个叫“anyLock”的key2、如果不存在,向Channel中广播一条消息,广播的内容是0,并返回1。3、如果存在,进一步判断字段85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1是否存在。4、若字段不存在,返回空,若字段存在,则字段值减1,5、若减完以后,字段值仍大于0,则返回0。6、减完后,若字段值小于或等于0,则广播一条消息,广播内容是0,并返回1;可以猜测,广播0表示资源可用,即通知那些等待获取锁的线程现在可以获得锁了

    • 等待
       private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
              long threadId = Thread.currentThread().getId();
              Long ttl = tryAcquire(leaseTime, unit, threadId);
              // lock acquired
              if (ttl == null) {
                  return;
              }
      
              RFuture<RedissonLockEntry> future = subscribe(threadId);
              if (interruptibly) {
                  commandExecutor.syncSubscriptionInterrupted(future);
              } else {
                  commandExecutor.syncSubscription(future);
              }
      
              try {
                  while (true) {
                      ttl = tryAcquire(leaseTime, unit, threadId);
                      // lock acquired
                      if (ttl == null) {
                          break;
                      }
      
                      // waiting for message
                      if (ttl >= 0) {
                          try {
                              future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                          } catch (InterruptedException e) {
                              if (interruptibly) {
                                  throw e;
                              }
                              future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                          }
                      } else {
                          if (interruptibly) {
                              future.getNow().getLatch().acquire();
                          } else {
                              future.getNow().getLatch().acquireUninterruptibly();
                          }
                      }
                  }
              } finally {
                  unsubscribe(future, threadId);
              }
      //        get(lockAsync(leaseTime, unit));
          }
      

      这里会订阅Channel,当资源可用时可以及时知道,并抢占,防止无效的轮询而浪费资源当资源可用用的时候,循环去尝试获取锁,由于多个线程同时去竞争资源,所以这里用了信号量,对于同一个资源只允许一个线程获得锁,其它的线程阻塞 

    • 总结

     

     

    展开全文
  • 自定义注解实现redssion分布式锁 1、自定义注解 package com.example.demo.annotation; import java.lang.annotation.*; /** * desc: 自定义 redisson 分布式锁注解 * * @author: 邢阳 * @mail: xydeveloper@...

    1、自定义注解

    package com.example.demo.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * desc: 自定义 redisson 分布式锁注解
     *
     * @author: 邢阳
     * @mail: xydeveloper@126.com
     * @create 2021-05-28 16:50
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface Lock {
    
    
        /**
         * 锁的key spel 表达式
         *
         * @return
         */
        String key();
    
        /**
         * 持锁时间
         *
         * @return
         */
        long keepMills() default 20;
    
        /**
         * 没有获取到锁时,等待时间
         *
         * @return
         */
        long maxSleepMills() default 30;
    
    }
    
    

    2、aop解析注解

    package com.example.demo.utils;
    
    import com.example.demo.annotation.Lock;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.DefaultParameterNameDiscoverer;
    import org.springframework.expression.EvaluationContext;
    import org.springframework.expression.spel.standard.SpelExpressionParser;
    import org.springframework.expression.spel.support.StandardEvaluationContext;
    import org.springframework.stereotype.Component;
    
    import java.util.Objects;
    import java.util.concurrent.TimeUnit;
    
    /**
     * desc: 解析 自定义 redisson 分布式锁注解
     *
     * @author: 邢阳
     * @mail: xydeveloper@126.com
     * @create 2021-05-28 16:50
     */
    @Aspect
    @Component
    public class LockAspect {
    
        @Autowired
        private RedissonClient redissonClient;
    
        /**
         * 用于SpEL表达式解析.
         */
        private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    
        /**
         * 用于获取方法参数定义名字.
         */
        private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    
        @Around("@annotation(com.example.demo.annotation.Lock)")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
            Object object = null;
    
            RLock lock = null;
    
            try {
    
                // 获取注解实体信息
                Lock lockEntity = (((MethodSignature) proceedingJoinPoint.getSignature()).getMethod())
                        .getAnnotation(Lock.class);
    
                // 根据名字获取锁实例
                lock = redissonClient.getLock(getKeyBySpeL(lockEntity.key(), proceedingJoinPoint));
    
                if (Objects.nonNull(lock)) {
    
                    if (lock.tryLock(lockEntity.maxSleepMills(), lockEntity.keepMills(), TimeUnit.SECONDS)) {
    
                        object = proceedingJoinPoint.proceed();
    
                    } else {
    
                        throw new RuntimeException();
    
                    }
    
                }
    
            } finally {
    
                if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
    
                    lock.unlock();
    
                }
    
            }
    
            return object;
    
        }
    
        /**
         * 获取缓存的key
         * 
         * key 定义在注解上,支持SPEL表达式
         *
         * @return
         */
        public String getKeyBySpeL(String spel, ProceedingJoinPoint proceedingJoinPoint) {
    
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
    
            String[] paramNames = defaultParameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
    
            EvaluationContext context = new StandardEvaluationContext();
    
            Object[] args = proceedingJoinPoint.getArgs();
    
            for (int i = 0; i < args.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
    
            return String.valueOf(spelExpressionParser.parseExpression(spel).getValue(context));
    
        }
    
    }
    
    

    3、service中使用注解加锁使用

    /**
     * desc: 锁
     *
     * @author: 邢阳
     * @mail: xydeveloper@126.com
     * @create 2021-05-28 17:58
     */
    @Service
    public class LockService {
    
      	@Lock(key = "#user.id", keepMills = 10, maxSleepMills = 15)
        public String lock(User user) {
    
    
            System.out.println("持锁");
    
            return "";
    
        }
    
    }
    
    展开全文
  • 文章目录基于Redis的分布式锁问题Redssion的简单的使用1、首先是导包2、编写配置文件3、编写lock的类4、调用 基于Redis的分布式锁问题Redssion的简单的使用 Redission这个框架就解决了 分布式锁的问题 Reddsion这个...

    基于Redis的分布式锁问题Redssion的简单的使用

    Redission这个框架就解决了 分布式锁的问题

    Reddsion这个框架实际上也是咋们的Redis的客户端代码

    1、首先是导包

    <!--导入redssion这框架包-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>3.11.0</version>
            </dependency>
    

    2、编写配置文件

     @Bean
        public RedissonClient redissonClient(){
            RedissonClient redissonClient=null;
            //获取config的实例
            Config config = new Config();
            //设置请求的URL地址
            String url="redis://106.54.13.167:6379";
            //设置config
            config.useSingleServer().setAddress(url);
            //通过Redisson来创建一个客户端对象
            try{
                redissonClient= Redisson.create(config);
                logger.info("创建RedissonClient成功");
                return redissonClient;
            }catch (Exception err){
                logger.info("创建RedissonClient失败:"+err.fillInStackTrace());
                return null;
            }
    
        }
    

    3、编写lock的类

    @Component
    public class DistributeRedisLock {
    
        private Logger logger= LoggerFactory.getLogger(getClass());
    
        @Autowired
        private RedissonClient redissonClient;
    
    
        //一个方法用来加锁
    
        /**
         * 加锁成功....
         * @param lockName
         * @return
         */
        public boolean lock(String lockName){
            try {
                if(null==redissonClient){  //如果对象没有注入进来那么说明是有问题的
                    logger.info("注入redissonClient对象失败....");
                    return false;
                }
                //获取这个锁
                RLock lock = redissonClient.getLock(lockName);
                //锁住了
                lock.lock(30, TimeUnit.SECONDS);
                logger.info("加锁成功.......");
                return true;
            } catch (Exception e) {
                logger.info("不可预期的异常造成了加锁失败....");
               return false;
            }
        }
    
    
        /**
         * 释放锁
         * @param lockName
         * @return
         */
        public boolean unlock(String lockName){
            try {
                if(null==redissonClient){  //说明没法释放出问题了....
                    logger.info("释放锁失败----"+lockName);
                }
                //获取到这个锁对象
                RLock lock = redissonClient.getLock(lockName);
                if(null!=lock){
                   lock.unlock();
                   logger.info("释放锁成功....");
                   return true;
                }
                return false;
            } catch (Exception e) {
                logger.info("释放锁失败了....");
                return false;
            }
        }
    }
    
    

    4、调用

     public String produceStockRedisson(){
            String lock="lock";
            try {
                boolean lock1 = distributeRedisLock.lock(lock);
                if(true==lock1){//说明加锁成功
                    int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("traintickes"));
                    //首先要判断下 这个库存是否>0
                    if (stock > 0) {  //说明可以减去库存
                        int rStock = stock - 1;
                        //下一步:将真实的库存放到咋们的Redis中去
                        stringRedisTemplate.opsForValue().set("traintickes", String.valueOf(rStock));
                        logger.info("扣减库存成功....剩余库存:" + rStock);
                    } else {   //说明不能扣减库存
                        logger.info("库存扣减失败、库存是负数、不足...");
                    }  //已经用了15秒钟
                }else{
                    return "当前的排队人数过多...";
                }
            }finally {
                distributeRedisLock.unlock(lock);
            }
            return "抢票成功....";
        }
    

    希望大家关注我一波,防止以后迷路,有需要的可以加我Q讨论互相学习java ,学习路线探讨,经验分享与java Q:2415773436

    展开全文
  • 文章目录Redisson概述适用场景实际应用1、导入依赖2、编写配置文件3、自定义分布式锁 lock4、业务逻辑中调用锁 Redisson概述 ​ Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。...

    Redisson概述

    ​ Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

    适用场景

    分布式应用,分布式缓存,分布式回话管理,分布式服务(任务,延迟任务,执行器),分布式redis客户端

    实际应用

    这里还使用高并发卖票问题来介绍Redisson,详情参见 手写一个Redis分布式锁解决实际开发中高并发问题

    Redission这个框架就解决了 分布式锁的问题

    Reddsion这个框架实际上也是的Redis的客户端代码

    1、导入依赖

    <!-- redssion -->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.12.0</version>
    </dependency>
    

    2、编写配置文件

    @SpringBootConfiguration
    public class RedisConfig {
    
        private Logger logger = LoggerFactory.getLogger(RedisConfig.class);
    
        @Bean
        public RedissonClient redisClient() {
            RedissonClient redissonClient = null;
            // 获取config的实例
            Config config = new Config();
            // 设置请求的URL地址
            String url = "redis://39.105.232.73:6379";
            config.useSingleServer().setAddress(url);
            // 设置密码
            String password = "root";
            config.useSingleServer().setPassword(password);
            // 通过redisson创建一个客户端对象
            try {
                redissonClient = Redisson.create(config);
                logger.info("创建RedissonClient成功...");
                return redissonClient;
            } catch (Exception e) {
                logger.error("创建RedissonClient失败:" + e.fillInStackTrace());
                return null;
            }
        }
    }
    

    3、自定义分布式锁 lock

    @Component
    public class DistributeRedisLock {
        private Logger logger = LoggerFactory.getLogger(DistributeRedisLock.class);
    
        @Autowired
        private RedissonClient redissonClient;
    
        /**
         * 加锁的方法
         */
        public boolean lock(String lockName) {
            try {
                if (null == redissonClient) { // 表示 redissonClient 注入失败
                    logger.info("注入redissonClient对象失败...");
                    return false;
                }
                // 获取这个锁
                RLock lock = redissonClient.getLock(lockName);
                // 给程序加锁
                lock.lock(30, TimeUnit.SECONDS);
                logger.info("加锁成功...");
                return true;
            } catch (Exception e) {
                logger.error("不可预期的异常造成了加锁失败...");
                return false;
            }
        }
        /**
         * 释放锁的方法
         */
        public boolean unlock(String lockName) {
            try {
                if (null == redissonClient) { // 表示 redissonClient 注入失败
                    logger.info("释放锁失败:" + lockName);
                    return false;
                }
                // 获取这个锁对象
                RLock lock = redissonClient.getLock(lockName);
                if (null != lock) {
                    lock.unlock();
                    logger.info("释放锁成功...");
                    return true;
                }
                return false;
            } catch (Exception e) {
                logger.info("不可预期的异常造成了锁释放失败...");
                return false;
            }
        }
    }
    

    4、业务逻辑中调用锁

    	@Autowired
        private DistributeRedisLock distributeRedisLock;
    
        private Logger logger = LoggerFactory.getLogger(TicketServiceImpl.class);
    
    	@Override
        public String sellTicketRedisson() {
            String lock = "lock";
            try {
                boolean addLock = distributeRedisLock.lock(lock);
                if (!addLock) { // 如果加锁失败
                    return "当前排队人数过多...请等待...";
                }
                String ticketStr = stringRedisTemplate.opsForValue().get("ticket");
                int ticket = 0;
                if (null != ticketStr) {
                    ticket = Integer.parseInt(ticketStr);
                }
                if (ticket > 0) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    int ticketNew = ticket - 1;
                    stringRedisTemplate.opsForValue().set("ticket", String.valueOf(ticketNew));
                    logger.info("当前票的库存为:" + ticketNew);
                } else {
                    logger.info("手速不够呀,票已经卖光了...");
                }
            } finally {
                // 关闭锁
                distributeRedisLock.unlock(lock);
            }
            return "抢票成功...";
        }
    
    展开全文
  • 文章目录1 reddsion概述2 创建一个spring boot 项目3 pom添加jar包依赖4 配置文件(application.yml)5 RedissonConfig配置类6 编写抽象类(CallService)做调用匿名类7 RedissonService接口8 RedissonServiceImpl实现...
  • Redisson简介 Javaer都知道Jedis,Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。Redission也是Redis的客户端,相比于Jedis功能简单。Jedis简单使用阻塞的I/O和redis交互,Redission通过...
  • 在非分布式系统中要实现锁的机制很简单,利用java.util.concurrent.locks包下的Lock和关键字synchronized都可以实现。但是在分布式系统中,如何实现各个单独的微服务需要共享某个资源的时候进行有效的锁的保护机制呢...
  • 一、项目结构: 1、 父工程 pom.xml:(父工程只有一个pom.xml) <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 381
精华内容 152
关键字:

redssion