分布式和微服務(wù)的區(qū)別
從設(shè)計(jì)理念上來(lái)看
- 分布式用于分散壓力
- 微服務(wù)用于分散能力
從部署角度上來(lái)看
分布式
主要用于分散壓力,所以分布式的服務(wù)都是部署在不同的服務(wù)器上的,再將服務(wù)做集群

分布式水平拆分
根據(jù)“分層”的思想進(jìn)行拆分。
例如,可以將一個(gè)項(xiàng)目根據(jù)“三層架構(gòu)”拆分
- 表示層(jsp+servlet)
- 業(yè)務(wù)邏輯層(service)
- 數(shù)據(jù)訪問(wèn)層(dao)
然后再分開(kāi)部署:
- 把表示層部署在服務(wù)器A上
- 把service和dao層部署在服務(wù)器B上(當(dāng)然,業(yè)務(wù)邏輯層和數(shù)據(jù)訪問(wèn)層也可以分開(kāi)部署在不同服務(wù)器上)
- 然后服務(wù)器A和服務(wù)器B之間通過(guò)dubbo等RPC進(jìn)行進(jìn)行整合,如圖所示。

分布式垂直拆分
根據(jù)業(yè)務(wù)進(jìn)行拆分。
例如,可以根據(jù)業(yè)務(wù)邏輯,將“電商項(xiàng)目”拆分成“訂單項(xiàng)目”、“用戶項(xiàng)目”和“秒殺項(xiàng)目”。顯然這三個(gè)拆分后的項(xiàng)目,仍然可以作為獨(dú)立的項(xiàng)目使用。像這種拆分的方法,就成為垂直拆分

微服務(wù)
主要用于分散能力,主要是將服務(wù)的顆粒度盡量細(xì)化,且自成一脈,壓力這塊并不是其關(guān)注的點(diǎn),所以多個(gè)微服務(wù)是可以部署在同一臺(tái)服務(wù)器上的

微服務(wù)可以理解為一種非常細(xì)粒度的垂直拆分。例如,以上“訂單項(xiàng)目”本來(lái)就是垂直拆分后的子項(xiàng)目,但實(shí)際上“訂單項(xiàng)目”還能進(jìn)一步拆分為“購(gòu)物項(xiàng)目”、“結(jié)算項(xiàng)目”和“售后項(xiàng)目”,如圖

現(xiàn)在看圖中的“訂單項(xiàng)目”,它完全可以作為一個(gè)分布式項(xiàng)目的組成元素,但就不適合作為微服務(wù)的組成元素了(因?yàn)樗€能再拆,而微服務(wù)應(yīng)該是不能再拆的“微小”服務(wù),類似于“原子性”)
從服務(wù)完整性來(lái)看
分布式服務(wù)需要提供給別的分布式服務(wù)去調(diào)用,單獨(dú)拆出來(lái)未必外部可用
微服務(wù)自成一脈,可以系統(tǒng)內(nèi)部調(diào)用,也可以單獨(dú)提供服務(wù)
分布式鎖
WHY
為什么需要用分布式鎖,見(jiàn)下圖

變量A存在三個(gè)服務(wù)器內(nèi)存中(這個(gè)變量A主要體現(xiàn)是在一個(gè)類中的一個(gè)成員變量,是一個(gè)有狀態(tài)的對(duì)象),如果不加任何控制的話,變量A同時(shí)都會(huì)在分配一塊內(nèi)存,三個(gè)請(qǐng)求發(fā)過(guò)來(lái)同時(shí)對(duì)這個(gè)變量操作,顯然結(jié)果是不對(duì)的!即使不是同時(shí)發(fā)過(guò)來(lái),三個(gè)請(qǐng)求分別操作三個(gè)不同內(nèi)存區(qū)域的數(shù)據(jù),變量A之間不存在共享,也不具有可見(jiàn)性,處理的結(jié)果也是不對(duì)的。
分布式鎖應(yīng)該具備哪些條件:
1、在分布式系統(tǒng)環(huán)境下,一個(gè)方法在同一時(shí)間只能被一個(gè)機(jī)器的一個(gè)線程執(zhí)行;
2、高可用的獲取鎖與釋放鎖;
3、高性能的獲取鎖與釋放鎖;
4、具備可重入特性;
5、具備鎖失效機(jī)制,防止死鎖;
6、具備非阻塞鎖特性,即沒(méi)有獲取到鎖將直接返回獲取鎖失敗
HOW
利用redis實(shí)現(xiàn)分布式鎖
WHY
Redis性能高
命令簡(jiǎn)單,實(shí)現(xiàn)方便
HOW
獲取鎖
使用setnx加鎖,key為鎖名,value隨意不重復(fù)就行(一般用uuid)
給鎖添加expire時(shí)間,超過(guò)該時(shí)間redis過(guò)期(即自動(dòng)釋放鎖)
設(shè)置獲取鎖的超時(shí)時(shí)間,若超過(guò)時(shí)間,則放棄獲取鎖
釋放鎖
通過(guò)鎖名獲取鎖值
比較鎖值和當(dāng)前uuid是否一致,一致則釋放鎖(通過(guò)delete命令刪除redis鍵值對(duì))
EXAMPLE
#連接redis
import time
import uuid
from threading import Thread
import redis
redis_client = redis.Redis(host="localhost",
port=6379,
# password=123,
db=10)
#獲取一個(gè)鎖
# lock_name:鎖定名稱
# acquire_time: 客戶端等待獲取鎖的時(shí)間
# time_out: 鎖的超時(shí)時(shí)間
def acquire_lock(lock_name, acquire_time=10, time_out=10):
"""獲取一個(gè)分布式鎖"""
identifier = str(uuid.uuid4())
end = time.time() + acquire_time
lock = "string:lock:" + lock_name
while time.time() < end:
if redis_client.setnx(lock, identifier):
# 給鎖設(shè)置超時(shí)時(shí)間, 防止進(jìn)程崩潰導(dǎo)致其他進(jìn)程無(wú)法獲取鎖
redis_client.expire(lock, time_out)
return identifier
elif not redis_client.ttl(lock):
redis_client.expire(lock, time_out)
time.sleep(0.001)
return False
#釋放一個(gè)鎖
def release_lock(lock_name, identifier):
"""通用的鎖釋放函數(shù)"""
lock = "string:lock:" + lock_name
pip = redis_client.pipeline(True)
while True:
try:
pip.watch(lock)
lock_value = redis_client.get(lock)
if not lock_value:
return True
if lock_value.decode() == identifier:
pip.multi()
pip.delete(lock)
pip.execute()
return True
pip.unwatch()
break
except redis.excetions.WacthcError:
pass
return False
分布式事務(wù)
2PC方案
2PC:two phase commit protocol,二階段提交協(xié)議,是一種強(qiáng)一致性設(shè)計(jì)。
同步阻塞(導(dǎo)致長(zhǎng)久的資源鎖定),只有第一階段全部正常完成(返回失敗,回字返回超時(shí)都會(huì)返回“準(zhǔn)備失敗”),才會(huì)進(jìn)入第二階段


