袁庭新老師帶你玩轉Zookeeper實現分布式鎖商品秒殺
對大名鼎鼎的Zookeeper分布式服務框架,想必很多小伙伴兒都聽過,今天袁老師就將帶領大家來學習Zookeeper相關的知識。首先,給大家介紹下ZooKeeper框架。ZooKeeper是一個分布式的(多臺服務器干一件事)、開放源碼的分布式應用程序協(xié)調服務,這是對Google Chubby開源項目的實現,是Hadoop和Hbase的重要組件。它是一個為分布式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分布式同步、組服務等。

美團、餓了么、淘寶和58同城等項目都是Zookeeper的現實生活版。舉個例子,袁老師我開了家飯店,如何才能讓大家都能吃到我的飯菜呢?只要入駐美團,大家就可以在美團APP中看到我的飯店進行下單了,這樣就完成了一次交易。
今天袁老師會給各位小伙伴們分享一篇關于分布式框架Zookeeper的高級使用教程,通過Zookeeper來實現分布式鎖的功能。

01 分布式鎖介紹
我們在多線程中接觸過鎖的概念,其作用就是讓當前的資源不會被其他線程訪問。給大家舉個例子:
我的日記本,不可以被別人看到,所以要鎖在保險柜中。
當我打開鎖,將日記本拿走后,別人才能使用這個保險柜。
在Zookeeper中使用傳統(tǒng)的鎖會引發(fā)的“羊群效應”:1000個人創(chuàng)建節(jié)點,只有一個人能成功,999人需要等待。
羊群是一種很散亂的組織,平時在一起也是盲目地左沖右撞,但一旦有一只頭羊動起來,其他的羊也會不假思索地一哄而上,全然不顧旁邊可能有的狼和不遠處更好的草。所以”羊群效應“就是比喻人都有 一種從眾心理,從眾心理很容易導致盲從,而盲從往往會陷入騙局或遭到失敗。袁老師通過下面這幅圖來解析Zookeeper分布式鎖的實現原理。

為了避免“羊群效應”的產生,Zookeeper采用了分布式鎖來解決。

我們可以將分布式鎖的實現概括為以下四個步驟:
1.所有請求進來后,在/lock下創(chuàng)建臨時順序節(jié)點,Zookeeper會幫你編號排序。
2.判斷自己是不是/lock下最小的節(jié)點。
? ?- 是,獲得鎖(創(chuàng)建節(jié)點)
? ?- 否,對前面小我一級的節(jié)點進行監(jiān)
3.獲得鎖請求,處理完業(yè)務邏輯,釋放鎖(刪除節(jié)點),后一個節(jié)點得到通知。
4.重復上面的步驟二。
接下來我們就按照以上步驟進行代碼實現吧。
02 搭建項目環(huán)境
1.初始化數據庫
我們先創(chuàng)建一個數據庫zk_db數據庫實例,并使用默認的字符集utf8。
然后在zk_db數據庫中創(chuàng)建商品表(t_product)和訂單表(t_order)。
2.搭建工程
首先,請大家跟著袁老師來創(chuàng)建一個Maven類型的項目工程,項目名稱設置為【zk_product】,然后在main?錄下創(chuàng)建?個webapp?件夾,并自動生成webapp/WEB-INF/web.xml核心配置文件。項目的工程搭建核心步驟見下。
1.我們可以搭建SSM架構類型的項目,搭建完后項目的目錄結構見下:

2.接著在項目的pom.xml配置文件中導入依賴:
3.然后在resources目錄下創(chuàng)建mybatis目錄,并在該目錄下創(chuàng)建MyBatis的配置文件mybatis-config.xml。
4.在resources目錄下創(chuàng)建jdbc.properties文件,并在該文件中添加數據庫連接配置。
5.接著在resources目錄下創(chuàng)建spring目錄,并在該目錄下創(chuàng)建Spring的配置文件spring.xml。
6.最后在web.xml文件中添加配置。
03 業(yè)務功能實現
1.在com.cy.product.pojo包下創(chuàng)建Order實體類。
2.在com.cy.product.pojo包下創(chuàng)建Product實體類。
3.在OrderMapper接口中添加生成訂單的方法。
4.在ProductMapper接口中添加查詢商品、減庫存的方法。
5.在OrderService接口中添加reduceStock(int id)方法。
6.在OrderServiceImpl類中實現reduceStock(int id)方法。
7.在ProductAction類中配置訪問的路徑。
8.啟動項目工程。然后雙擊選項【Maven】-【zk_product】-【Lifecycle】-【package】啟動項目工程。最后通過瀏覽器訪問http://localhost:8001/product/reduce?id=1地址進行測試。
9.異常情況。項目啟動后,如果訪問上述地址時提示NoSuchMethodError的異常。出現這種情況,是因為spring-webmvc依賴的版本太高了,設置為5.3.0以下。
04 功能測試
接下來我們要搭建一個測試環(huán)境,大家可以跟著袁老師安裝Nginx和JMeter。
1.Nginx配置
在CentOS系統(tǒng)中,我們喲啊提前安裝好Nginx服務器,關于Nginx的安裝在這里我就不再贅述了。
然后要進入Nginx安裝目錄,編輯nginx.conf配置文件。
最后在nginx.conf文件中添加如下的配置做負載均衡。
2.JMeter配置
訪問JMeter官網地址見下鏈接:
根據不同操作系統(tǒng)選擇對應的版本。

2.1 Windows系統(tǒng)安裝JMeter
在windows系統(tǒng)下下,我們將下載的apache-meter-5.5.zip壓縮包解壓到指定目錄下即可。然后新建一個JMETER_HOME環(huán)境變量,變量值填寫解壓后的目錄。
接著我們要在用系統(tǒng)變量path中添加bin配置。
2.2 Mac OS系統(tǒng)安裝JMeter
如果是在Mac OS系統(tǒng)下,我們下載成功后,需要在終端使用命令進入下載文件夾,通過命令解壓壓縮包。
接著配置JMeter的啟動方式,在終端打開環(huán)境變量配置.bash_profile文件。
然后在.bash_profile文件中添加JMeter環(huán)境變量的配置。
保存后關閉文檔,使用命令使文件生效。
我們在Mac終端直接輸入以下命令,就可以直接打開JMeter頁面。
界面設置為永久中文。找到JMeter安裝目錄下的bin目錄,打開jmeter.properties文件。
請求的響應體亂碼解決。找到JMeter安裝目錄下的bin目錄,打開jmeter.properties文件。
3.使用JMeter
我們可以打開JMeter的選項【Test Plan】-【Add】-【Threads (Users)】-【Thread Group】,并做如下的配置。

在JMeter窗口中右鍵選項【Thread Group】并選擇【Add】-【Sampler】-【HTTP Request】,做如下的配置。

最后右鍵選項【HTTP Request】并選擇【Add】-【Listener】-【View Results Tree】選項,然后點擊“播放”按鈕執(zhí)行請求發(fā)送。
4.項目測試
我們可以啟動兩次工程,端口號分別設置為8001和8002。在不同的端口號下啟動同一個項目,相當于運行了兩個項目。
使用JMeter工具模擬1秒內發(fā)出10個THHP請求。查看測試結果,10次請求全部成功。請求執(zhí)行結束后,查看數據庫,發(fā)現表中的stock字段的庫存數量變?yōu)?5 (并發(fā)導致的數據結果錯誤)。
05 Zookeeper解決并發(fā)問題
基于Zookeeper原生態(tài)的客戶端類實現分布式是非常麻煩的,我們可以使用Apahce Curator提供的一個Zookeeper客戶端來實現。Curator官網:http://curator.apache.org。
我們先在項目的pom.xml文件中添加Curator依賴。
recipes是Curator族譜大全,里面包含Zookeeper和Framework。然后在控制層中加入分布式鎖的邏輯代碼。
接著將項目中的pom.xml文件中配置的端口分別設置為8001和8002,然后啟動兩次工程。
并再次使用JMeter工具,模擬1秒內發(fā)出10個THHP請求進行測試,從測試結果看出并發(fā)問題解決。
06 回顧總結
利用Zookeeper分布式鎖實現商品秒殺的案例,袁老師就給大家分享完了。在今天的內容中,袁老師首先給大家介紹了分布式鎖的概念以及分布式鎖的實現原理,然后通過代碼實現Zookeeper分布式鎖商品秒殺。為了模擬出分布式環(huán)境下秒殺的問題,我們搭建了Nginx代理服務,并通過JMeter進行高并發(fā)請求壓力測試,最后通過Curator工具來解決分布式存在的并發(fā)問題。
今天的內容,你學會了嗎?關注「袁庭新」,干貨天天都不斷!
