REDIS10_Redission的入门案例、多主案例搭建、分布式锁进行加锁、解锁底层

代码 代码 1338 人阅读 | 0 人回复

<
文章目次



①. 怎样基于民网停止开拓



  • ①. 进进redis中文民网,面击文档
    145303t9n6o4cn4ocxx294.jpg

  • ②.挑选散布式锁,翻开页里
    145303ndk99gxl4xdmprcd.jpg
    145304mn339hnh55h8qtkz.jpg

  • ③. 面击Wiki
    145304y7zqauqzhnfzaa7z.jpg

    145304yvv8dnyu8enha4up.jpg

  • ④. 后绝闭于散布式锁,需求甚么内乱容,停止文档的查阅
    145305vi8n7i28vh8inqiu.jpg

  • ⑤. 天上飞的理念(RedLock)一定有降天的完成(Redisson)
    145305cgh682u2t8381n08.jpg
    145306hhdnfpnggpdngbnw.jpg

  • ⑥. redission打点了两个成绩

  • 锁的主动绝期,假如营业超少,运转时期主动给锁绝上新的30s,不消担忧营业工夫少
  • 假如营业宕机了,那个默许的过时工夫是30s,制止了逝世锁
  • 减锁的营业只需运转完成,便没有会给当前锁绝期,即便没有脚动解锁,锁默许正在30s当前主动删除
②. 基于Redisson的进门案例



  • ①. 导进pom
  1.         <!--引进redisson散布式锁-->
  2.         <dependency>
  3.                 <groupId>org.redisson</groupId>
  4.                 <artifactId>redisson</artifactId>
  5.                 <version>3.13.4</version>
  6.         </dependency>
复造代码


  • ②. 成立设置类,参考文档
  1. //2.成立设置类
  2. @Configuration
  3. public class MyRedisConfig {
  4.     /**
  5.      * 一切对Redisson的利用
  6.      * @return
  7.      * @throws IOException
  8.      */
  9.     //https://github.com/redisson/redisson/wiki/14.-%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A1%86%E6%9E%B6%E6%95%B4%E5%90%88
  10.     @Bean(destroyMethod="shutdown")
  11.     public RedissonClient redisson() throws IOException {
  12.         Config config = new Config();
  13.         // 创立单例形式的设置
  14.         //config.useSingleServer().setAddress("redis://" + ipAddr + ":6379");
  15.         config.useSingleServer().setAddress("redis://192.168.56.10:6379");
  16.         return Redisson.create(config);
  17.     }
  18. }
复造代码
145306hour8lhg8z6hlhkw.jpg



  • ③. 停止单元测试
  1. @Autowired
  2. //RedissonClient redissonClient;
  3. RedissonClient redisson;
  4. @Test
  5. public void redission(){
  6.         System.out.println(redissonClient);
  7.         RLock lock = redisson.getLock("lock");
  8. }
