zookeeper案例-分布式锁-商品秒杀原理解析

锁:我们在多线程中接触过,作用就是让当前的资源不会被其他线程访问! 我的日记本,不可以被别人看到。所以要锁在保险柜中

当我打开锁,将日记本拿走了,别人才能使用这个保险柜

在zookeeper中使用传统的锁引发的 “羊群效应” :1000个人创建节点,只有一个人能成功,999 人需要等待!

羊群是一种很散乱的组织,平时在一起也是盲目地左冲右撞,但一旦有一只头羊动起来,其他的羊  也会不假思索地一哄而上,全然不顾旁边可能有的狼和不远处更好的草。羊群效应就是比喻人都有  一种从众心理,从众心理很容易导致盲从,而盲从往往会陷入骗局或遭到失败。

zookeeper案例-分布式锁-商品秒杀原理解析

 避免“羊群效应”,zookeeper采用分布式锁

zookeeper案例-分布式锁-商品秒杀原理解析

 

  1. 所有请求进来,在/lock下创建 临时顺序节点 ,放心,zookeeper会帮你编号排序
  2. 判断自己是不是/lock下最小的节点
    1. 是,获得锁(创建节点)
    2. 否,对前面小我一级的节点进行监听
    3. 获得锁请求,处理完业务逻辑,释放锁(删除节点),后一个节点得到通知(比你年轻的死了,你  成为最嫩的了)
    4. 重复步骤2

1. 实现步骤

1.1 初始化数据库

创建数据库zkproduct,使用默认的字符集utf8

-- 商品表
create table product(
id int primary key auto_increment,	-- 商品编号product_name varchar(20) not null, -- 商品名称stock int not null, -- 库存
version int not null -- 版本
)

insert into product (product_name,stock,version) values('锦鲤-清空购物车-大奖',5,0)
-- 订单表
create table `order`(
id varchar(100) primary key, -- 订单编号
pid int not null,	-- 商品编号
userid int not null	-- 用户编号
)

1.2 搭建工程

搭建ssm框架,对库存表-1,对订单表+1

zookeeper案例-分布式锁-商品秒杀原理解析

具体代码见github

1.3 启动测试

  1. 启动两次工程,端口号分别8001和8002
  2. 使用nginx做负载均衡

zookeeper案例-分布式锁-商品秒杀原理解析

 1.3 使用 JMeter 模拟1秒内发出10个http请求

zookeeper案例-分布式锁-商品秒杀原理解析

 下载地址:http://jmeter.apache.org/download_jmeter.cgi

zookeeper案例-分布式锁-商品秒杀原理解析

 zookeeper案例-分布式锁-商品秒杀原理解析

 

  1. 查看测试结果,10次请求全部成功
  2. 查看数据库,stock库存变成 -5 (并发导致的数据结果错误)

1.4 apahce提供的zookeeper客户端

基于zookeeper原生态的客户端类实现分布式是非常麻烦的,我们使用apahce提供了一个zookeeper客  户端来实现

Curatorh   ttp://curator.apache.org/

<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version> <!-- 网友投票最牛逼版本 -->
</dependency>

recipes是curator族谱大全,里面包含zookeeper和framework

1.5 在控制层中加入分布式锁的逻辑代码

@Controller
public class ProductAction {

@Autowired
private ProductService productService;

private static String connectString = "192.168.204.141:2181,192.168.204.142:2181,192.168.204.143:2181";

@GetMapping("/product/reduce") @ResponseBody
public Object reduce( int id) throws Exception {
// 重试策略 (1000毫秒试1次,最多试3次)
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
//1.创建curator工具对象CuratorFramework client =
CuratorFrameworkFactory.newClient(connectString, retryPolicy); client.start();
//2.根据工具对象创建“内部互斥锁”
InterProcessMutex lock = new InterProcessMutex(client, "/product_"+id); try {
//3.加锁
lock.acquire(); productService.reduceStock(id);
}catch(Exception e){
if(e instanceof RuntimeException){ throw e;
}
}finally{
//4.释放锁lock.release();
}
return "ok";
}
}

再次测试,并发问题解决!

节选自拉钩教育JAVA系列课程

上一篇:分布式协调-Zookeeper使用(Watcher、Curator、Session、Acl)


下一篇:java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor()