一.分布式锁介绍

由于传统的锁是基于Tomcat服务器内部的,搭建了集群之后,导致锁失效,应该使用分布式锁来处理。

基于Zookeeper与Redis进行分布式锁的代码实现_分布式锁

二.分布式锁解决方案

我们要想实现分布式锁,可以使用两种方案:

  • 1.基于Zookeeper;
  • 2.基于Redis。

下面我分别就两种方案进行代码实现讲解。

先展示一段未加锁时的秒杀需求

创建SpringBoot项目,编写抢购的业务。

@RestController
public class SecondKillController {

//1. 准备商品的库存
public static Map<String,Integer> itemStock = new HashMap<>();

//2. 准备商品的订单
public static Map<String,Integer> itemOrder = new HashMap<>();

static{
itemStock.put("牙刷",10000);
itemOrder.put("牙刷",0);
}

@GetMapping("/kill")
public String kill(String item) throws InterruptedException {

//1. 减库存
Integer stock = itemStock.get(item);
if(stock <= 0){
return "商品库存数不足!!!";
}

Thread.sleep(100);
itemStock.put(item,stock - 1);

//2. 创建订单
Thread.sleep(100);
itemOrder.put(item,itemOrder.get(item) + 1);

//3. 返回信息
return "抢购成功!!!" + item + ": 剩余库存数为 - " + itemStock.get(item) + ",订单数为 - " + itemOrder.get(item);
}
}

下载ab进行压力测试

ab -n 请求数 -c 并发数 访问的路径

进行压力测试的结果展示:

基于Zookeeper与Redis进行分布式锁的代码实现_1024程序员节_02

会发现在进行并发操作时存在数量不一致的问题!出现了超卖的现象。

三.基于Zookeeper实现分布式锁

基于Zookeeper与Redis进行分布式锁的代码实现_redis_03

1. 创建项目,添加依赖

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>

2. 编写配置类

@Configuration
public class ZkConfig {

@Bean
public CuratorFramework cf(){

RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString("192.168.199.109:2181,192.168.199.109:2182,192.168.199.109:2183")
.retryPolicy(retryPolicy)
.build();

curatorFramework.start();

return curatorFramework;
}

}

3. 在业务代码中添加分布式锁

InterProcessMutex lock = new InterProcessMutex(cf,"/lock");

//...加锁
lock.acquire();
lock.acquire(1,TimeUnit.SECONDS); // 指定排队多久就放弃获取锁资源

//----------------业务逻辑代码------------------------

// 释放锁
lock.release();

4. 进行ab压力测试

   可以发现并发抢购时不会出现超卖现象,卖出的商品与剩余商品的数量完全对应,保证了抢购时的数据一致性!

四.基于Redis实现分布式锁

基于Zookeeper与Redis进行分布式锁的代码实现_spring_04

1.创建springboot项目,添加依赖包

# redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.编写yml配置文件

# 配置文件
spring:
redis:
host: 192.168.199.109
port: 6379

3.编写工具类

@Component
public class RedisLockUtil {

@Autowired
private StringRedisTemplate redisTemplate;

public boolean lock(String key,String value,int second){
return redisTemplate.opsForValue().setIfAbsent(key,value,second,TimeUnit.SECONDS);
}

public void unlock(String key){
redisTemplate.delete(key);
}

}

4.修改业务逻辑代码

@GetMapping("/redis/kill")
public String redisKill(String item) throws Exception {
//...加锁
if(lock.lock(item,System.currentTimeMillis() + "",1)){
// 业务代码。。。
// 释放锁
lock.unlock(item);
}
}

5.进行ab压力测试

  会发现也可以解决抢购时的超卖问题。