复造代码
③. setnx的散布式锁有哪些不敷



  • ①. 基于setnx的散布式锁有甚么缺陷?

  • 线程1起首获得锁胜利,将键值对写进 redis 的 master 节面
  • 正在redis将该键值对同步到slave节面之前,master 发作了毛病
  • redis 触收毛病转移,此中一个 slave 晋级为新的 master
  • 此时新的master其实不包含线程1写进的键值对,因而线程2尝试获得锁也能够胜利拿到锁
  • 此时相称于有两个线程获得到了锁,大要会招致各类预期以外的状况发作,比方最多见的净数据。
    我们减的是排它独有锁,统一工夫只能有一个建redis锁胜利并持有锁,宽禁呈现2个以上的恳求线程拿到锁。
    145307mu131iou11xuhzwd.jpg



  • ②. redis之女提出了Redlock算法打点那个成绩
    (Redis也供给了Redlock算法,用去完成基于多个真例的散布式锁。锁变量由多个真例保护,即便有真例发作了毛病,锁变量仍旧是存正在的,客户端仍是能够完成锁操纵。Redlock算法是完成下牢靠散布式锁的一种有用打点计划,能够正在理想开拓中利用)
    145307utwsaz3gtwopptof.jpg

  • ③. Redis散群的AP(redis同步复制作成的锁丧失,好比:主节面出去的及把方才set出去那条数据给从节面,便挂了)
  • ④. redis散布式锁,多主散群形式,需求计较容错率(N=2X+1)
    好比,收集中逝世了1台机械,我请求仍是OK的,能够用,请示,最多主散群安排几台?
    N=2*1+1=3
    145308ikq6hqojjjpks3o3.png

  • ⑤. 为何是偶数? N = 2X + 1 (N是终极安排机械数,X是容错机械数)
    起码的机械,最多的产出结果
    参加正在散群状况中,redis失利1台,可担任。2N+2= 2 * 1+2 =4,安排4台
    参加正在散群状况中,redis失利2台,可担任。2N+2 = 2 * 2+2 =6,安排6台

  • ⑥. 那末甚么是容错呢?

  • 失利了几个机械真例后我仍是能够容忍的,所谓的容忍便是数据分歧性仍是能够Ok的,CP数据分歧性仍是能够合意
  • 参加正在散群状况中,redis失利1台,可担任。2X+1 = 2 * 1+1 =3,安排3台,逝世了1个剩下2个能够一般事情,那便安排3台
  • 参加正在散群状况中,redis失利2台,可担任。2X+1 = 2 * 2+1 =5,安排5台,逝世了2个剩下3个能够一般事情,那便安排5台
④. 三台主机案例拆建



  • ①. docker上装置三台机械
  1. docker run -p 6381:6379 --name redis-master-1 -d redis:6.08
  2. docker run -p 6382:6379 --name redis-master-2 -d redis:6.0.8
  3. docker run -p 6383:6379 --name redis-master-3 -d redis:6.0.8
复造代码
145308a1p8w9dwudt19f9z.png



  • ②. 建Module(redis_redlock)、改pom.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <parent>
  6.         <groupId>org.springframework.boot</groupId>
  7.         <artifactId>spring-boot-starter-parent</artifactId>
  8.         <version>2.3.10.RELEASE</version>
  9.         <relativePath/> <!-- lookup parent from repository -->
  10.     </parent>
  11.     <groupId>com.xiaozhi.redis.redlock</groupId>
  12.     <artifactId>redis_redlock</artifactId>
  13.     <version>0.0.1-SNAPSHOT</version>
  14.     <properties>
  15.         <java.version>1.8</java.version>
  16.     </properties>
  17.     <dependencies>
  18.         <dependency>
  19.             <groupId>org.springframework.boot</groupId>
  20.             <artifactId>spring-boot-starter-web</artifactId>
  21.         </dependency>
  22.         <dependency>
  23.             <groupId>org.springframework.boot</groupId>
  24.             <artifactId>spring-boot-starter-test</artifactId>
  25.             <scope>test</scope>
  26.         </dependency>
  27.         <dependency>
  28.             <groupId>org.redisson</groupId>
  29.             <artifactId>redisson</artifactId>
  30.             <!--<version>3.12.0</version>-->
  31.             <version>3.13.4</version>
  32.         </dependency>
  33.         <dependency>
  34.             <groupId>org.projectlombok</groupId>
  35.             <artifactId>lombok</artifactId>
  36.             <version>1.18.8</version>
  37.         </dependency>
  38.         <!--swagger-->
  39.         <dependency>
  40.             <groupId>io.springfox</groupId>
  41.             <artifactId>springfox-swagger2</artifactId>
  42.             <version>2.9.2</version>
  43.         </dependency>
  44.         <!--swagger-ui-->
  45.         <dependency>
  46.             <groupId>io.springfox</groupId>
  47.             <artifactId>springfox-swagger-ui</artifactId>
  48.             <version>2.9.2</version>
  49.         </dependency>
  50.         <dependency>
  51.             <groupId>org.apache.commons</groupId>
  52.             <artifactId>commons-lang3</artifactId>
  53.             <version>3.4</version>
  54.             <scope>compile</scope>
  55.         </dependency>
  56.     </dependencies>
  57.     <build>
  58.         <plugins>
  59.             <plugin>
  60.                 <groupId>org.springframework.boot</groupId>
  61.                 <artifactId>spring-boot-maven-plugin</artifactId>
  62.             </plugin>
  63.         </plugins>
  64.     </build>
  65. </project>
