基于Zookeeper与Redis进行分布式锁的代码实现
原创
©著作权归作者所有:来自51CTO博客作者一一哥Sun的原创作品,请联系作者获取转载授权,否则将追究法律责任
一.分布式锁介绍
由于传统的锁是基于Tomcat服务器内部的,搭建了集群之后,导致锁失效,应该使用分布式锁来处理。
二.分布式锁解决方案
我们要想实现分布式锁,可以使用两种方案:
下面我分别就两种方案进行代码实现讲解。
先展示一段未加锁时的秒杀需求
创建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实现分布式锁
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实现分布式锁
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压力测试
会发现也可以解决抢购时的超卖问题。