溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

發(fā)布時(shí)間:2020-08-08 10:02:05 來(lái)源:ITPUB博客 閱讀:150 作者:lihong 欄目:編程語(yǔ)言

1.分布式事務(wù)

2. 分布式鎖

Java 原生 API 雖然有并發(fā)鎖,但并沒(méi)有提供分布式鎖的能力,所以針對(duì)分布式場(chǎng)景中的鎖需要解決的方案。

分布式鎖的解決方案大致有以下幾種:

  • 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)
  • 基于緩存(redis,memcached 等)實(shí)現(xiàn)
  • 基于 Zookeeper 實(shí)現(xiàn)

2.1. 基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖

實(shí)現(xiàn)

1. 創(chuàng)建表

CREATE TABLE `methodLock` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法名',
  `desc` varchar(1024) NOT NULL DEFAULT '備注信息',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數(shù)據(jù)時(shí)間,自動(dòng)生成',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';

2. 獲取鎖

想要鎖住某個(gè)方法時(shí),執(zhí)行以下 SQL:

insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)

因?yàn)槲覀儗?duì)  method_name  做了唯一性約束,這里如果有多個(gè)請(qǐng)求同時(shí)提交到數(shù)據(jù)庫(kù)的話(huà),數(shù)據(jù)庫(kù)會(huì)保證只有一個(gè)操作可以成功,那么我們就可以認(rèn)為操作成功的那個(gè)線(xiàn)程獲得了該方法的鎖,可以執(zhí)行方法體內(nèi)容。

成功插入則獲取鎖。

3. 釋放鎖

當(dāng)方法執(zhí)行完畢之后,想要釋放鎖的話(huà),需要執(zhí)行以下 Sql:

delete from methodLock where method_name ='method_name'

問(wèn)題
  1. 這把鎖強(qiáng)依賴(lài)數(shù)據(jù)庫(kù)的可用性。如果數(shù)據(jù)庫(kù)是一個(gè)單點(diǎn),一旦數(shù)據(jù)庫(kù)掛掉,會(huì)導(dǎo)致業(yè)務(wù)系統(tǒng)不可用。
  2. 這把鎖沒(méi)有失效時(shí)間,一旦解鎖操作失敗,就會(huì)導(dǎo)致鎖記錄一直在數(shù)據(jù)庫(kù)中,其他線(xiàn)程無(wú)法再獲得到鎖。
  3. 這把鎖只能是非阻塞的,因?yàn)閿?shù)據(jù)的 insert 操作,一旦插入失敗就會(huì)直接報(bào)錯(cuò)。沒(méi)有獲得鎖的線(xiàn)程并不會(huì)進(jìn)入排隊(duì)隊(duì)列,要想再次獲得鎖就要再次觸發(fā)獲得鎖操作。
  4. 這把鎖是非重入的,同一個(gè)線(xiàn)程在沒(méi)有釋放鎖之前無(wú)法再次獲得該鎖。因?yàn)閿?shù)據(jù)中數(shù)據(jù)已經(jīng)存在了。

解決辦法
  1. 單點(diǎn)問(wèn)題可以用多數(shù)據(jù)庫(kù)實(shí)例,同時(shí)塞 N 個(gè)表,N/2+1 個(gè)成功就任務(wù)鎖定成功
  2. 寫(xiě)一個(gè)定時(shí)任務(wù),隔一段時(shí)間清除一次過(guò)期的數(shù)據(jù)。
  3. 寫(xiě)一個(gè) while 循環(huán),不斷的重試插入,直到成功。
  4. 在數(shù)據(jù)庫(kù)表中加個(gè)字段,記錄當(dāng)前獲得鎖的機(jī)器的主機(jī)信息和線(xiàn)程信息,那么下次再獲取鎖的時(shí)候先查詢(xún)數(shù)據(jù)庫(kù),如果當(dāng)前機(jī)器的主機(jī)信息和線(xiàn)程信息在數(shù)據(jù)庫(kù)可以查到的話(huà),直接把鎖分配給他就可以了。