复造代码


  • ③. 各个设置文件以下
  1. spring.application.name=spring-boot-redis
  2. server.port=9090
  3. spring.swagger2.enabled=true
  4. spring.redis.database=0
  5. spring.redis.password=
  6. spring.redis.timeout=3000
  7. #sentinel/cluster/single
  8. spring.redis.mode=single
  9. spring.redis.pool.conn-timeout=3000
  10. spring.redis.pool.so-timeout=3000
  11. spring.redis.pool.size=10
  12. spring.redis.single.address1=192.168.111.147:6381
  13. spring.redis.single.address2=192.168.111.147:6382
  14. spring.redis.single.address3=192.168.111.147:6383
复造代码
  1. //CacheConfiguration
  2. @Configuration
  3. @EnableConfigurationProperties(RedisProperties.class)
  4. public class CacheConfiguration {
  5.     @Autowired
  6.     RedisProperties redisProperties;
  7.     @Bean
  8.     RedissonClient redissonClient1() {
  9.         Config config = new Config();
  10.         String node = redisProperties.getSingle().getAddress1();
  11.         node = node.startsWith("redis://") ? node : "redis://" + node;
  12.         SingleServerConfig serverConfig = config.useSingleServer()
  13.                 .setAddress(node)
  14.                 .setTimeout(redisProperties.getPool().getConnTimeout())
  15.                 .setConnectionPoolSize(redisProperties.getPool().getSize())
  16.                 .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
  17.         if (StringUtils.isNotBlank(redisProperties.getPassword())) {
  18.             serverConfig.setPassword(redisProperties.getPassword());
  19.         }
  20.         return Redisson.create(config);
  21.     }
  22.     @Bean
  23.     RedissonClient redissonClient2() {
  24.         Config config = new Config();
  25.         String node = redisProperties.getSingle().getAddress2();
  26.         node = node.startsWith("redis://") ? node : "redis://" + node;
  27.         SingleServerConfig serverConfig = config.useSingleServer()
  28.                 .setAddress(node)
  29.                 .setTimeout(redisProperties.getPool().getConnTimeout())
  30.                 .setConnectionPoolSize(redisProperties.getPool().getSize())
  31.                 .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
  32.         if (StringUtils.isNotBlank(redisProperties.getPassword())) {
  33.             serverConfig.setPassword(redisProperties.getPassword());
  34.         }
  35.         return Redisson.create(config);
  36.     }
  37.     @Bean
  38.     RedissonClient redissonClient3() {
  39.         Config config = new Config();
  40.         String node = redisProperties.getSingle().getAddress3();
  41.         node = node.startsWith("redis://") ? node : "redis://" + node;
  42.         SingleServerConfig serverConfig = config.useSingleServer()
  43.                 .setAddress(node)
  44.                 .setTimeout(redisProperties.getPool().getConnTimeout())
  45.                 .setConnectionPoolSize(redisProperties.getPool().getSize())
  46.                 .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
  47.         if (StringUtils.isNotBlank(redisProperties.getPassword())) {
  48.             serverConfig.setPassword(redisProperties.getPassword());
  49.         }
  50.         return Redisson.create(config);
  51.     }
  52.     /**
  53.      * 单机
  54.      * @return
  55.      */
  56.     /*@Bean
  57.     public Redisson redisson()
  58.     {
  59.         Config config = new Config();
  60.         config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);
  61.         return (Redisson) Redisson.create(config);
  62.     }*/
  63. }
复造代码
  1. //RedisPoolProperties
  2. @Data
  3. public class RedisPoolProperties {
  4.     private int maxIdle;
  5.     private int minIdle;
  6.     private int maxActive;
  7.     private int maxWait;
  8.     private int connTimeout;
  9.     private int soTimeout;
  10.     /**
  11.      * 池巨细
  12.      */
  13.     private  int size;
  14. }
