0
点赞
收藏
分享

微信扫一扫

几种常见的分布式锁实现方案

几种常见的分布式锁实现方案

Jedis实现分布式锁

添加依赖

      <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

编码实现

public class RedisLockTest {

    private static int count = 0;
    private static String lockKey = "lock";

    private static void call(Jedis jedis) {
        // 生成标识,由于超时原因可能导致释放其他线程的锁,释放锁时保证是释放自己的锁
        String requestId = UUID.randomUUID().toString();
        // 加锁
        boolean locked = tryLock(jedis, lockKey, requestId, 60);
        try {
            if (locked) {
                for (int i = 0; i < 1000; i++) {
                    count++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            unlock(jedis, lockKey, requestId);
        }
    }

    /**
     * 尝试获取分布式锁
     *
     * @param jedis      Redis客户端
     * @param lockKey    锁
     * @param requestId  锁的值
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        // 自旋锁
        while (true) {
            // set key value ex seconds nx(只有键不存在的时候才会设置key)
            String result = jedis.set(lockKey, requestId, SetParams.setParams().ex(expireTime).nx());
            if ("OK".equals(result)) {
                return true;
            }
        }
    }

    /**
     * 释放分布式锁
     *
     * @param jedis   Redis客户端
     * @param lockKey 锁
     * @return 是否释放成功
     */
    public static boolean unlock(Jedis jedis, String lockKey, String requestId) {
        if (!requestId.equals(jedis.get(lockKey))) {
            return false;
        }
        Long result = jedis.del(lockKey);
        return 1L == result;
    }

    public static void main(String[] args) throws Exception {
        RedisLockTest redisLockTest = new RedisLockTest();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMinIdle(1);
        jedisPoolConfig.setMaxTotal(5);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 1000, null);
        Thread t1 = new Thread(() -> RedisLockTest.call(jedisPool.getResource()));
        Thread t2 = new Thread(() -> RedisLockTest.call(jedisPool.getResource()));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(redisLockTest.count);
    }
}

RedisTemplate实现分布式锁

加锁: 调用set命令设置值,能够设置成功则表示加锁成功  set lock_key lock_value NX PX 5000

释放锁: 调用del命令来删除设置的键值  del lock_key

注意事项

编码实现

	@Autowired
    private StringRedisTemplate redisTemplate;
    
	@GetMapping("/test")
    public String test() {
        try {
        	// 获取当前线程id
            String threadId = Thread.currentThread().getId() + "";
            
             // 开始加锁
            Boolean locked = redisTemplate.opsForValue().setIfAbsent("lock", threadId, 5000, TimeUnit.MILLISECONDS);
            if (locked) {
                //加锁成功
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    log.info("库存扣减成功,剩余库存:{}", stock);
                } else {
                    log.info("库存不足,剩余库存:{}", stock);
                }

                String lockValue = redisTemplate.opsForValue().get("lock");
                if (threadId.equals(lockValue)) {
                    //释放锁
                    redisTemplate.delete("lock");
                }
            } else {
                log.info("获取锁失败...");
            }
        } catch (Exception ex) {
            log.info("出现异常....");
        }

        return "success";
    }

Redisson实现分布式锁

https://redisson.org/

添加依赖

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.16.6</version>
</dependency>  

配置Redisson

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;
    @Value("${spring.redis.password}")
    private String password;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        config.useSingleServer().setPassword(password);
        return Redisson.create(config);
    }
}

编码实现

	@Autowired
    private RedissonClient redissonClient;
    	
    @GetMapping("/test")
    public String test() {
        //获得分布式锁对象
        RLock lock = redissonClient.getLock("lock");
        try {
            //尝试加锁,如果加锁成功则后续程序继续执行,如果加锁不成功则阻塞等待
            lock.lock(5000, TimeUnit.MILLISECONDS);

            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                log.info("库存扣减成功,剩余库存:{}", stock);
            } else {
                log.info("库存不足,剩余库存:{}", stock);
            }
        } catch (Exception exception) {
            log.info("出现异常....");
        } finally {
            //解锁
            lock.unlock();
        }
        return "success";
    }
}

ZooKeeper实现分布式锁

Zookeeper中节点分为4种类型:

持久节点 (PERSISTENT)

默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在

持久顺序节点(PERSISTENT_SEQUENTIAL)

顺序节点就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号

临时节点(EPHEMERAL)

和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除

临时顺序节点(EPHEMERAL_SEQUENTIAL)

临时顺序节点结合和临时节点和顺序节点的特点:在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后,临时节点会被删除

Zookeeper实现分布式锁的原理是基于Zookeeper的临时顺序节点

Apache Curator官网: https://curator.apache.org/

添加依赖

	    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.14</version>
        </dependency>

配置Zookeeper

@Configuration
public class ZkConfig {
    @Bean
    public CuratorFramework curatorFramework(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("IP:2181")
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();
        client.start();
        return client;
    }
}

编码实现

    @Autowired
    private CuratorFramework curatorFramework;
    
	@GetMapping("/test")
    public String test() {
        InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/lock");

        try {
            boolean locked = mutex.acquire(0, TimeUnit.SECONDS);
            if (locked) {
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    stock--;
                    redisTemplate.opsForValue().set("stock", stock + "");
                    log.info("库存扣减成功,剩余库存:{}", stock);
                } else {
                    log.info("库存不足,剩余库存:{}", stock);
                }
                //释放锁
                mutex.release();
            } else {
                log.info("获取锁失败...");
            }
        } catch (Exception exception) {
            log.info("出现异常....");
        }

        return "success";
    }
举报

相关推荐

0 条评论