ZooKeeper系列(二)
之
商品秒杀
今天跟大家分享一个使用ZooKeeper锁服务(Lock Service)机制实现商品秒杀的一个生活案例。
一、ZooKeeper环境准备
准备三台服务器作为ZooKeeper(简称ZK)集群搭建环境,搭建方法参见我们之前分享的《ZooKeeper系列(一)之集群环境搭建》一文,各服务器信息如下:
在每台服务器上执行zkServer.sh start,启动ZK集群,执行zkServer.sh status命令,查看各节点运行状态,如图:
bigdata112:
bigdata113:
bigdata114:
我们看到113服务器作为ZK集群的leader,事实上,“领导者”是通过ZK的选举机制产生的。
二、商品秒杀代码实现
1、商品类Product
代码如下:
public classProduct {
//商品数量
private static intnum=100;
//秒杀商品
public static voidseckill(){
System.out.println("***************秒杀开始***************");
System.out.println("秒杀前商品余量:"+num);
//商品数减1
num--;
System.out.println("秒杀后商品余量:"+num);
try{
//模拟秒杀过程花费的时间
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("***************秒杀结束***************");
System.out.println();
}
}
Product类描述了我们要秒杀的商品,类中只有一个整型变量num和秒杀方法seckill,num表示库存中商品数量;seckill方法模拟了整个秒杀过程,秒杀成功,商品总量减1,这里我们使用线程沉睡来模拟秒杀过程花费的时间。
2、用户类Client
代码实现:
public classClient {
//开始秒杀
public voidstart() {
//重试策略
RetryPolicy rp =newExponentialBackoffRetry(1000,10);
//客户端
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.189.112:2181,192.168.189.113:2181,192.168.189.114:2181").retryPolicy(rp).build();
client.start();
//分布式锁
finalInterProcessMutex lock =newInterProcessMutex(client,"/mylock");
//开始秒杀
try{
//获取分布式锁
lock.acquire();
//秒杀商品
Product.seckill();
}catch(Exception e) {
e.printStackTrace();
}finally{
try{
//释放分布式锁
lock.release();
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
Client类模拟了用户秒杀行为,包括定义秒杀策略,创建客户端,实现分布式锁等,ExponentialBackoffRetry类描述了用户秒杀的策略:当用户秒杀失败时,每隔1秒重新秒杀,最多秒杀10次。
CuratorFrameworkFactory工厂类创建连接ZK服务器的客户端,并以秒杀策略连接ZK集群,连接字符串中配置了三台服务器的ZK地址和端口,主要是为了实现ZK集群的恢复性:当某个服务器因为某种原因出现单点故障,整个集群照样能够正常使用。
InterProcessMutex实现了分布式锁的定义,mylock是存储在ZK中的一个znode(z-节点),事先不用在ZK中创建,当ZK中没有该znode时会自动创建,事实上,ZK是一个树形结构的文件系统,每个znode节点即是文件又是目录,说是文件是指znode本身可以存储不超过1M的数据,目录是指znode下可以再创建新的znode作为子节点。
3、主类Main
代码如下:
public classTestDistributedLock {
public static voidmain(String[] args) {
//创建100个线程,模拟100个秒杀用户
for(inti=;i
newThread(newRunnable() {
public voidrun() {
newClient().start();
}
}).start();
}
}
}
主类非常简单,通过for循环创建了100个线程,而每个线程创建了一个用户。也就是说主类模拟了100用户秒杀商品。
我们执行程序,部分结果如下:
ZK的mylock节点下生成了100个节点,用于存储每个客户的数据,部分结果如图:
从运行结果看,每次只能有一个用户成功秒杀商品,我们可以停止任意一台服务器,因为当某台服务器被停止后,例如停止bigdata113(leader),ZK集群会从剩余的两台服务器中选举新的leader,继续接受客户端的write请求(这里就是秒杀)。事实上,在2n+1台ZK集群中,有n+1台服务器正常工作,ZK集群就能正常工作(我们这里是3台ZK集群,有2台服务器正常工作即可)。
领取专属 10元无门槛券
私享最新 技术干货