复造代码
  1. //RedisProperties
  2. @ConfigurationProperties(prefix = "spring.redis", ignoreUnknownFields = false)
  3. @Data
  4. public class RedisProperties {
  5.     private int database;
  6.     /**
  7.      * 等候节面复兴号令的工夫。该工夫从号令收收胜利时开端计时
  8.      */
  9.     private int timeout;
  10.     private String password
  11.     private String mode;
  12.     /**
  13.      * 池设置
  14.      */
  15.     private RedisPoolProperties pool;
  16.     /**
  17.      * 单机疑息设置
  18.      */
  19.     private RedisSingleProperties single;
  20. }
复造代码
  1. //RedisSingleProperties
  2. @Data
  3. public class RedisSingleProperties {
  4.     private  String address1;
  5.     private  String address2;
  6.     private  String address3;
  7. }
复造代码


  • ④. controller代码展现
  1. @RestController
  2. @Slf4j
  3. public class RedLockController {
  4.     public static final String CACHE_KEY_REDLOCK = "TANGZHI_REDLOCK";
  5.     @Autowired
  6.     RedissonClient redissonClient1;
  7.     @Autowired
  8.     RedissonClient redissonClient2;
  9.     @Autowired
  10.     RedissonClient redissonClient3;
  11.     @GetMapping(value = "/redlock")
  12.     public void getlock() {
  13.         //CACHE_KEY_REDLOCK为redis 散布式锁的key
  14.         RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);
  15.         RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);
  16.         RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);
  17.         RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
  18.         boolean isLockBoolean;
  19.         try {
  20.             //waitTime 抢锁的等候工夫,一般状况劣等3秒
  21.             //leaseTime便是redis key的过时工夫,一般状况劣等5分钟300秒。
  22.             isLockBoolean = redLock.tryLock(3, 300, TimeUnit.SECONDS);
  23.             log.info("线程{},能否拿到锁:{} ",Thread.currentThread().getName(),isLockBoolean);
  24.             if (isLockBoolean) {
  25.                 System.out.println(Thread.currentThread().getName()+"\t"+"---come in biz");
  26.                 //营业逻辑,闲10分钟
  27.                 try { TimeUnit.MINUTES.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
  28.             }
  29.         } catch (Exception e) {
  30.             log.error("redlock exception ",e);
  31.         } finally {
  32.             // 不管怎样, 最初皆要解锁
  33.             redLock.unlock();
  34.         }
  35.     }
  36. }
复造代码


  • ⑤. 测试:http://localhost:9090/redlock
  1. [root@TANG2021 ~]# docker exec -it redis-master-1 redis-cli
  2. 127.0.0.1:6379> keys *
  3. 1) "TANGZHI_REDLOCK"
  4. 127.0.0.1:6379> type TANGZHI_REDLOCK
  5. hash
  6. 127.0.0.1:6379> hgetall TANGZHI_REDLOCK
  7. 1) "ca512c4b-f05f-4578-9b8f-45e2e35aa0d7:50"
  8. 2) "1"
复造代码
⑤. Redisson源码分析



  • ①. 测试代码展现
  1. public class WatchDogDemo {
  2.     public static final String LOCKKEY = "DEBUG_YUANMA";
  3.     private static Config config;
  4.     private static Redisson redisson;
  5.     static {
  6.         config = new Config();
  7.         config.useSingleServer().setAddress("redis://"+"192.168.68.143"+":6379").setDatabase(0);
  8.         redisson = (Redisson)Redisson.create(config);
  9.     }
  10.     public static void main(String[] args) {
  11.         RLock redissonLock = redisson.getLock(LOCKKEY);
  12.         redissonLock.lock();
  13.         try {
  14.             System.out.println("1111-------biz");
  15.             //停息几秒钟线程
  16.             try { TimeUnit.SECONDS.sleep(25); } catch (InterruptedException e) { e.printStackTrace(); }
  17.         }catch (Exception e){
  18.             e.printStackTrace();
  19.         }finally {
  20.             if(redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
  21.                 redissonLock.unlock();
  22.             }
  23.         }
  24.         System.out.println(Thread.currentThread().getName() + " main ------ ends.");
  25.         //停息几秒钟线程
  26.         try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
  27.         redisson.shutdown();
  28.     }
  29. }