小結(jié)
  • 優(yōu)點(diǎn): 直接借助數(shù)據(jù)庫(kù),容易理解。
  • 缺點(diǎn): 會(huì)有各種各樣的問(wèn)題,在解決問(wèn)題的過(guò)程中會(huì)使整個(gè)方案變得越來(lái)越復(fù)雜。操作數(shù)據(jù)庫(kù)需要一定的開(kāi)銷(xiāo),性能問(wèn)題需要考慮。

2.2. 基于 Redis 實(shí)現(xiàn)分布式鎖

相比于用數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)分布式鎖,基于緩存實(shí)現(xiàn)的分布式鎖的性能會(huì)更好一些。目前有很多成熟的分布式產(chǎn)品,包括 Redis、memcache、Tair 等。這里以 Redis 舉例。

Redis 命令
  • setnx - setnx key val:當(dāng)且僅當(dāng) key 不存在時(shí),set 一個(gè) key 為 val 的字符串,返回 1;若 key 存在,則什么都不做,返回 0。
  • expire - expire key timeout:為 key 設(shè)置一個(gè)超時(shí)時(shí)間,單位為 second,超過(guò)這個(gè)時(shí)間鎖會(huì)自動(dòng)釋放,避免死鎖。
  • delete - delete key:刪除 key

實(shí)現(xiàn)

單點(diǎn)實(shí)現(xiàn)步驟:

  1. 獲取鎖的使用,使用 setnx 加鎖,鎖的 value 值為一個(gè)隨機(jī)生成的 UUID,再使用 expire 設(shè)置一個(gè)過(guò)期值。
  2. 獲取鎖的時(shí)候還設(shè)置一個(gè)獲取的超時(shí)時(shí)間,若超過(guò)這個(gè)時(shí)間則放棄獲取鎖。
  3. 釋放鎖的時(shí)候,通過(guò) UUID 判斷是不是該鎖,若是該鎖,則執(zhí)行 delete 進(jìn)行鎖釋放。

問(wèn)題
  • 單點(diǎn)問(wèn)題。如果單機(jī) redis 掛掉了,那么程序會(huì)跟著出錯(cuò)。
  • 如果轉(zhuǎn)移使用 slave 節(jié)點(diǎn),復(fù)制不是同步復(fù)制,會(huì)出現(xiàn)多個(gè)程序獲取鎖的情況

小結(jié)

可以考慮使用 redisson 的解決方案。

2.3. 基于 ZooKeeper 實(shí)現(xiàn)分布式鎖

實(shí)現(xiàn)

這也是 ZooKeeper 客戶(hù)端 curator 的分布式鎖實(shí)現(xiàn)。

  1. 創(chuàng)建一個(gè)目錄 mylock;
  2. 線(xiàn)程 A 想獲取鎖就在 mylock 目錄下創(chuàng)建臨時(shí)順序節(jié)點(diǎn);
  3. 獲取 mylock 目錄下所有的子節(jié)點(diǎn),然后獲取比自己小的兄弟節(jié)點(diǎn),如果不存在,則說(shuō)明當(dāng)前線(xiàn)程順序號(hào)最小,獲得鎖;
  4. 線(xiàn)程 B 獲取所有節(jié)點(diǎn),判斷自己不是最小節(jié)點(diǎn),設(shè)置監(jiān)聽(tīng)比自己次小的節(jié)點(diǎn);
  5. 線(xiàn)程 A 處理完,刪除自己的節(jié)點(diǎn),線(xiàn)程 B 監(jiān)聽(tīng)到變更事件,判斷自己是不是最小的節(jié)點(diǎn),如果是則獲得鎖。

小結(jié)

