DCS实践干货:使用Redis实现分布式锁

场景介绍

很多互联网场景(如商品秒杀,论坛回帖盖楼等),需要用加锁的方式,以对某种资源进行顺序访问控制。如果应用服务集群部署,则涉及到对分布式应用加锁。当前分布式加锁主要有三种方式:(磁盘)数据库、缓存数据库、Zookeeper。接下里让我们一起看看加锁实践过程。

加锁实现

1  package dcsDemo01;

2

3  import java.util.UUID;

4

5  import redis.clients.jedis.Jedis;

6

7  public class DistributedLock {

8      private final String host = "192.168.0.220";

9      private final int port = 6379;

10

11      private static final String SUCCESS = "OK";

12      private static final String SET_IF_NOT_EXIST = "NX";

13      private static final String EXPIRE_TIME = "PX";

14

15      public  DistributedLock(){}

16

17      /*

18       * @param lockName      锁名

19       * @param timeout       获取锁的超时时间

20       * @param lockTimeout   锁的有效时间

21       * @return              锁的标识

22       */

23      public String getLockWithTimeout(String lockName, long timeout, long lockTimeout) {

24          String ret = null;

25          Jedis jedisClient = new Jedis(host, port);

26

27          try {

28              String authMsg = jedisClient.auth("[email protected]");

29              if (!SUCCESS.equals(authMsg)) {

30                  System.out.println("AUTH FAILED: " + authMsg);

31              }

32

33              String identifier = UUID.randomUUID().toString();

34              String lockKey = "DLock:" + lockName;

35              long end = System.currentTimeMillis() + timeout;

36

37              while(System.currentTimeMillis() < end) {

38                  String result = jedisClient.set(lockKey, identifier, SET_IF_NOT_EXIST, EXPIRE_TIME, lockTimeout);

39                  if(SUCCESS.equals(result)) {

40                      ret = identifier;

41                      break;

42                  }

43

44                  try {

45                      Thread.sleep(2);

46                  } catch (InterruptedException e) {

47                      Thread.currentThread().interrupt();

48                  }

49              }

50          }

51          catch (Exception e) {

52              e.printStackTrace();

53          }finally {

54              jedisClient.quit();

55              jedisClient.close();

56          }

57

58          return ret;

59      }

60

61      /*

62       * @param lockName        锁名

63       * @param identifier    锁的标识

64       */

65      public void releaseLock(String lockName, String identifier) {

66          Jedis jedisClient = new Jedis(host, port);

67

68          try {

69              String authMsg = jedisClient.auth("[email protected]");

70              if (!SUCCESS.equals(authMsg)) {

71                  System.out.println("AUTH FAILED: " + authMsg);

72              }

73

74              String lockKey = "DLock:" + lockName;

75              if(identifier.equals(jedisClient.get(lockKey))) {

76                  jedisClient.del(lockKey);

77              }

78          }

79          catch (Exception e) {

80              e.printStackTrace();

81          }finally {

82              jedisClient.quit();

83              jedisClient.close();

84          }

85      }

86  }

测试代码

假设20个线程对10台mate10手机进行抢购:

package dcsDemo01;

import java.util.UUID;

public class CaseTest {

public static void main(String[] args) {

ServiceOrder service = new ServiceOrder();

for (int i = 0; i < 20; i++) {

ThreadBuy client = new ThreadBuy(service);

client.start();

}

}

}

class ServiceOrder {

private final int MAX = 10;

DistributedLock DLock = new DistributedLock();

int n = 10;

public void handleOder() {

String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();

String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);

System.out.println("正在为用户:" + userName + " 处理订单");

if(n > 0) {

int num = MAX - n + 1;

System.out.println("用户:"+ userName + "购买第" + num + "台,剩余" + (--n) + "台");

}else {

System.out.println("用户:"+ userName + "无法购买,已售罄!");

}

DLock.releaseLock("Huawei Mate 10", identifier);

}

}

class ThreadBuy extends Thread {

private ServiceOrder service;

public ThreadBuy(ServiceOrder service) {

this.service = service;

}

@Override

public void run() {

service.handleOder();

}

}

