前言
冪等性(Idempotence)
是一個(gè)在計(jì)算機(jī)科學(xué)中使用的術(shù)語(yǔ)。當(dāng)某個(gè)操作無(wú)論進(jìn)行一次或多次都產(chǎn)生相同的結(jié)果,我們就說(shuō)這個(gè)操作是冪等的。
例如,刪除文件的操作就是冪等的,因?yàn)闊o(wú)論你嘗試刪除一次還是兩次,結(jié)果都是文件被刪除。相對(duì)地,計(jì)數(shù)器增加操作就不是冪等的,因?yàn)槊看尾僮?,?jì)數(shù)器的值都會(huì)改變。
在分布式系統(tǒng)和網(wǎng)絡(luò)協(xié)議中,冪等性是非常重要的特性。由于網(wǎng)絡(luò)延遲,服務(wù)端可能會(huì)接收到同一條消息的多個(gè)副本,如果此類(lèi)操作是冪等的,那么就不會(huì)出現(xiàn)任何問(wèn)題。
解決方案:
- 全局唯一ID :為每個(gè)請(qǐng)求分配一個(gè)唯一的任務(wù) ID,并確保服務(wù)器對(duì)每個(gè)任務(wù)只執(zhí)行一次。如果服務(wù)器接收到相同的任務(wù) ID,它將忽略這個(gè)任務(wù)或返回之前任務(wù)的結(jié)果。
- Token 機(jī)制: 根據(jù)業(yè)務(wù)的操作和內(nèi)容生成一個(gè)Token值(全局唯一ID,執(zhí)行之前判斷方法是否執(zhí)行過(guò)。
- 發(fā)送確認(rèn)消息:客戶端在發(fā)送完請(qǐng)求后并不立即進(jìn)行下一步操作,而是等待服務(wù)器的確認(rèn)消息。如果客戶端沒(méi)有收到確認(rèn)消息,那么它會(huì)重新發(fā)送請(qǐng)求。
- 使用分布式鎖或事務(wù):通過(guò)這種方式,可以確保一次只有一個(gè)任務(wù)在執(zhí)行,避免因重復(fù)執(zhí)行同一個(gè)任務(wù)而產(chǎn)生的問(wèn)題。
- 使用樂(lè)觀鎖:樂(lè)觀鎖對(duì)數(shù)據(jù)進(jìn)行更新時(shí),會(huì)檢查數(shù)據(jù)在此期間是否被別人修改過(guò),如果被修改過(guò),則拒絕更新。
- 使用“compare-and-swap”等原子操作:原子操作可以在不使用鎖的情況下完成復(fù)雜的更新操作,這也可以保證冪等性。
冪等可以用來(lái)解決什么問(wèn)題?
冪等性可以用于解決在分布式系統(tǒng)中的重復(fù)請(qǐng)求問(wèn)題。在分布式系統(tǒng)中,由于網(wǎng)絡(luò)等原因,可能會(huì)導(dǎo)致同一個(gè)請(qǐng)求被多次執(zhí)行,從而可能會(huì)造成臟數(shù)據(jù)或資源浪費(fèi)等問(wèn)題。通過(guò)實(shí)現(xiàn)冪等性,可以確保相同的請(qǐng)求只被執(zhí)行一次,從而防止出現(xiàn)重復(fù)請(qǐng)求的情況。這樣可以提高系統(tǒng)的穩(wěn)定性、可靠性和性能。另外,冪等性也可以用于實(shí)現(xiàn)事務(wù)的原子性,確保事務(wù)只被執(zhí)行一次,從而防止出現(xiàn)數(shù)據(jù)不一致的情況。
1.全局唯一ID
全局唯一ID的生成,需要根據(jù)實(shí)際的業(yè)務(wù)需要進(jìn)行生成,單機(jī)的部署一般使用UUID或者數(shù)據(jù)庫(kù)自增ID就ok,分布式下現(xiàn)在比較流行的雪花算法生成全局唯一ID。單機(jī)也需要考慮后期業(yè)務(wù)的擴(kuò)展進(jìn)行使用全局唯一ID,根據(jù)阿里規(guī)范:
單表行數(shù)超過(guò)500萬(wàn)行或者單表容量超過(guò)2GB,才推薦進(jìn)行分庫(kù)分表
1.1 前端防止重復(fù)提交
一般開(kāi)發(fā)中,前端也會(huì)做一些簡(jiǎn)單防止重復(fù)提交操作,一般的操作就是提交請(qǐng)求后禁用操作。此處就不在贅述,比較簡(jiǎn)單,此做法不能解決問(wèn)題,只能簡(jiǎn)單解決人為的操作。
解決不了由于網(wǎng)絡(luò)波動(dòng),網(wǎng)絡(luò)重試機(jī)制導(dǎo)致數(shù)據(jù)不一致問(wèn)題。
1.2 token機(jī)制
Token的機(jī)制通過(guò)進(jìn)行業(yè)務(wù)操作之前進(jìn)行根據(jù)業(yè)務(wù)生成一個(gè)token,token可以使用雪花算法進(jìn)行生成,進(jìn)行業(yè)務(wù)操作之前先獲取token,獲取成功請(qǐng)求業(yè)務(wù)攜帶token,請(qǐng)求,后臺(tái)會(huì)根據(jù)邏輯進(jìn)行有效操作的判斷。后端判斷該 token 是否存在,如存在,則為第一次提交,放行并刪除token,如無(wú)token,第二次提交,阻攔該請(qǐng)求。
高并發(fā)環(huán)境下還需要考慮操作的原子性。
此處使用redis來(lái)存生成的token。
安裝和具體的操作參考:
centos7下安裝redis以及本地連接注意事項(xiàng)
Springboot整合redis
簡(jiǎn)單token服務(wù)代碼
/**
* token服務(wù)
*/
@Service
public class TokenService {
@Autowired
RedisService redisService;
/**
* 獲取token
* @param bizType
* @return
*/
public String getToken(String bizType){
///業(yè)務(wù)的key
String key = bizType+":"+UUID.randomUUID().toString();
redisService.saveKeyValue(key,1,10);
return key;
}
/**
* 校驗(yàn)token
* @param token
* @return
*/
public boolean verifyToken(String token){
///token
String value = redisService.getValueByKey(token);
//判斷值是否存在
if(!StringUtils.isEmpty(value)){
//存在刪除
redisService.delete(token);
return true;
}
return false;
}
controller:
@RestController
public class TokenController {
@Autowired
TokenService tokenService;
@GetMapping("/getToken/{bizType}")
public String getToken(@PathVariable("bizType") String bizType){
return tokenService.getToken(bizType);
}
}
1.3 數(shù)據(jù)庫(kù)表加唯一約束
比如用戶表加上身份證號(hào)作為唯一的約束,也可以防止重復(fù)提交用戶注冊(cè)。對(duì)于一些業(yè)務(wù)唯一不明確的也能導(dǎo)致不冪等性。
2.冪等下 ABA問(wèn)題 與樂(lè)觀鎖
ABA問(wèn)題
:是在并發(fā)環(huán)境中經(jīng)常會(huì)遇到的問(wèn)題,最典型的例子就是CAS(Compare And Swap)操作。CAS操作是一種樂(lè)觀加鎖機(jī)制,它執(zhí)行時(shí)首先把某個(gè)內(nèi)存值A(chǔ)讀入到未提交緩冤區(qū),在此期間可能其他線程也修改了這個(gè)內(nèi)存場(chǎng)所的值變成了B,若這個(gè)時(shí)候重來(lái)執(zhí)行CAS操作,盡管它能檢測(cè)到這個(gè)內(nèi)存值已經(jīng)被修改過(guò),但修改過(guò)后的這個(gè)值B又被其他線程改變成了A,這樣CAS操作就可能錯(cuò)誤地成功。這就是ABA問(wèn)題。
2.1 樂(lè)觀鎖
樂(lè)觀鎖一種并發(fā)控制技術(shù)。在寫(xiě)入數(shù)據(jù)時(shí),并不加鎖,而是假定沒(méi)有其他線程與其同時(shí)修改數(shù)據(jù)。當(dāng)操作系統(tǒng)執(zhí)行寫(xiě)入操作時(shí),它會(huì)檢查沒(méi)有線程已經(jīng)修改從開(kāi)頭到現(xiàn)在的數(shù)據(jù)。如果檢測(cè)到其他線程已經(jīng)修改了該數(shù)據(jù),那么操作系統(tǒng)就會(huì)拒絕寫(xiě)入,并通知失敗。需要重試直到?jīng)]有其他線程修改需要寫(xiě)入的數(shù)據(jù)。
樂(lè)觀鎖主要解決了悲觀鎖長(zhǎng)期占據(jù)鎖而導(dǎo)致其他線程長(zhǎng)時(shí)間等待鎖的問(wèn)題。
2.2 如何解決ABA問(wèn)題?
- 使用版本號(hào):在每個(gè)變量后面追加上版本號(hào),每次修改該變量都對(duì)版本號(hào)加一,這就能解決ABA問(wèn)題了。
- 使用原子類(lèi):Java從1.5開(kāi)始提供了一個(gè)原子包(java.util.concurrent.atomic),在這個(gè)包中提供了一些原子類(lèi),其中ABA問(wèn)題可以使用類(lèi)AtomicStampedReference進(jìn)行解決,它通過(guò)控制變量的版本解決了ABA問(wèn)題。
3.分布式鎖和事務(wù)
3.1 分布式鎖:
其主要目的是在系統(tǒng)內(nèi)提供一種機(jī)制,來(lái)確保在任何時(shí)刻只有一個(gè)節(jié)點(diǎn)可以執(zhí)行某個(gè)程序區(qū)塊,所以分布式鎖主要目的是為了防止并發(fā)操作引發(fā)的數(shù)據(jù)不一致問(wèn)題。
分布式鎖通過(guò)一種協(xié)議來(lái)決定哪一個(gè)請(qǐng)求可以獲得鎖,以執(zhí)行相應(yīng)的操作。例如,一個(gè)典型的應(yīng)用程序可能需要對(duì)數(shù)據(jù)庫(kù)中的一個(gè)數(shù)據(jù)進(jìn)行更新,程序在進(jìn)行更新操作的時(shí)候會(huì)先嘗試獲取分布式鎖,如果獲取成功則進(jìn)行操作,如果獲取失敗則等待或者拋出錯(cuò)誤。
3.2. 分布式事務(wù)
事務(wù)的核心是:ACID原則(原子性、一致性、隔離性、持久性)。
在一個(gè)分布式系統(tǒng)中,為了保證跨多個(gè)節(jié)點(diǎn)的數(shù)據(jù)一致性和操作可靠性,通常需要引入一種機(jī)制——分布式事務(wù)。該機(jī)制可以確保分布式系統(tǒng)中多個(gè)操作要么全都成功執(zhí)行,要么全都不執(zhí)行(如果某處失敗,其他所有成功的操作也需要回滾)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-600653.html
分布式鎖主要用于保證在多節(jié)點(diǎn)環(huán)境下,某一時(shí)刻只有一個(gè)節(jié)點(diǎn)能夠操作某資源,用于解決并發(fā)問(wèn)題;而分布式事務(wù),主要用于確保在多節(jié)點(diǎn)環(huán)境下,一個(gè)業(yè)務(wù)流程中包含的多個(gè)操作要么全部成功,要么全部失敗,從而保證數(shù)據(jù)的一致性。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-600653.html
到了這里,關(guān)于冪等性設(shè)計(jì)與實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!