ZooKeeper 版本的分布式鎖問(wèn)題相對(duì)比較來(lái)說(shuō)少。

  • 鎖的占用時(shí)間限制:redis 就有占用時(shí)間限制,而 ZooKeeper 則沒(méi)有,最主要的原因是 redis 目前沒(méi)有辦法知道已經(jīng)獲取鎖的客戶(hù)端的狀態(tài),是已經(jīng)掛了呢還是正在執(zhí)行耗時(shí)較長(zhǎng)的業(yè)務(wù)邏輯。而 ZooKeeper 通過(guò)臨時(shí)節(jié)點(diǎn)就能清晰知道,如果臨時(shí)節(jié)點(diǎn)存在說(shuō)明還在執(zhí)行業(yè)務(wù)邏輯,如果臨時(shí)節(jié)點(diǎn)不存在說(shuō)明已經(jīng)執(zhí)行完畢釋放鎖或者是掛了。由此看來(lái) redis 如果能像 ZooKeeper 一樣添加一些與客戶(hù)端綁定的臨時(shí)鍵,也是一大好事。
  • 是否單點(diǎn)故障:redis 本身有很多中玩法,如客戶(hù)端一致性 hash,服務(wù)器端 sentinel 方案或者 cluster 方案,很難做到一種分布式鎖方式能應(yīng)對(duì)所有這些方案。而 ZooKeeper 只有一種玩法,多臺(tái)機(jī)器的節(jié)點(diǎn)數(shù)據(jù)是一致的,沒(méi)有 redis 的那么多的麻煩因素要考慮。

總體上來(lái)說(shuō) ZooKeeper 實(shí)現(xiàn)分布式鎖更加的簡(jiǎn)單,可靠性更高。但 ZooKeeper 因?yàn)樾枰l繁的創(chuàng)建和刪除節(jié)點(diǎn),性能上不如 Redis 方式。

3. 分布式 Session

在分布式場(chǎng)景下,一個(gè)用戶(hù)的 Session 如果只存儲(chǔ)在一個(gè)服務(wù)器上,那么當(dāng)負(fù)載均衡器把用戶(hù)的下一個(gè)請(qǐng)求轉(zhuǎn)發(fā)到另一個(gè)服務(wù)器上,該服務(wù)器沒(méi)有用戶(hù)的 Session,就可能導(dǎo)致用戶(hù)需要重新進(jìn)行登錄等操作。

分布式 Session 的幾種實(shí)現(xiàn)策略:

  1. 粘性 session
  2. 應(yīng)用服務(wù)器間的 session 復(fù)制共享
  3. 基于 cache DB 緩存的 session 共享

3.1. Sticky Sessions

需要配置負(fù)載均衡器,使得一個(gè)用戶(hù)的所有請(qǐng)求都路由到一個(gè)服務(wù)器節(jié)點(diǎn)上,這樣就可以把用戶(hù)的 Session 存放在該服務(wù)器節(jié)點(diǎn)中。

缺點(diǎn):當(dāng)服務(wù)器節(jié)點(diǎn)宕機(jī)時(shí),將丟失該服務(wù)器節(jié)點(diǎn)上的所有 Session。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

3.2. Session Replication

在服務(wù)器節(jié)點(diǎn)之間進(jìn)行 Session 同步操作,這樣的話(huà)用戶(hù)可以訪問(wèn)任何一個(gè)服務(wù)器節(jié)點(diǎn)。

缺點(diǎn):占用過(guò)多內(nèi)存;同步過(guò)程占用網(wǎng)絡(luò)帶寬以及服務(wù)器處理器時(shí)間。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

3.3. Session Server

使用一個(gè)單獨(dú)的服務(wù)器存儲(chǔ) Session 數(shù)據(jù),可以存在 MySQL 數(shù)據(jù)庫(kù)上,也可以存在 Redis 或者 Memcached 這種內(nèi)存型數(shù)據(jù)庫(kù)。

缺點(diǎn):需要去實(shí)現(xiàn)存取 Session 的代碼。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

4. 分布式存儲(chǔ)

