一话浇、概述
- 一次和多次請(qǐng)求某一個(gè)資源應(yīng)該具有相同的副作用。
1. 服務(wù)間調(diào)用狀態(tài)
-
把系統(tǒng)解耦后闹究,服務(wù)間的調(diào)用三個(gè)狀態(tài)
- 成功 success
- 失敗 failed
- 超時(shí) timeout幔崖, 超時(shí)完全不知道是什么狀態(tài)
- 假如超時(shí) 是網(wǎng)絡(luò)傳輸丟包的問題
- 可能是請(qǐng)求時(shí)就沒有請(qǐng)求到
- 可能是請(qǐng)求到了,返回結(jié)果時(shí)沒有正常返回等等情況。
- 假如超時(shí) 是網(wǎng)絡(luò)傳輸丟包的問題
-
因?yàn)橄到y(tǒng)超時(shí)赏寇,而調(diào)用方重試一下吉嫩,會(huì)給我們的系統(tǒng)帶來不一致的副作用。
- 兩種處理方式
- 一種是需要下游系統(tǒng)提供相應(yīng)的查詢接口嗅定。
- 上游系統(tǒng)在timeout 后去查詢一下自娩。
- 查到了,表明做了渠退,成功了就不用做了忙迁, 失敗了就走失敗流程
- 上游系統(tǒng)在timeout 后去查詢一下自娩。
- 一種是通過冪等性的方式。
- 這個(gè)查詢操作交給下游系統(tǒng)碎乃,上游系統(tǒng)只管重試姊扔,下游系統(tǒng)保證一次和多次的請(qǐng)求結(jié)果是一樣的。
- 一種是需要下游系統(tǒng)提供相應(yīng)的查詢接口嗅定。
- 兩種處理方式
二梅誓、冪等性的方式
1. 全局ID
- 要做到冪等性的交易接口恰梢,需要一個(gè)唯一的標(biāo)識(shí),來標(biāo)志交易是同一筆交易梗掰。
- 全局id 參考 全局id 的文章嵌言。
2. 處理流程
1. 流程
- 是需要一個(gè)存儲(chǔ)來記錄收到的交易。
- 當(dāng)收到交易請(qǐng)求的時(shí)候及穗,我們回到這個(gè)存儲(chǔ)中去查詢
- 如果查到了就不再做操作呀页。并把上次做的結(jié)果返回。
- 如果沒有查到拥坛,那么我們就記錄下來蓬蝶。
2 問題
- 上面流程有個(gè)問題,28定律猜惋,80%的請(qǐng)求都是正常的丸氛,讓100%的請(qǐng)求都到這個(gè)存儲(chǔ)里去查一下,這會(huì)導(dǎo)致處理流程變的很慢著摔。
2.1 解決
- 最好是當(dāng)這個(gè)存儲(chǔ)出現(xiàn)沖突的時(shí)候會(huì)報(bào)錯(cuò)缓窜,
- 我們收到交易請(qǐng)求后,直接去存儲(chǔ)里記錄這個(gè) ID(相對(duì)于數(shù)據(jù)的 Insert 操作)谍咆,如果出現(xiàn) ID 沖突了的異常禾锤,那么我們就知道這個(gè)之前已經(jīng)有人發(fā)過來了,所以就不用再做了摹察。
- 假如是mysql數(shù)據(jù)庫(kù)中你可以使用 insert into … values … on DUPLICATE KEY UPDATE … 這樣的操作
CREATE TABLE unique_table
(
id bigint DEFAULT 0 NOT NULL
CONSTRAINT `PRIMARY`
PRIMARY KEY,
type varchar(50) NULL,
state int DEFAULT 0 NOT NULL
);
insert into unique_table values (2,'訂單系統(tǒng)',1) on DUPLICATE KEY UPDATE state = state + 1 ;
# 第一次操作 1 row affected in 19 ms
# 第二次操作 2 rows affected in 15 ms
根據(jù) row的值不同恩掷,判斷值是否有
- 假如是更新的場(chǎng)景
update table set status = “paid” where id = xxx and status = “unpaid”;
- 網(wǎng)上還有 MVCC 通過使用版本號(hào)等其他方式,我覺得這些都不標(biāo)準(zhǔn)供嚎,我們希望我們有一個(gè)標(biāo)準(zhǔn)的方式來做這個(gè)事黄娘,所以峭状,最好還是用一個(gè) ID。
3. 存儲(chǔ)
- 因?yàn)閮绲刃苑欠植际降谋普孕枰鎯?chǔ)也是共享的优床。
- 這樣每個(gè)服務(wù)就變成沒有狀態(tài)的了。但是誓焦,這個(gè)存儲(chǔ)就成了一個(gè)非常關(guān)鍵的依賴胆敞,其擴(kuò)展性和可用性也成了非常關(guān)鍵的指標(biāo)。
- 你可以使用關(guān)系型數(shù)據(jù)庫(kù)杂伟,或是 key-value 的 NoSQL(如 MongoDB)來構(gòu)建這個(gè)存儲(chǔ)系統(tǒng)移层。
3. HTTP 的冪等性
1. http 常見方法的冪等性
HTTP GET 方法用于獲取資源,不應(yīng)有副作用稿壁,所以是冪等的
-
HTTP HEAD 和 GET 本質(zhì)是一樣的幽钢,區(qū)別在于 HEAD 不含有呈現(xiàn)數(shù)據(jù)歉备,而僅僅是 HTTP 頭信息傅是,不應(yīng)有副作用,也是冪等的
- 欲判斷某個(gè)資源是否存在蕾羊,我們通常使用 GET喧笔,但這里用 HEAD 則意義更加明確。也就是說龟再,HEAD 方法可以用來做探活使用书闸。
-
HTTP OPTIONS 主要用于獲取當(dāng)前 URL 所支持的方法,所以也是冪等的
- 若請(qǐng)求成功利凑,則它會(huì)在 HTTP 頭中包含一個(gè)名為“Allow”的頭浆劲,值是所支持的方法,如“GET, POST”哀澈。
-
HTTP DELETE 方法用于刪除資源牌借,有副作用,但它應(yīng)該滿足冪等性割按。
- 比如:DELETE http://www.forum.com/article/4231膨报,調(diào)用一次和 N 次對(duì)系統(tǒng)產(chǎn)生的副作用是相同的,即刪掉 ID 為 4231 的帖子适荣。因此现柠,調(diào)用者可以多次調(diào)用或刷新頁面而不必?fù)?dān)心引起錯(cuò)誤。
-
HTTP POST 方法用于創(chuàng)建資源弛矛,所對(duì)應(yīng)的 URI 并非創(chuàng)建的資源本身够吩,而是去執(zhí)行創(chuàng)建動(dòng)作的操作者,有副作用丈氓,不滿足冪等性废恋。
- 比如:POST http://www.forum.com/articles的語義是在http://www.forum.com/articles下創(chuàng)建一篇帖子谈秫,HTTP 響應(yīng)中應(yīng)包含帖子的創(chuàng)建狀態(tài)以及帖子的 URI。兩次相同的 POST 請(qǐng)求會(huì)在服務(wù)器端創(chuàng)建兩份資源鱼鼓,它們具有不同的 URI拟烫;所以,POST 方法不具備冪等性迄本。
-
HTTP PUT 方法用于創(chuàng)建或更新操作硕淑,所對(duì)應(yīng)的 URI 是要?jiǎng)?chuàng)建或更新的資源本身,滿足冪等性
- 用于更新資源嘉赎,沒有的話則執(zhí)行創(chuàng)建操作置媳。每次執(zhí)行請(qǐng)求時(shí)都會(huì)先判斷一下序號(hào)為1的花信息是否存在,不存在則創(chuàng)建公条,否則視為更新拇囊。
HTTP PATCH 是對(duì)PUT方法的補(bǔ)充,用來對(duì)已知資源進(jìn)行局部更新 , 不滿足冪等性
-
TRACE 方法 實(shí)現(xiàn)沿通向目標(biāo)資源的路徑的消息環(huán)回(loop-back)測(cè)試靶橱,提供了一種實(shí)用的 debug 機(jī)制寥袭。
- 請(qǐng)求的最終接收者應(yīng)當(dāng)原樣反射(reflect)它接收到的消息,除了以下字段部分关霸,作為一個(gè)Content-Type 為 message/http 的 200(OK)響應(yīng)的消息的主體(body)返回給客戶端传黄。
2. POST 方式不支持冪等性,的冪等性設(shè)計(jì)队寇。
在表單中需要隱藏一個(gè) token(可以前后端生成)
-
后端把用戶提交的數(shù)據(jù)和這個(gè) token 保存在數(shù)據(jù)庫(kù)中
- 如果有重復(fù)提交膘掰,那么數(shù)據(jù)庫(kù)中的 token 會(huì)做排它限制,從而做到冪等性佳遣。
更為穩(wěn)妥的做法是识埋,后端成功后向前端返回 302 跳轉(zhuǎn),把用戶的前端頁跳轉(zhuǎn)到 GET 請(qǐng)求零渐,把剛剛 POST 的數(shù)據(jù)給展示出來窒舟。如果是 Web 上的最好還把之前的表單設(shè)置成過期,這樣用戶不能通過瀏覽器后退按鈕來重新提交相恃。這個(gè)模式又叫做 PRG 模式(Post/Redirect/Get)辜纲。
3 . POST 方式不支持冪等性,常用方式
- 前端實(shí)現(xiàn)
- 點(diǎn)擊提交拦耐, 按鈕disable
- 后端
- 數(shù)據(jù)庫(kù)唯一索引
- 適合場(chǎng)景
- 插入操作
- 適合場(chǎng)景
- 基于redis 實(shí)現(xiàn)一套冪等性防重框架
- 適合場(chǎng)景
- 更新操作耕腾,配合業(yè)務(wù)操作
- 步驟
- 用個(gè)攔截器,攔截所有參數(shù)杀糯, 參數(shù)拼接作為key去redis 判斷下扫俺。
- 適合 系統(tǒng)參數(shù)比較規(guī)定。
- 先uuid 獲取固翰,然后操作成功刪除狼纬。
- 用個(gè)攔截器,攔截所有參數(shù)杀糯, 參數(shù)拼接作為key去redis 判斷下扫俺。
- 適合場(chǎng)景
- 數(shù)據(jù)庫(kù)狀態(tài)機(jī)
- 適合場(chǎng)景
- 更新操作
- 適合場(chǎng)景
- 通過鎖實(shí)現(xiàn)
- 使用數(shù)據(jù)庫(kù)實(shí)現(xiàn)冪等性
- 通過悲觀鎖來實(shí)現(xiàn)冪等性
- 通過唯一索引來實(shí)現(xiàn)冪等性
- 通過樂觀鎖來實(shí)現(xiàn)冪等性
- 使用 JVM 鎖實(shí)現(xiàn)冪等性
- 使用分布式鎖實(shí)現(xiàn)冪等性
- 使用數(shù)據(jù)庫(kù)實(shí)現(xiàn)冪等性
- 數(shù)據(jù)庫(kù)唯一索引