面對分布式架構(gòu)和微服務(wù)復(fù)雜的系統(tǒng)架構(gòu)和網(wǎng)絡(luò)超時服務(wù)器異常等帶來的系統(tǒng)穩(wěn)定性問題,分布式接口的冪等性設(shè)計顯得尤為重要。本文簡要介紹了幾種分布式接口冪等性設(shè)計實現(xiàn),包括Token去重機制、樂觀鎖機制、數(shù)據(jù)庫主鍵和狀態(tài)機實現(xiàn)等,以加深理解。
1、分布式接口冪等性相關(guān)概念
1.1 什么是冪等性
冪等性來源自數(shù)學(xué)領(lǐng)域,數(shù)學(xué)上的冪等性是指對于某一元運算為冪等的操作,在任意元素上多次執(zhí)行的結(jié)果是相同的。例如,函數(shù)f(x) = f(x)對于任意的x,在x上的第一次和第二次執(zhí)行可以得到相同的結(jié)果。
在HTTP/1.1規(guī)范中冪等性的定義如下:
Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
一次和多次請求某一個資源對于資源本身應(yīng)該具有同樣的結(jié)果(網(wǎng)絡(luò)超時等問題除外)。也就是說,其任意多次執(zhí)行對資源本身所產(chǎn)生的影響均與一次執(zhí)行的影響相同。
在HTTP協(xié)議中,HTTP GET是一個清晰的冪等操作,HTTP DELETE/POST是非冪等的,HTTP PUT也是冪等的,因為對同一個URI進行多次PUT的side-effetcs是一致的。
在分布式架構(gòu)或者微服務(wù)架構(gòu)中,由于分布式自身的時序問題以及系統(tǒng)網(wǎng)絡(luò)的穩(wěn)定性,接口具有成功、失敗和無響應(yīng)的三種狀態(tài),為了提供系統(tǒng)的可用性,重復(fù)提交是不可避免的,而重試就會引發(fā)冪等性的問題。
1.2 冪等性的使用場景
分布式接口的冪等性實際上就是接口可重復(fù)調(diào)用,在調(diào)用方多次調(diào)用的情況下,接口最終得到的結(jié)果是一致的。冪等性適用于以下場景:
- 前端重復(fù)提交:在訂單系統(tǒng)中用戶在前端提交訂單,快速重復(fù)點擊多次,造成后端生成多個內(nèi)容重復(fù)的訂單,但是后臺應(yīng)該只產(chǎn)生一個訂單。
- 接口超時重試:對于給第三方調(diào)用的接口,為了防止網(wǎng)絡(luò)抖動或其他原因造成請求丟失,這樣的接口一般都會設(shè)計成超時重試多次。防止外部多次調(diào)用對系統(tǒng)數(shù)據(jù)狀態(tài)的發(fā)生多次改變,將服務(wù)接口設(shè)計成冪等,就是為了防止多次重試造成系統(tǒng)不一致的問題。比如賬戶扣款操作超時重試了多次,理應(yīng)只扣款一次。
- 消息重復(fù)消費:MQ消息中間件,消息重復(fù)消費,相同請求條件下這次消費的結(jié)果與下一次應(yīng)該保持一致。
1.3 分布式接口冪等性的實現(xiàn)方案
接口冪等性的解決方案可以在客戶端和服務(wù)端實現(xiàn),但是客戶端控制效果不佳,比如按鈕置灰、不可點擊等,由于涉及到多設(shè)備兼容性以及接口調(diào)用的問題,并不能真正實現(xiàn)冪等。因此安全的措施還是從后端接口層進行控制,有以下幾種方案:
- Token去重:根據(jù)業(yè)務(wù)的操作和內(nèi)容生成一個Token值(全局唯一ID),在執(zhí)行操作前先根據(jù)這個全局唯一ID進行校驗,來判斷這個操作是否已經(jīng)執(zhí)行。如果存在則表示該方法已經(jīng)執(zhí)行。
- 樂觀鎖機制:適用于更新操作。在查詢和刪除操作中使用樂觀鎖機制,保證一次處理結(jié)果,避免重復(fù)操作。設(shè)計表結(jié)構(gòu)時使用樂觀鎖,通過version來做樂觀鎖,這樣既能保證執(zhí)行效率,又能保證冪等。
- 數(shù)據(jù)庫主鍵:適用于插入時的冪等性。利用數(shù)據(jù)庫中主鍵唯一約束的特性,保證一張表中只能存在一條帶該唯一主鍵的記錄。
- 狀態(tài)機冪等:根據(jù)業(yè)務(wù)表的狀態(tài)特性設(shè)計,只支持狀態(tài)的單向改變,在執(zhí)行的時候加上狀態(tài)信息,實現(xiàn)冪等。
冪等性設(shè)計簡化了客戶端的處理邏輯,卻增加了服務(wù)端邏輯處理和設(shè)計上的復(fù)雜性,增加額外控制冪等的業(yè)務(wù)邏輯的同時,將并行執(zhí)行改為串行降低了執(zhí)行效率。
2、幾種接口冪等性方案介紹
2.1 Token去重
Token機制是通過在服務(wù)端生成一個唯一的Token,并將其存儲在客戶端中,來保證多個客戶端之間對同一個服務(wù)的請求結(jié)果的一致性。Token機制的實現(xiàn)原理如下:
- 服務(wù)端生成Token:服務(wù)端需要生成一個唯一的Token,可以使用時間戳、隨機數(shù)等信息來生成。生成Token后,將其存儲在服務(wù)端的數(shù)據(jù)庫中。
- 客戶端獲取Token:客戶端在每次請求服務(wù)時,需要向服務(wù)端發(fā)送一個請求Token。請求Token是服務(wù)端根據(jù)Token生成的唯一標(biāo)識,客戶端通過該Token來識別自己的身份,并在服務(wù)端的數(shù)據(jù)庫中查找對應(yīng)的Token。
- 如果找到了對應(yīng)的Token,則說明該請求是第一次請求,服務(wù)端將其存儲在數(shù)據(jù)庫中,并返回一個唯一的標(biāo)識符;如果在數(shù)據(jù)庫中找不到該Token,則說明該請求是重復(fù)請求,服務(wù)端不返回任何結(jié)果,并提示用戶重新操作。
- 如果在數(shù)據(jù)庫中也找不到該Token,則說明該請求是冪等請求,服務(wù)端可以直接返回結(jié)果,不做任何操作。
Token機制的優(yōu)點是實現(xiàn)簡單、易于部署和維護,能夠保證分布式系統(tǒng)的冪等性。但是,它也存在一些局限性,例如需要在服務(wù)端和客戶端之間傳遞Token,可能會導(dǎo)致性能問題;另外,如果Token被濫用,也可能會帶來安全問題。因此,在使用Token機制時,需要根據(jù)具體情況進行權(quán)衡和選擇。
2.2 樂觀鎖機制
數(shù)據(jù)庫樂觀鎖方案一般適用于更新操作的冪等性,實現(xiàn)邏輯是在對應(yīng)的數(shù)據(jù)表中添加一個version字段,作為當(dāng)前數(shù)據(jù)的的版本標(biāo)識。這樣每次對這條數(shù)據(jù)執(zhí)行更新時,都會將該版本標(biāo)識作為一個條件,值需要為上次待更新數(shù)據(jù)中的版本標(biāo)識的值。
1)先根據(jù)條件查詢數(shù)據(jù),得到對應(yīng)的版本號version
select version from tablename where xxx
2)更新數(shù)據(jù)時帶上版本號version,只有版本號匹配才會更新數(shù)據(jù),如果不匹配則不更新
update tablename set count=count+1, version=version+1 where version=#{version}
3)更新數(shù)據(jù)的時候,同時需要更新數(shù)據(jù)對應(yīng)的版本號version,這樣可以解決ABA問題。
如果一個變量V初次讀取的時候是A值,并且在準(zhǔn)備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因為在這段時間它的值可能被改為其他值,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。這個問題被稱為CAS操作的 "ABA"問題。
樂觀鎖機制實際上是犧牲了并發(fā)性來實現(xiàn)更新操作的冪等性,在并發(fā)場景下會導(dǎo)致大量的鎖沖突等待和性能問題。
2.3 數(shù)據(jù)庫主鍵
數(shù)據(jù)庫唯一主鍵機制是利用主鍵的唯一性約束,適用于插入操作的冪等性,當(dāng)插入主鍵重復(fù)的數(shù)據(jù)時會拋出異常,保證數(shù)據(jù)的一致性。表結(jié)構(gòu)設(shè)計如下所示:
CREATE TABLE `t_check` (
`id` int(11) NOT NULL COMMENT 'ID',
`serial_no` varchar(255) NOT NULL COMMENT '唯一序列號',
`source_type` varchar(255) NOT NULL COMMENT '資源類型',
`status` int(4) DEFAULT NULL COMMENT '狀態(tài)',
PRIMARY KEY (`id`)
UNIQUE KEY `key_s` (`serial_no`,`source_type`) COMMENT '保證業(yè)務(wù)唯一性'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='冪等性校驗表';
唯一主鍵UNIQUE KEY的關(guān)鍵性字段如下:
- serial_no:唯一序列號的值,在分布式架構(gòu)下是全局唯一的ID
- source_type:業(yè)務(wù)類型,區(qū)分不同的業(yè)務(wù),訂單,支付等。
具體處理邏輯如下圖所示:
2.4 狀態(tài)機實現(xiàn)冪等
對于很多業(yè)務(wù)是有業(yè)務(wù)流轉(zhuǎn)狀態(tài)的,如訂單的待提交,待支付,已支付,取消等,在業(yè)務(wù)邏輯處理的時候只支持狀態(tài)的單向改變。業(yè)務(wù)表在設(shè)計的時候增加狀態(tài)字段status,這樣在更新的時候加上“status=期望的status”,多次調(diào)用的話實際也只會執(zhí)行一次。
update xx where id=1 and status=1
3、總結(jié)
分布式架構(gòu)下冪等性是保證接口能夠重復(fù)執(zhí)行的重要機制,冪等性和防重又有所不同,防重是在第一次請求已經(jīng)成功的情況下人為多次重復(fù)操作導(dǎo)致的狀態(tài)改變,冪等性是在不確定第一次請求結(jié)果的情況下,發(fā)起多次請求不會出現(xiàn)狀態(tài)的變化。實際使用中,通過數(shù)據(jù)庫主鍵的唯一性可以實現(xiàn)冪等性和防重,樂觀鎖的version機制能夠?qū)崿F(xiàn)并發(fā)更新下的冪等性,也可以通過數(shù)據(jù)庫悲觀鎖機制在業(yè)務(wù)操作前獲取鎖資源實現(xiàn)唯一性操作??偠灾?,分布式接口的冪等性是在犧牲一定的并發(fā)和性能的前提下,以實現(xiàn)系統(tǒng)的穩(wěn)定性和容錯性。文章來源:http://www.zghlxwxcb.cn/news/detail-450597.html
參考資料:文章來源地址http://www.zghlxwxcb.cn/news/detail-450597.html
- https://blog.csdn.net/tengxvincent/article/details/81773745
- https://www.cnblogs.com/jajian/p/10926681.html
- https://blog.csdn.net/qq_41863849/article/details/123973348
- https://zhuanlan.zhihu.com/p/70748661
到了這里,關(guān)于分布式接口冪等性設(shè)計實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!