通常有兩種解決方案:

  1. 數(shù)據(jù)分布:就是把數(shù)據(jù)分塊存在不同的服務(wù)器上(分庫(kù)分表)。
  2. 數(shù)據(jù)復(fù)制:讓所有的服務(wù)器都有相同的數(shù)據(jù),提供相當(dāng)?shù)姆?wù)。

5. 分布式緩存

使用緩存的好處:

  • 提升數(shù)據(jù)讀取速度
  • 提升系統(tǒng)擴(kuò)展能力,通過(guò)擴(kuò)展緩存,提升系統(tǒng)承載能力
  • 降低存儲(chǔ)成本,Cache+DB 的方式可以承擔(dān)原有需要多臺(tái) DB 才能承擔(dān)的請(qǐng)求量,節(jié)省機(jī)器成本

根據(jù)業(yè)務(wù)場(chǎng)景,通常緩存有以下幾種使用方式

  • 懶漢式(讀時(shí)觸發(fā)):寫(xiě)入 DB 后, 然后把相關(guān)的數(shù)據(jù)也寫(xiě)入 Cache
  • 饑餓式(寫(xiě)時(shí)觸發(fā)):先查詢(xún) DB 里的數(shù)據(jù), 然后把相關(guān)的數(shù)據(jù)寫(xiě)入 Cache
  • 定期刷新:適合周期性的跑數(shù)據(jù)的任務(wù),或者列表型的數(shù)據(jù),而且不要求絕對(duì)實(shí)時(shí)性

緩存分類(lèi):

  • 應(yīng)用內(nèi)緩存:如:EHCache
  • 分布式緩存:如:Memached、Redis

6. 分布式計(jì)算

7. 負(fù)載均衡

7.1. 算法

輪詢(xún)(Round Robin)

輪詢(xún)算法把每個(gè)請(qǐng)求輪流發(fā)送到每個(gè)服務(wù)器上。下圖中,一共有 6 個(gè)客戶(hù)端產(chǎn)生了 6 個(gè)請(qǐng)求,這 6 個(gè)請(qǐng)求按 (1, 2, 3, 4, 5, 6) 的順序發(fā)送。最后,(1, 3, 5) 的請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,(2, 4, 6) 的請(qǐng)求會(huì)被發(fā)送到服務(wù)器 2。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

該算法比較適合每個(gè)服務(wù)器的性能差不多的場(chǎng)景,如果有性能存在差異的情況下,那么性能較差的服務(wù)器可能無(wú)法承擔(dān)過(guò)大的負(fù)載(下圖的 Server 2)。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

加權(quán)輪詢(xún)(Weighted Round Robbin)

加權(quán)輪詢(xún)是在輪詢(xún)的基礎(chǔ)上,根據(jù)服務(wù)器的性能差異,為服務(wù)器賦予一定的權(quán)值。例如下圖中,服務(wù)器 1 被賦予的權(quán)值為 5,服務(wù)器 2 被賦予的權(quán)值為 1,那么 (1, 2, 3, 4, 5) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,(6) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 2。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

最少連接(least Connections)

由于每個(gè)請(qǐng)求的連接時(shí)間不一樣,使用輪詢(xún)或者加權(quán)輪詢(xún)算法的話(huà),可能會(huì)讓一臺(tái)服務(wù)器當(dāng)前連接數(shù)過(guò)大,而另一臺(tái)服務(wù)器的連接過(guò)小,造成負(fù)載不均衡。例如下圖中,(1, 3, 5) 請(qǐng)求會(huì)被發(fā)送到服務(wù)器 1,但是 (1, 3) 很快就斷開(kāi)連接,此時(shí)只有 (5) 請(qǐng)求連接服務(wù)器 1;(2, 4, 6) 請(qǐng)求被發(fā)送到服務(wù)器 2,只有 (2) 的連接斷開(kāi)。該系統(tǒng)繼續(xù)運(yùn)行時(shí),服務(wù)器 2 會(huì)承擔(dān)過(guò)大的負(fù)載。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