复造代码


  • ②. Redis散布式锁过时了,可是营业逻辑借出处置完怎样办?引进缓存绝命

  • 分外起一个线程,按期查抄线程能否借持有锁,假如有则耽误过时工夫。
  • Redisson里面便完成了那个计划,利用“看门狗”按期查抄(每1/3的锁工夫查抄1次),假如线程借持有锁,则革新过时工夫
  • 正在获得锁胜利后,给锁减一个watchdog,watchdog 会起一个按时使命,正在锁出有被开释且将近过时的时分会绝期
    145308ofdfddib82zzl5oo.jpg



  • ③. 详解缓存绝命源码分析一:经由过程redisson新建出去的锁key,默许是30秒
    145309u421kxnnkz0n2k62.jpg
    145309ro5t57bkrro5zrtp.jpg

  • ④. 详解缓存绝命源码分析两
    145310a2zl3l337n37mo3c.jpg

  • ⑤. 详解缓存绝命源码分析三

  • 那里面初初化了一个按时器,dely 的工夫是 internalLockLeaseTime/3
  • 正在Redisson中,internalLockLeaseTime是30s,也便是每隔10s绝期一次,每次30s
145310dytzj3ommoroleht.jpg
145311b199oz5xccuqrxxr.jpg



  • ⑥. watch dog主动延期机造
    (客户端A减锁胜利,便会启动一个watch dog看门狗,他是一个背景线程,会每隔10秒查抄一下,假如客户端A借持有锁key,那末便会不竭的耽误锁key的保存工夫,默许每次绝命又从30秒新开端)
    145311ja2i2i98s9su203z.jpg

  • ⑦. 详解缓存绝命源码分析四:减锁逻辑(针关于默许工夫30s过时的) lock.lock()

  • 出有锁,减锁(独一的id),启动按时使命,设置30s的过时工夫
  • 有锁:将独一id+1(可重进锁)
  • 非减锁线程,返回当前减锁线程的过时工夫
    145311i9rtskz2x99v3fss.jpg



  • ⑧. 详解缓存绝命源码分析五:解锁逻辑

  • 假如开释的锁的线程战已存正在锁的线程没有是统一个线程,返回null
  • 经由过程hincrby递加1,先开释一次锁。若盈余次数借年夜于0,则证实当前锁是可重进锁,革新过时工夫
  • 若盈余次数小于0,删除key并开释锁,解锁胜利
145312ccgyjp968cj5ec9e.jpg



  • ⑨. 详解缓存绝命源码分析六:减锁逻辑
    (lock.lock(10,TimeUnit.SECONDS):10s主动解锁,没有会主动绝期)
    底层道理:假如我们传递了锁的超不时间,便收收给redis施行lua剧本,停止占锁,默许超时便是我们指定的工夫

  1.     <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
  2.         this.internalLockLeaseTime = unit.toMillis(leaseTime);
  3.         return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE,
  4. command, "if (redis.call(&#39;exists&#39;, KEYS[1]) == 0) then redis.call(&#39;hincrby&#39;, KEYS[1], ARGV[2], 1); redis.call(&#39;pexpire&#39;, KEYS[1], ARGV[1]); return nil; end; if (redis.call(&#39;hexists&#39;, KEYS[1], ARGV[2]) == 1) then redis.call(&#39;hincrby&#39;, KEYS[1], ARGV[2], 1); redis.call(&#39;pexpire&#39;, KEYS[1], ARGV[1]); return nil; end; return redis.call(&#39;pttl&#39;, KEYS[1]);", Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));
  5.     }
复造代码
145312kz3v13cv6qc619by.jpg


免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作!
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
回复 关闭延时

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则