1. Zookeeper简述

我们要了解一样技术,首先应该要到它的官网,因为官网的信息一般都是最准确的,如下图是Zookeeper官网对它的介绍。

从官网的介绍中,可以总结出,Zookeeper是一个集中式服务,它能够实现高度可靠的分布式协调,可用于开发和维护开源服务器。

除了官网的解释外,我的观点是还可以这样理解。它也相当于是一个数据库,具有数据同步和选举功能,能够用来存储一些信息,可用于解决大数据集群的单点故障问题。Zookeeper有leader和follow两种角色,当leader的节点宕掉之后,会自动选举出新的leader,如果只剩一个节点活着,就是standalone状态。Zookeeper各个节点之间的数据会自动同步,比如在Zookeeper集群的A节点存储数据,那么这份数据也会自动拷贝到集群中另外的节点上。在Hadoop、Storm、Spark集群都可以使用Zookeeper实现高可用(HA),防止出现单点故障。

2. 为什么要加锁

在多线程编程中,必须要考虑到线程安全问题,当共享数据被高并发访问时,会破坏数据的一致性。比如抢购商品,商品数量为1,有两个用户(线程)同时对它进行访问,当第一个线程拿到数据,还没有对数量执行减1操作的这段时间,第二个线程在这个时间段也拿到了数据,两个线程都对商品数量进行减1操作的话,就会出现商品数量是 -1 的数据,就违背了实际原则。

    因此,在程序中引入了锁,在线程访问共享数据之前,首先要请求锁,当得到这把锁的时候,才能够访问共享数据,使用完以后再归还这把锁。如果锁已经被一个线程获取,其它线程就请求不到锁,就执行重试策略,进入等待状态,不会访问共享数据,也就保证了数据的一致性。

3. 编程实战

3.1 原理

3.2 实现

3.2.1 代码

(1) 创建Maven项目,并在pom文件中加入以下依赖

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
</dependency>
</dependencies>

        (2) Product.java

package com.nova;
/**
*
* @author Supernova
* @date 2018/06/16
*
*/
public class Product {
// 商品数量,这里默认共有8件商品
private static int number = 5; public static int getNumber() {
return number;
} public static void setNumber(int number) {
Product.number = number;
} }

        (2) Client.java

package com.nova;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
*
* @author Supernova
* @date 2018/06/16
*
*/
public class Client {
/*
* 抢购商品的方法
* 作用:访问共享资源,获取并更新商品数量
*/
public static void buy() {
System.out.println("--------【"+Thread.currentThread().getName()+"】开始购买-------");
//获取商品数量
int currentNumber = Product.getNumber();
/*
* 如果商品数量为0,则不能购买
* 如果还有商品,则执行购买操作
*/
if(currentNumber == 0 ) {
System.out.println("商品已被抢空!!!");
}else {
System.out.println("当前商品数量:"+currentNumber); //购买后商品数量减1
currentNumber--;
Product.setNumber(currentNumber); //为了便于观察程序的运行结果,这里使线程在执行购买操作后,停顿3秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("--------【"+Thread.currentThread().getName()+"】 购买结束-------");
} public static void main(String[] args) {
/*
* 定义重试策略:等待2秒,重试10次
* 第一个参数:等待时间
* 第二个参数:重试次数
*/
RetryPolicy policy = new ExponentialBackoffRetry(2000, 10); /*
* 创建客户端向zookeeper请求锁
* connectString() : zookeeper地址
* retryPolicy() : 重试策略
*/
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().connectString("192.168.243.11").retryPolicy(policy).build();
//启用
curatorFramework.start(); //获取zookeeper锁的信息
final InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/myMutex"); /*
* 创建8个线程模拟8个客户端并发访问
*
*/
for (int i = 0; i < 8; i++) {
new Thread(new Runnable() {
public void run() {
try {
//请求锁资源,如果没有得到锁资源,就会执行重试策略
mutex.acquire();
//开始访问共享资源,这里是访问商品信息
buy(); } catch (Exception e) {
e.printStackTrace();
}finally {
try {
//将锁归还
mutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
} }
}

3.2.2 运行测试

        (1) 启动Zookeeper。通过Putty连接VM的Linux系统,我是在Hadoop伪分布的机器上启动的Zookeeper,只启动一个Zookeeper节点,因此是standalone的状态。 

        (2) 运行Java程序,程序运行结果如下图

可以看到运行的结果是线程安全的,只有在一个线程购买商品操作结束后,另一个线程才能接着购买。保证了数据的一致性。那么,如果去掉锁的情况是如何的呢?

  (3) 将请求锁的代码mutex.acquire();和mutex.release();注释掉之后。运行结果如下:

    从运行结果可以看出,如果没有锁的限制,程序运行的结果将会混乱。

 

【Zookeeper】编程实战之Zookeeper分布式锁实现秒杀的更多相关文章

  1. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  2. ZooKeeper典型应用场景:分布式锁

    分布式锁是控制分布式系统之间同步访问共享资源的一种方式.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致 ...

  3. Zookeeper--0300--java操作Zookeeper,临时节点实现分布式锁原理

    删除Zookeeper的java客户端有  : 1,Zookeeper官方提供的原生API, 2,zkClient,在原生api上进行扩展的开源java客户端 3, 一.Zookeeper原生API ...

  4. zookeeper学习实践1-实现分布式锁

    引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...

  5. Docker 下的Zookeeper以及.ne core 的分布式锁

    单节点 1.拉取镜像:docker pull zookeeper 2.运行容器 a.我的容器同一放在/root/docker下面,然后创建相应的目录和文件, mkdir zookeeper cd zo ...

  6. SpringBoot电商项目实战 — Redis实现分布式锁

    最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...

  7. Redis实战--Jedis实现分布式锁

    echo编辑整理,欢迎转载,转载请声明文章来源.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!!! 分布式 ...

  8. 用redis实现分布式锁,秒杀案例(转)

    分布式锁的简单实现代码: 需要的jar包: jedis-2.9.0.jar. commons-pool2-2.4.2.jar import java.util.List; import java.ut ...

  9. Java-redis分布式锁 抢购秒杀系统 实现

    一.使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现) 共享资源(各个系统访问同一个资源,资源的载体 ...

随机推荐

  1. 让UpdatePanel支持文件上传(1):开始 .

    UpdatePanel从一开始就无法支持AJAX的文件上传方式.Eilon Lipton写了一篇文章解释了这个问题的原因.文章中提供了两个绕开此问题的方法: 将“上传”按钮设为一个传统的PostBac ...

  2. golang 时间戳 时间格式化 获取当前时间 timestamp 计算时间差

    获取当前时间 func Now func Now() Time 1 Now returns the current local time. func (Time) UTC func (t Time) ...

  3. gitlab自动备份恢复与卸载

    一.gitlab数据备份gitlab-rake gitlab:backup:create命令会在/var/opt/gitlab/backups目录下创建一个名称类似为1393513186_gitlab ...

  4. Gdb调试工具/ Makfile项目管理

    gdb调试工具 gcc -g main.c -o main 常用命令 命令            简写         作用 help                h            按模块列 ...

  5. unity3d项目版本管理设置

    unity3d老是有一堆乱七八糟的文件,好像不提交也不行,特别是那烦人的meta文件,哪到底unity项目提交到版本管理哪些东西可以忽略呢?应该设置些什么东西呢? 菜单,Edit => Proj ...

  6. 在python中逐行读取大文件

    在我们日常工作中,难免会有处理日志文件的时候,当文件小的时候,基本不用当心什么,直接用file.read()或readlines()就可以了,但是如果是将一个10G大小的日志文件读取,即文件大于内存的 ...

  7. msysgit 上传文件夹,规范化的日常

    在我们第一次成功的上传到github之后,要上传文件夹的我们要在msysgit里输入些什么呢? 选择要上传的文件夹前一项右键点击git bash here 进入msysgit后 首先初始化,输入 gi ...

  8. scala当中的类型参数

    类型参数主要就是研究scala当中的类或者scala当中的方法的泛型 1.scala当中的类的泛型         object Demo8 {          def main(args: Arr ...

  9. scala当中的特质trait

    1.将trait作为接口使用 Scala中的trait是一种特殊的概念: 首先先将trait作为接口使用,此时的trait就与Java中的接口 (interface)非常类似: 在trait中可以定义 ...

  10. Linux的图形模式和文本模式以及单用户模式切换

    1.默认开机进入文本模式 如果想让开机自动进纯文本模式, 修改/etc/inittab 找到其中的 id:5:initdefault: 这行指示启动时的运行级是5,也就是图形模式 改成3就是文本模式了 ...