流程
- 準(zhǔn)備。除了commit之外的所有命令都已完成
- 協(xié)調(diào)者接收所有準(zhǔn)備的響應(yīng)
- 所有資源響應(yīng)后,進(jìn)入第二階段,提交/回滾階段(只要準(zhǔn)備階段有一個(gè)是準(zhǔn)備失敗,那么所有節(jié)點(diǎn)的事務(wù)全部回滾)
- 若第二階段失敗,不管是回滾還是提交,就不斷重試,直到所有都回滾成功或者提交成功
協(xié)調(diào)者設(shè)計(jì)
因?yàn)閰f(xié)調(diào)者可能會(huì)在任意一個(gè)時(shí)間點(diǎn)(發(fā)送準(zhǔn)備命令之前,發(fā)送準(zhǔn)備命令之后,發(fā)送回滾事務(wù)命令之前,發(fā)送回滾事務(wù)命令之后,發(fā)送提交事務(wù)命令之前,發(fā)送提交事務(wù)命令之后)故障,導(dǎo)致資源阻塞。
3PC方案

流程
- 準(zhǔn)備階段,只是詢問(wèn)參與者是否有條件接收事務(wù),并沒(méi)有像2PC那樣,上來(lái)就直接執(zhí)行了除commit之外的所有工作,就不會(huì)一上來(lái)就直接鎖定資源
- 預(yù)提交階段,就像2PC的準(zhǔn)備階段,除了事務(wù)的提交,該做的都做了
- 提交階段,和2PC的提交階段一樣
TCC解決方案
T:try,指的是預(yù)留,即資源的預(yù)留和鎖定,注意是預(yù)留
C:confirm,指的是確認(rèn)操作,這一步其實(shí)就是真正的執(zhí)行了
C:cancel,指的是撤銷操作,可以理解為把預(yù)留階段的動(dòng)作撤銷了
從思想上看和 2PC 差不多,都是先試探性的執(zhí)行,如果都可以那就真正的執(zhí)行,如果不行就回滾。
劣勢(shì)
- 對(duì)業(yè)務(wù)入侵較大,與業(yè)務(wù)緊耦合(需要根據(jù)具體業(yè)務(wù),對(duì)每一個(gè)動(dòng)作都要寫一份tcc)
- 提交和撤銷可能需要重試,所以要保證冪等性

最終消息一致性方案
WHERE
適用于對(duì)實(shí)時(shí)性要求沒(méi)那么高的業(yè)務(wù)場(chǎng)景,如:短信通知
HOW
- A服務(wù)器數(shù)據(jù)表操作 + 消息表操作
- A的數(shù)據(jù)和消息表內(nèi)容發(fā)生消息到MQ(如果失敗,嘗試重新發(fā)送,屬于消息隊(duì)列的機(jī)制)
- B服務(wù)器接收MQ消息,執(zhí)行自己的邏輯。如果B的本地事務(wù)成功,不回傳消息;如果B的本地事務(wù)失敗,發(fā)消息給MQ,A接收MQ消息,執(zhí)行回滾操作。(如果失敗,嘗試重新發(fā)送,屬于消息隊(duì)列的機(jī)制)
- A服務(wù)器和B服務(wù)器定時(shí)掃描本地消息表,把沒(méi)處理完的消息或者失敗的消息再發(fā)送一遍。(防止網(wǎng)絡(luò)異常導(dǎo)致回滾失敗)