【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檔案中加入以下依賴
(2) Product.java<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>
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.javapackage 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
【zookeeper】Apache 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 OpenJudge】【1.4】程式設計基礎之邏輯表示式與條件分支
01:判斷數正負 #include<cstdio> #include<iostream> using namespace std; int main(){ int n; cin>>n; if(n > 0){
【NOI OpenJudge】【1.3】程式設計基礎之算術表示式與順序執行
01: A+B問題 #include<iostream> using namespace std; int main(){ int a, b; cin>>a>>b; cout<<a+b<<"\n"; return
【NOI OpenJudge】【1.2】程式設計基礎之變數定義、賦值及轉換
01: 整型資料型別儲存空間大小 #include<cstdio> int main(){ int a; short b; printf("%d %d",sizeof(a),sizeof(b)); return 0; } 02: 浮點型資料型別儲存空間大小
【NOI OpenJudge】【1.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