Redisson
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet
, Set
, Multimap
, SortedSet
, Map
, List
, Queue
, BlockingQueue
, Deque
, BlockingDeque
, Semaphore
, Lock
, AtomicLong
, CountDownLatch
, Publish / Subscribe
, Bloom filter
, Remote service
, Spring cache
, Executor service
, Live Object service
, Scheduler service
) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
RedLock
Redlock 算法的基本思路,是让客户端和多个独立的 Redis 实例依次请求加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,就认为客户端成功地获得分布式锁了,否则加锁失败。即使有单个 Redis 实例发生故障,因为锁变量在其它实例上也有保存,所以,客户端仍然可以正常地进行锁操作,锁变量并不会丢失。
客户端获取当前时间
客户端按照N个Redis实例执行加锁操作
加锁操作和在单实例上执行的加锁操作一样,使用 SET 命令,带上 NX,EX/PX 选项,以及带上客户端的唯一标识。还有一个超时时间。如果在某一个Redis实例上加锁失败,会直接在下一个实例上执行。加锁操作的超时时间需要远远地小于锁的有效时间,一般也就是设置为几十毫秒。
客户端完成了和所有 Redis 实例的加锁操作,客户端就要计算整个加锁过程的总耗时。
当客户端加锁操作满足,以下条件才算成功。
客户端从超过半数(大于等于 N/2+1)的 Redis 实例上成功获取到了锁;
客户端获取锁的总耗时没有超过锁的有效时间。
客户端释放锁
Redisson实现RedLock
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
Redis主从版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Configuration public class RedisConfig { @Bean public RedissonConnectionFactory redissonConnectionFactory (RedissonClient redisson) { return new RedissonConnectionFactory (redisson); } @Bean public RedisTemplate redisTemplate (RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate (); RedisSerializer<String> redisSerializer = new StringRedisSerializer (); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(redisSerializer); redisTemplate.setValueSerializer(redisSerializer); redisTemplate.setHashValueSerializer(redisSerializer); return redisTemplate; } @Bean public RedissonClient redissonClient () throws IOException { Config config = new Config (); config.useMasterSlaveServers() .setMasterAddress("redis://127.0.0.1:6379" ).setPassword("redis_pwd" ) .addSlaveAddress("redis://127.0.0.1:6380" ,"redis://127.0.0.1:6381" ).setPassword("redis_pwd" ) .setRetryInterval(5000 ) .setTimeout(10000 ) .setConnectTimeout(10000 ); return Redisson.create(config); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Slf4j @ExtendWith(SpringExtension.class) @SpringBootTest(classes = AppApplication.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class AppApplicationTest { @Autowired private RedissonClient redissonClient; private static final String LOCK_TITLE = "redisLock_" ; @Test @Disabled void contextLoads () throws InterruptedException { int count = 10 ; CountDownLatch countDownLatch = new CountDownLatch (count); ExecutorService executorService = Executors.newFixedThreadPool(count); String key = UUID.randomUUID().toString(); String lockKey = LOCK_TITLE + key; for (int i = 0 ; i < count; i++) { executorService.execute(() -> { try { RLock lock = redissonClient.getLock(lockKey); Boolean result = lock.tryLock(2 , TimeUnit.MINUTES); log.info(" {} lock {}" , result, lockKey); Thread.sleep(1000L ); redissonClient.getBucket("value" ).set(String.valueOf(System.currentTimeMillis())); String value = (String) redissonClient.getBucket("value" ).get(); log.info(" {} value {}" , Thread.currentThread().getName(), value); } catch (Exception e) { log.error("{}" , e.getMessage()); } finally { RLock lock = redissonClient.getLock(lockKey); lock.unlock(); log.info(" {} unlock {}" , Thread.currentThread().getName(), lockKey); } countDownLatch.countDown(); }); } countDownLatch.countDown(); executorService.shutdown(); ; log.info("run end~~~" ); Thread.sleep(11000L ); } }
END