最少連接算法就是將請(qǐng)求發(fā)送給當(dāng)前最少連接數(shù)的服務(wù)器上。例如下圖中,服務(wù)器 1 當(dāng)前連接數(shù)最小,那么新到來(lái)的請(qǐng)求 6 就會(huì)被發(fā)送到服務(wù)器 1 上。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

加權(quán)最少連接(Weighted Least Connection)

在最少連接的基礎(chǔ)上,根據(jù)服務(wù)器的性能為每臺(tái)服務(wù)器分配權(quán)重,再根據(jù)權(quán)重計(jì)算出每臺(tái)服務(wù)器能處理的連接數(shù)。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

隨機(jī)算法(Random)

把請(qǐng)求隨機(jī)發(fā)送到服務(wù)器上。和輪詢(xún)算法類(lèi)似,該算法比較適合服務(wù)器性能差不多的場(chǎng)景。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

源地址哈希法 (IP Hash)

源地址哈希通過(guò)對(duì)客戶(hù)端 IP 哈希計(jì)算得到的一個(gè)數(shù)值,用該數(shù)值對(duì)服務(wù)器數(shù)量進(jìn)行取模運(yùn)算,取模結(jié)果便是目標(biāo)服務(wù)器的序號(hào)。

  • 優(yōu)點(diǎn):保證同一 IP 的客戶(hù)端都會(huì)被 hash 到同一臺(tái)服務(wù)器上。
  • 缺點(diǎn):不利于集群擴(kuò)展,后臺(tái)服務(wù)器數(shù)量變更都會(huì)影響 hash 結(jié)果??梢圆捎靡恢滦?Hash 改進(jìn)。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

7.2. 實(shí)現(xiàn)

HTTP 重定向

HTTP 重定向負(fù)載均衡服務(wù)器收到 HTTP 請(qǐng)求之后會(huì)返回服務(wù)器的地址,并將該地址寫(xiě)入 HTTP 重定向響應(yīng)中返回給瀏覽器,瀏覽器收到后需要再次發(fā)送請(qǐng)求。

缺點(diǎn):

  • 用戶(hù)訪問(wèn)的延遲會(huì)增加;
  • 如果負(fù)載均衡器宕機(jī),就無(wú)法訪問(wèn)該站點(diǎn)。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

DNS 重定向

使用 DNS 作為負(fù)載均衡器,根據(jù)負(fù)載情況返回不同服務(wù)器的 IP 地址。大型網(wǎng)站基本使用了這種方式做為第一級(jí)負(fù)載均衡手段,然后在內(nèi)部使用其它方式做第二級(jí)負(fù)載均衡。

缺點(diǎn):

  • DNS 查找表可能會(huì)被客戶(hù)端緩存起來(lái),那么之后的所有請(qǐng)求都會(huì)被重定向到同一個(gè)服務(wù)器。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

修改 MAC 地址

使用 LVS(Linux Virtual Server)這種鏈路層負(fù)載均衡器,根據(jù)負(fù)載情況修改請(qǐng)求的 MAC 地址。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

修改 IP 地址

在網(wǎng)絡(luò)層修改請(qǐng)求的目的 IP 地址。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)

代理自動(dòng)配置

正向代理與反向代理的區(qū)別:

  • 正向代理:發(fā)生在客戶(hù)端,是由用戶(hù)主動(dòng)發(fā)起的。比如外網(wǎng),客戶(hù)端通過(guò)主動(dòng)訪問(wèn)代理服務(wù)器,讓代理服務(wù)器獲得需要的外網(wǎng)數(shù)據(jù),然后轉(zhuǎn)發(fā)回客戶(hù)端。
  • 反向代理:發(fā)生在服務(wù)器端,用戶(hù)不知道代理的存在。

PAC 服務(wù)器是用來(lái)判斷一個(gè)請(qǐng)求是否要經(jīng)過(guò)代理。

T5大牛帶你解析:如何實(shí)現(xiàn)分布式技術(shù)


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI