1. 程式人生 > >【Zookeeper】程式設計實戰之Zookeeper分散式鎖實現秒殺

【Zookeeper】程式設計實戰之Zookeeper分散式鎖實現秒殺

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官網對它的介紹。    從官網的介紹中,可以總結出,Zookeeper是一個集中式服務,它能夠實現高度可靠的分散式協調,可用於開發和維護開源

Redis分散式 實現系統 SET命令實現

基於Redis命令:SET key valueNX EX max-lock-time   可適用於redis單機和redis叢集模式 1.SET命令是原子性操作,NX指令保證只要當key不存在時才會設定value 2.設定的value要有唯一性,來確保鎖不會被誤刪(

基於redis分散式實現

最近在專案中遇到了類似“秒殺”的業務場景,在本篇部落格中,我將用一個非常簡單的demo,闡述實現所謂“秒殺”的基本思路。 業務場景 所謂秒殺,從業務角度看,是短時間內多個使用者“爭搶”資源,這裡的資源在大部分秒殺場景裡是商品;將業務抽象,技術角度看,秒殺就是

SpringMVC+Redis實現分散式實現功能

1.實現分散式鎖的幾種方案 1.Redis實現 (推薦) 2.Zookeeper實現 3.資料庫實現 Redis實現分散式鎖 * * 在叢集等多伺服器中經常使用到同步處理一下業務,這是普通的事務是滿足不了業務需求,需要分散

Zookeeper C++程式設計實戰主備切換

預設zookeeper日誌輸出到stderr, 可以呼叫zoo_set_log_stream(FILE*)設定輸出到檔案中 還可以呼叫zoo_set_debug_level(ZooLogLevel)控制日誌級別!!! 類CZookeeperHelper提供基於zookeeper的主備切換介面和讀

Zookeeper C++程式設計實戰配置更新

CZookeeperHelper:https://github.com/eyjian/libmooon/blob/master/include/mooon/net/zookeeper_helper.h CMainHelper:https://github.com/eyjian/libmooon/

原創redis庫存操作,分散式的四種實現方式[連載一]--基於zookeeper實現分散式

一、背景 在電商系統中,庫存的概念一定是有的,例如配一些商品的庫存,做商品秒殺活動等,而由於庫存操作頻繁且要求原子性操作,所以絕大多數電商系統都用Redis來實現庫存的加減,最近公司專案做架構升級,以微服務的形式做分散式部署,對庫存的操作也單獨封裝為一個微服務,這樣在高併發情況下,加減庫存時,就會出現超賣等

java併發程式設計實戰理解顯示ReentrantLock

前面兩篇部落格分別介紹了通過synchronized關鍵字(內建鎖機制)和volatile關鍵字實現同步機制。由於volatile實現的同步不能保證操作的原子性,因此一般常用內建鎖實現同步機制,但java5.0版本的內建鎖在功能上有很多缺陷:如無法中斷一個正在等

原創redis庫存操作,分散式的四種實現方式[連載二]--基於Redisson實現分散式

一、redisson介紹 redisson實現了分散式和可擴充套件的java資料結構,支援的資料結構有:List, Set, Map, Queue, SortedSet, ConcureentMap, Lock, AtomicLong, CountDownLatch。並且是執行緒安全的,底層使用N

連載redis庫存操作,分散式的四種實現方式[三]--基於Redis watch機制實現分散式

一、redis的事務介紹 1、 Redis保證一個事務中的所有命令要麼都執行,要麼都不執行。如果在傳送EXEC命令前客戶端斷線了,則Redis會清空事務佇列,事務中的所有命令都不會執行。而一旦客戶端傳送了EXEC命令,所有的命令就都會被執行,即使此後客戶端斷線也沒關係,因為Redis中已經記錄了所有要執行的

C++智慧指標引用計數的實現

在C++11的標準中,引入了智慧指標的概念。 相比於auto_ptr而言,其主要缺陷在於在進行指標拷貝的時候,會出現管理權轉移的問題,導致原指標最終編成一個懸掛指標(dangling pointer

zookeeperApache curator的使用及zk分散式實現

接上篇,本篇主要講Apache開源的curator的使用,有了curator,利用Java對zookeeper的操作變得極度便捷. 其實在學之前我也有個疑慮,我為啥要學curator,撇開漲薪這些外在的東西,就單技術層面來講,學curator能幫我做些什麼?這就不得不從zookeeper說起,上

Zookeeper原始碼分析伺服器(二)

一、前言   前面闡述了伺服器的總體框架,下面來分析伺服器的所有父類ZooKeeperServer。 二、ZooKeeperServer原始碼分析   2.1 類的繼承關係  public class ZooKeeperServer implements SessionE

java併發程式設計實戰—–執行緒基本概念

轉自 http://cmsblogs.com/?p=1638 共享和可變 要編寫執行緒安全的程式碼,其核心在於對共享的和可變的狀態進行訪問。 “共享”就意味著變數可以被多個執行緒同時訪問。我們知道系統中的資源是有限的,不同的執行緒對資源都是具有著同等的使用權。有限、公平就意味著競爭

原創需求評審實戰演練

一 我在面試時,經常會出一道簡易計算器需求的程式設計題,完了之後再讓寫一下這個需求的用例,題目看起來很簡單,但是幾乎可以把我想了解到的基礎測試理論全部都涵蓋了。 今天我還拿這個例子來實操下在《測試人員參與需求評審的價值是什麼?》中提到的需求評審關注點。 比如我現

NOI OpenJudge1.4程式設計基礎邏輯表示式與條件分支

01:判斷數正負 #include<cstdio> #include<iostream> using namespace std; int main(){ int n; cin>>n; if(n > 0){

NOI OpenJudge1.3程式設計基礎算術表示式與順序執行

01: A+B問題 #include<iostream> using namespace std; int main(){ int a, b; cin>>a>>b; cout<<a+b<<"\n"; return

NOI OpenJudge1.2程式設計基礎變數定義、賦值及轉換

01: 整型資料型別儲存空間大小 #include<cstdio> int main(){ int a; short b; printf("%d %d",sizeof(a),sizeof(b)); return 0; } 02: 浮點型資料型別儲存空間大小

NOI OpenJudge1.1程式設計基礎輸入輸出

01:Hello, World! #include<iostream> #include<cstdio> using namespace std; int main(){ printf("Hello, World!"); return 0;

redis學習基於redis的分散式實現

    在單個JVM中,我們可以很方便的用sychronized或者reentrantLock在資源競爭時進行加鎖,保證高併發下資料執行緒安全。但是若是分散式環境下,多個JVM同時對一個資源進行競爭時,我們該如何保證執行緒安全呢?分散式鎖便能實現我們的要求。   &n