运行结果

配置好实际的缓存实例连接地址、端口与连接密码,运行代码,得到以下结果:

正在为用户:eee56fb7Thread-16 处理订单

用户:eee56fb7Thread-16购买第1台,剩余9台

正在为用户:d6521816Thread-2 处理订单

用户:d6521816Thread-2购买第2台,剩余8台

正在为用户:d7b3b983Thread-19 处理订单

用户:d7b3b983Thread-19购买第3台,剩余7台

正在为用户:36a6b97aThread-15 处理订单

用户:36a6b97aThread-15购买第4台,剩余6台

正在为用户:9a973456Thread-1 处理订单

用户:9a973456Thread-1购买第5台,剩余5台

正在为用户:03f1de9aThread-14 处理订单

用户:03f1de9aThread-14购买第6台,剩余4台

正在为用户:2c315ee6Thread-11 处理订单

用户:2c315ee6Thread-11购买第7台,剩余3台

正在为用户:2b03b7c0Thread-12 处理订单

用户:2b03b7c0Thread-12购买第8台,剩余2台

正在为用户:75f25749Thread-0 处理订单

用户:75f25749Thread-0购买第9台,剩余1台

正在为用户:26c71db5Thread-18 处理订单

用户:26c71db5Thread-18购买第10台,剩余0台

正在为用户:c32654dbThread-17 处理订单

用户:c32654dbThread-17无法购买,已售罄!

正在为用户:df94370aThread-7 处理订单

用户:df94370aThread-7无法购买,已售罄!

正在为用户:0af94cddThread-5 处理订单

用户:0af94cddThread-5无法购买,已售罄!

正在为用户:e52428a4Thread-13 处理订单

用户:e52428a4Thread-13无法购买,已售罄!

正在为用户:46f91208Thread-10 处理订单

用户:46f91208Thread-10无法购买,已售罄!

正在为用户:e0ca87bbThread-9 处理订单

用户:e0ca87bbThread-9无法购买,已售罄!

正在为用户:f385af9aThread-8 处理订单

用户:f385af9aThread-8无法购买,已售罄!

正在为用户:46c5f498Thread-6 处理订单

用户:46c5f498Thread-6无法购买,已售罄!

正在为用户:935e0f50Thread-3 处理订单

用户:935e0f50Thread-3无法购买,已售罄!

正在为用户:d3eaae29Thread-4 处理订单

用户:d3eaae29Thread-4无法购买,已售罄!

不加锁场景

如果注释掉加锁代码,变成无锁情况,则抢购无序。

//测试类中注释两行用于加锁的代码:
public void handleOder() {
    String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();
    //加锁代码
    //String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);
    System.out.println("正在为用户:" + userName + " 处理订单");
    if(n > 0) {
        int num = MAX - n + 1;
        System.out.println("用户:"+ userName + "够买第" + num + "台,剩余" + (--n) + "台");
    }else {
        System.out.println("用户:"+ userName + "无法够买,已售罄!");
    }
    //加锁代码
    //DLock.releaseLock("Huawei Mate 10", identifier);
}

注释加锁代码后的运行结果,可以看出处理过程是无序的:

正在为用户:e04934ddThread-5 处理订单
正在为用户:a4554180Thread-0 处理订单
用户:a4554180Thread-0购买第2台,剩余8台
正在为用户:b58eb811Thread-10 处理订单
用户:b58eb811Thread-10购买第3台,剩余7台
正在为用户:e8391c0eThread-19 处理订单
正在为用户:21fd133aThread-13 处理订单
正在为用户:1dd04ff4Thread-6 处理订单
用户:1dd04ff4Thread-6购买第6台,剩余4台
正在为用户:e5977112Thread-3 处理订单
正在为用户:4d7a8a2bThread-4 处理订单
用户:e5977112Thread-3购买第7台,剩余3台
正在为用户:18967410Thread-15 处理订单
用户:18967410Thread-15购买第9台,剩余1台
正在为用户:e4f51568Thread-14 处理订单
用户:21fd133aThread-13购买第5台,剩余5台
用户:e8391c0eThread-19购买第4台,剩余6台
正在为用户:d895d3f1Thread-12 处理订单
用户:d895d3f1Thread-12无法购买,已售罄!
正在为用户:7b8d2526Thread-11 处理订单
用户:7b8d2526Thread-11无法购买,已售罄!
正在为用户:d7ca1779Thread-8 处理订单
用户:d7ca1779Thread-8无法购买,已售罄!
正在为用户:74fca0ecThread-1 处理订单
用户:74fca0ecThread-1无法购买,已售罄!
用户:e04934ddThread-5购买第1台,剩余9台
用户:e4f51568Thread-14购买第10台,剩余0台
正在为用户:aae76a83Thread-7 处理订单
用户:aae76a83Thread-7无法购买,已售罄!
正在为用户:c638d2cfThread-2 处理订单
用户:c638d2cfThread-2无法购买,已售罄!
正在为用户:2de29a4eThread-17 处理订单
用户:2de29a4eThread-17无法购买,已售罄!
正在为用户:40a46ba0Thread-18 处理订单
用户:40a46ba0Thread-18无法购买,已售罄!
正在为用户:211fd9c7Thread-9 处理订单
用户:211fd9c7Thread-9无法购买,已售罄!
正在为用户:911b83fcThread-16 处理订单
用户:911b83fcThread-16无法购买,已售罄!
用户:4d7a8a2bThread-4购买第8台,剩余2台

总的来说,使用DCS服务中Redis类型的缓存实例实现分布式加锁,有几大优势:

1、加锁操作简单,使用SET、GET、DEL等几条简单命令即可实现锁的获取和释放。

2、性能优越,缓存数据的读写优于磁盘数据库与Zookeeper。

3、可靠性强,DCS有主备和集群实例类型,避免单点故障。

以上代码实现仅展示使用DCS服务进行加锁访问的便捷性,具体技术实现需要考虑死锁、锁的检查等情况,欢迎点击分布式缓存服务DCS了解更多。

原文地址:https://www.cnblogs.com/middleware/p/9237098.html

时间: 2024-11-13 06:33:49

DCS实践干货:使用Redis实现分布式锁的相关文章

基于redis的分布式锁的分析与实践

转:https://my.oschina.net/wnjustdoit/blog/1606215 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于线程之间是否相互阻塞. 那么,本文主要来讨论基于redis的分布式锁算法问题. 从2.6.12版本开始,redis为SET命令增加了一系列选项(SET key value [EX seconds] [PX

利用Redis实现分布式锁

写在最前面 我在之前总结幂等性的时候,写过一种分布式锁的实现,可惜当时没有真正应用过,着实的心虚啊.正好这段时间对这部分实践了一下,也算是对之前填坑了. 分布式锁按照网上的结论,大致分为三种:1.数据库乐观锁: 2.基于Redis的分布式锁:3..基于ZooKeeper的分布式锁: 关于乐观锁的实现其实在之前已经讲的很清楚了,有兴趣的移步:使用mysql乐观锁解决并发问题 .今天先简单总结下redis的实现方法,后面详细研究过ZooKeeper的实现原理后再具体说说ZooKeeper的实现. 为

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

基于Redis实现分布式锁(转载)

原文地址:http://blog.csdn.net/ugg/article/details/41894947 Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令(SET if Not eXists)语法:SETNX key value功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1:若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0. GETSET命令语法:GETSET key value功能:将给定 key 的值设为

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

Redis实现分布式锁

http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性. 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock,我们相信这个算法会比一般的普通方法更加安全可靠.我们也希望社区能一起分析这个算法,

基于redis的分布式锁

<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * https://gist.github.com/nickyleach/3694555 */ pc_base::load_sys_class('cache_redis', '', 0); class dist_key_redis { //锁的超时时间 const TIMEOUT = 20; const SL

转载:基于Redis实现分布式锁

转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分

基于Redis实现分布式锁

http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决