最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

在工作中,接口冪等該如何做?

2023-06-24 14:23 作者:你認(rèn)識(shí)張大衛(wèi)嗎  | 我要投稿

前言

大家好,我是路由器沒有路。

隨著互聯(lián)網(wǎng)的發(fā)展,Web API 已成為現(xiàn)代應(yīng)用程序的重要組成部分,它允許不同的應(yīng)用程序之間進(jìn)行通信和數(shù)據(jù)交換。

那么今天就來講下關(guān)于 Web API 中接口冪等性的一些內(nèi)容,希望對(duì)大家有所幫助。

背景

在 Web API 中,接口冪等性是一種重要的概念,或者說是一種機(jī)制,它可以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)系統(tǒng)造成任何負(fù)面影響。

在 Web 應(yīng)用程序中,由于網(wǎng)絡(luò)延遲、請(qǐng)求重試等因素,可能會(huì)導(dǎo)致同一個(gè)請(qǐng)求被執(zhí)行多次。如果接口不具備冪等性,這樣的重復(fù)請(qǐng)求可能會(huì)導(dǎo)致系統(tǒng)狀態(tài)的不一致性,數(shù)據(jù)的重復(fù)提交等問題。

因此,接口冪等性已成為 Web API 設(shè)計(jì)中的重要考慮因素。

接口冪等的使用場(chǎng)景

我們?cè)谄匠5臉I(yè)務(wù)開發(fā)中會(huì)經(jīng)常接觸到接口需要實(shí)現(xiàn)冪等的場(chǎng)景,但冪等往往又是開發(fā)最容易忽略的一個(gè)點(diǎn)之一,由于冪等實(shí)現(xiàn)不當(dāng)或缺少冪等實(shí)現(xiàn)導(dǎo)致嚴(yán)重的系統(tǒng)故障,如:重復(fù)下單、重復(fù)發(fā)貨、庫存重復(fù)扣減等。

接口冪等性在許多場(chǎng)景下都是非常重要的。以下是一些常見的使用場(chǎng)景

  • 前端重復(fù)提交表單:用戶填寫完成表單提交,很多時(shí)候會(huì)因網(wǎng)絡(luò)波動(dòng)沒有及時(shí)對(duì)用戶做出提交成功響應(yīng),致使用戶認(rèn)為沒有成功提交,然后一直點(diǎn)提交按鈕,這時(shí)就會(huì)發(fā)生重復(fù)提交表單請(qǐng)求。

  • 支付系統(tǒng):在支付系統(tǒng)中,重復(fù)的支付請(qǐng)求可能會(huì)導(dǎo)致用戶的賬戶被重復(fù)扣款。因此,支付接口必須具備冪等性,以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)用戶的賬戶造成任何影響。

  • 訂單系統(tǒng):在訂單系統(tǒng)中,重復(fù)的訂單請(qǐng)求可能會(huì)導(dǎo)致系統(tǒng)中出現(xiàn)重復(fù)的訂單。因此,訂單接口必須具備冪等性,以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)系統(tǒng)中的訂單數(shù)據(jù)造成任何影響。

  • 數(shù)據(jù)庫操作:在數(shù)據(jù)庫操作中,重復(fù)的插入、更新、刪除請(qǐng)求可能會(huì)導(dǎo)致數(shù)據(jù)的重復(fù)提交或丟失。因此,數(shù)據(jù)庫接口必須具備冪等性,以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)數(shù)據(jù)庫中的數(shù)據(jù)造成任何影響。

  • 用戶惡意刷單:例如在實(shí)現(xiàn)用戶投票這種功能時(shí),如果用戶針對(duì)一個(gè)用戶進(jìn)行重復(fù)提交投票,這樣會(huì)導(dǎo)致接口接收到用戶重復(fù)提交的投票信息,這樣會(huì)使投票結(jié)果與事實(shí)嚴(yán)重不符。

  • 接口超時(shí)重復(fù)提交: 很多時(shí)候 HTTP 客戶端工具(如 OpenFeign)都默認(rèn)開啟超時(shí)重試的機(jī)制,尤其是第三方調(diào)用接口時(shí)候,為了防止網(wǎng)絡(luò)波動(dòng)超時(shí)等造成的請(qǐng)求失敗,都會(huì)添加重試機(jī)制,導(dǎo)致一個(gè)請(qǐng)求提交多次。

  • 消息進(jìn)行重復(fù)消費(fèi):當(dāng)使用 MQ 消息中間件時(shí)候,如果發(fā)生消息中間件出現(xiàn)錯(cuò)誤未及時(shí)提交消費(fèi)信息,導(dǎo)致發(fā)生重復(fù)消費(fèi)。

Restful API 接口的冪等性是怎么樣的

Restful API 中,接口冪等性是一種非常重要的概念。Restful API 的冪等性可以通過 HTTP 方法來實(shí)現(xiàn)。

根據(jù) HTTP 規(guī)范,GETHEAD 方法是冪等的,因?yàn)樗鼈冎皇亲x取資源,不會(huì)對(duì)資源進(jìn)行修改。

POSTPUTDELETE 方法則不是冪等的,因?yàn)樗鼈儠?huì)對(duì)資源進(jìn)行修改。

方法是否冪等描述GET?GET 方法用于獲取資源,其一般不會(huì)對(duì)系統(tǒng)資源進(jìn)行改變,所以是冪等的HEAD?Head 與 get 請(qǐng)求類似,返回的響應(yīng)中沒有具體內(nèi)容,用于獲取報(bào)頭,所以也是冪等的POST×POST 方法一般用于創(chuàng)建新的資源。其每次執(zhí)行都會(huì)新增數(shù)據(jù),所以不是冪等的PUT×PUT 方法一般用于修改資源,所以也是非冪等的DELETE×DELETE 方法一般用戶刪除資源,所以也是非冪等的

冪等的不足之處

雖然接口冪等性可以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)系統(tǒng)造成任何負(fù)面影響,但是它也存在一些不足之處。

以下是一些常見的問題:

  • 增加了額外的控制冪等業(yè)務(wù)邏輯,復(fù)雜了業(yè)務(wù)邏輯處理

  • 把本可以并行執(zhí)行的操作變成了串行執(zhí)行,降低了執(zhí)行效率

  • 實(shí)現(xiàn)復(fù)雜:實(shí)現(xiàn)接口冪等性可能需要使用一些復(fù)雜的技術(shù),如分布式鎖、樂觀鎖等,這可能會(huì)增加系統(tǒng)的復(fù)雜度

  • 性能影響:實(shí)現(xiàn)接口冪等性可能會(huì)對(duì)系統(tǒng)的性能產(chǎn)生影響,如分布式鎖可能會(huì)降低系統(tǒng)的吞吐量

  • 業(yè)務(wù)復(fù)雜度:有些業(yè)務(wù)場(chǎng)景下,可能無法實(shí)現(xiàn)接口冪等性,如在某些業(yè)務(wù)場(chǎng)景下,重復(fù)的請(qǐng)求可能會(huì)對(duì)系統(tǒng)產(chǎn)生影響,這時(shí)就需要在業(yè)務(wù)層面上進(jìn)行處理

冪等實(shí)現(xiàn)的關(guān)鍵點(diǎn)

仔細(xì)分析冪等的定義,發(fā)現(xiàn)冪等在實(shí)現(xiàn)上關(guān)注的重點(diǎn)是辨別重復(fù)請(qǐng)求和重復(fù)請(qǐng)求對(duì)系統(tǒng)不會(huì)多次造成不良影響。

那如何判斷相同請(qǐng)求呢?

  1. 請(qǐng)求方生成唯一請(qǐng)求 Id,服務(wù)提供方通過請(qǐng)求 Id 辨別請(qǐng)求是否重復(fù),如果請(qǐng)求 Id 相同則判定為重復(fù)請(qǐng)求;

  2. 服務(wù)提供方根據(jù)請(qǐng)求參數(shù)經(jīng)過一系列的 Hash 算法生成對(duì)應(yīng)的 Hash 值,若 Hash 值相同則判定為重復(fù)請(qǐng)求;

實(shí)現(xiàn)接口冪等性需要注意以下幾個(gè)關(guān)鍵點(diǎn):

  • 確定請(qǐng)求的唯一性:需要確定每個(gè)請(qǐng)求的唯一性,可以使用請(qǐng)求頭、請(qǐng)求參數(shù)等方式來標(biāo)識(shí)每個(gè)請(qǐng)求的唯一性。

  • 避免重復(fù)執(zhí)行:需要避免同一個(gè)請(qǐng)求多次執(zhí)行的情況發(fā)生,可以使用分布式鎖、樂觀鎖等方式來實(shí)現(xiàn)。

  • 處理異常情況:需要處理異常情況,如網(wǎng)絡(luò)異常、系統(tǒng)故障等情況,可以使用重試機(jī)制來處理異常情況。

  • 記錄請(qǐng)求日志:需要記錄每個(gè)請(qǐng)求的日志,以便于進(jìn)行排查和追蹤。

冪等實(shí)現(xiàn)的方式

實(shí)現(xiàn)接口冪等性的方式有很多,以下是一些常見的方式。

去重表

利用數(shù)據(jù)庫的特性來實(shí)現(xiàn)冪等。通常是在表上構(gòu)建一個(gè)唯一索引,那么只要某一個(gè)數(shù)據(jù)構(gòu)建完畢,后面再次操作也無法成功寫入。

常見的業(yè)務(wù)就是博客系統(tǒng)點(diǎn)贊功能,一個(gè)用戶對(duì)一個(gè)博文點(diǎn)贊后,就把用戶 id 與 博文 id 綁定,后續(xù)該用戶點(diǎn)贊同一個(gè)博文就無法插入了。

或是在金融系統(tǒng)中,給用戶創(chuàng)建金融賬戶,一個(gè)用戶肯定不能有多個(gè)賬戶,就在賬戶表中增加唯一索引來存儲(chǔ)用戶 id,這樣即使重復(fù)操作用戶也只能擁有一個(gè)賬戶。

狀態(tài)標(biāo)識(shí)

狀態(tài)標(biāo)識(shí)是很常見的冪等設(shè)計(jì)方式,主要思路就是通過狀態(tài)標(biāo)識(shí)的變更,保證業(yè)務(wù)中每個(gè)流程只會(huì)在對(duì)應(yīng)的狀態(tài)下執(zhí)行,如果標(biāo)識(shí)已經(jīng)進(jìn)入下一個(gè)狀態(tài),這時(shí)候來了上一個(gè)狀態(tài)的操作就不允許變更狀態(tài),保證了業(yè)務(wù)的冪等性。

狀態(tài)標(biāo)識(shí)經(jīng)常用在業(yè)務(wù)流程較長,修改數(shù)據(jù)較多的場(chǎng)景里。最經(jīng)典的例子就是訂單系統(tǒng),假如一個(gè)訂單要經(jīng)歷 創(chuàng)建訂單 -> 訂單支付/取消-> 發(fā)貨-> 確認(rèn)收貨->關(guān)閉訂單 這幾個(gè)步驟。

那么就有可能一筆待支付的訂單去支付,需要去賬戶里扣除對(duì)應(yīng)的余額,消耗對(duì)應(yīng)的優(yōu)惠卷,但是由于支付完成后網(wǎng)絡(luò)等原因返回了錯(cuò)誤信息,這時(shí)候就會(huì)重試再次去進(jìn)行賬戶余額扣減步驟造成數(shù)據(jù)錯(cuò)誤。

所以為了保證整個(gè)訂單流程的冪等性,可以在訂單信息中增加一個(gè)狀態(tài)標(biāo)識(shí),一旦完成了一個(gè)步驟就修改對(duì)應(yīng)的狀態(tài)標(biāo)識(shí)。

比如訂單支付成功后,就把訂單標(biāo)識(shí)為修改為支付完成待發(fā)貨,現(xiàn)在再次調(diào)用訂單支付或者取消接口,會(huì)先判斷訂單狀態(tài)標(biāo)識(shí),如果是已經(jīng)支付過或者取消訂單,就不會(huì)再次支付了。

Token 機(jī)制(冪等標(biāo)識(shí))

Token 機(jī)制應(yīng)該是適用范圍最廣泛的一種冪等設(shè)計(jì)方案了,具體實(shí)現(xiàn)方式也很多樣化。但是核心思想就是每次操作都生成一個(gè)唯一 Token 憑證,服務(wù)器通過這個(gè)唯一憑證保證同樣的操作不會(huì)被執(zhí)行兩次。

這個(gè) Token 除了字面形式上的唯一字符串,也可以是多個(gè)標(biāo)志的組合(比如上面提到的狀態(tài)標(biāo)志),甚至可以是時(shí)間段標(biāo)識(shí)等等。

換句話說,冪等標(biāo)識(shí)可以在請(qǐng)求頭或響應(yīng)頭中添加一個(gè)唯一的標(biāo)識(shí)符,來標(biāo)識(shí)每個(gè)請(qǐng)求的唯一性。

舉個(gè)例子,比如下單,這是一個(gè)典型的 Post 新增操作,要怎樣防止用戶多次點(diǎn)擊提交導(dǎo)致產(chǎn)生多個(gè)同樣的訂單呢??梢宰層脩籼峤坏臅r(shí)候帶一個(gè)唯一 Token,服務(wù)器只要判斷該 Token 存在了就不允許提交(提示用戶已重復(fù)下單),便能保證冪等性。

上面這個(gè)例子比較容易理解,但是業(yè)務(wù)比較簡(jiǎn)單。由于 Token 機(jī)制適用較廣,所以其設(shè)計(jì)中要注意的要求也會(huì)根據(jù)業(yè)務(wù)不同而不同。

那么 Token 在何時(shí)生成,怎么生成?

這是該機(jī)制的核心,就拿下單服務(wù)來說,如果在用戶提交訂單的時(shí)候才生成 Token,那用戶每次點(diǎn)提交都會(huì)生成新的 Token 然后都能提交成功,就不是冪等的了。必須在用戶提交內(nèi)容之前,比如進(jìn)入 checkout 頁的時(shí)候生成 Token,用戶在提交的時(shí)候內(nèi)容帶著 Token 一起提交,對(duì)于同一個(gè)頁面無論用戶提交多少次,就至多能成功一次。

所以 Token 生成的時(shí)機(jī)必須保證能夠使該操作具多次執(zhí)行都是相同的效果才行。使用 Token 機(jī)制就要求開發(fā)者對(duì)業(yè)務(wù)流程有較好的理解。

案例說明

下面以“數(shù)據(jù)庫樂觀鎖 + 冪等性 + Go 偽代碼”的案例來說明。

在數(shù)據(jù)庫中,樂觀鎖是通過在數(shù)據(jù)表中增加一個(gè)版本號(hào)(或者時(shí)間戳)字段來實(shí)現(xiàn)的。

在更新數(shù)據(jù)時(shí),先查詢當(dāng)前數(shù)據(jù)的版本號(hào),然后將要更新的數(shù)據(jù)的版本號(hào)設(shè)置為當(dāng)前版本號(hào)+1,然后執(zhí)行更新操作。如果更新成功,則說明當(dāng)前數(shù)據(jù)沒有被其他線程修改,否則說明當(dāng)前數(shù)據(jù)已經(jīng)被其他線程修改過,更新失敗。

樂觀鎖的實(shí)現(xiàn)可以很好地解決并發(fā)更新數(shù)據(jù)時(shí)的沖突問題,但是在某些情況下,可能會(huì)出現(xiàn)冪等性問題。例如,在某個(gè)接口中,多個(gè)請(qǐng)求同時(shí)對(duì)同一條數(shù)據(jù)進(jìn)行更新,如果使用樂觀鎖來實(shí)現(xiàn)冪等,那么可能會(huì)導(dǎo)致多次更新操作,最終數(shù)據(jù)的結(jié)果可能并不是我們期望的結(jié)果。

為了解決這個(gè)問題,我們可以在接口層面增加一個(gè)冪等性校驗(yàn),通過校驗(yàn)請(qǐng)求的唯一標(biāo)識(shí)符(如請(qǐng)求 ID)來判斷當(dāng)前請(qǐng)求是否已經(jīng)處理過。如果當(dāng)前請(qǐng)求已經(jīng)處理過,則直接返回結(jié)果,否則執(zhí)行更新操作,并將請(qǐng)求的唯一標(biāo)識(shí)符記錄到數(shù)據(jù)庫中,以便下次校驗(yàn)。這樣就可以保證同一個(gè)請(qǐng)求只會(huì)被處理一次,從而實(shí)現(xiàn)冪等性。

下面是一個(gè)用 Golang 偽代碼實(shí)現(xiàn)樂觀鎖和冪等性的示例:

go復(fù)制代碼//?定義數(shù)據(jù)結(jié)構(gòu) type?User?struct?{ ????ID??????int ????Name????string ????Version?int } ?//?更新用戶信息 func?updateUser(db?*sql.DB,?user?*User,?requestId?string)?error?{ ????//?查詢當(dāng)前版本號(hào) ????var?currentVersion?int ????err?:=?db.QueryRow("SELECT?version?FROM?user?WHERE?id?=??",?user.ID).Scan(&currentVersion) ????if?err?!=?nil?{ ????????return?err ????} ?????//?設(shè)置新版本號(hào) ????user.Version?=?currentVersion?+?1 ?????//?執(zhí)行更新操作 ????result,?err?:=?db.Exec("UPDATE?user?SET?name?=??,?version?=???WHERE?id?=???AND?version?=??",?user.Name,?user.Version,?user.ID,?currentVersion) ????if?err?!=?nil?{ ????????return?err ????} ?????//?判斷更新是否成功 ????rowsAffected,?err?:=?result.RowsAffected() ????if?err?!=?nil?{ ????????return?err ????} ????if?rowsAffected?==?0?{ ????????return?errors.New("update?failed") ????} ?????//?記錄請(qǐng)求ID,用于冪等性校驗(yàn) ????_,?err?=?db.Exec("INSERT?INTO?request_log(request_id)?VALUES(?)",?requestId) ????if?err?!=?nil?{ ????????return?err ????} ?????return?nil } ?//?冪等性校驗(yàn) func?checkRequestId(db?*sql.DB,?requestId?string)?bool?{ ????var?count?int ????err?:=?db.QueryRow("SELECT?COUNT(*)?FROM?request_log?WHERE?request_id?=??",?requestId).Scan(&count) ????if?err?!=?nil?{ ????????return?false ????} ????return?count?>?0 } ?//?處理請(qǐng)求 func?handleRequest(db?*sql.DB,?user?*User,?requestId?string)?error?{ ????//?冪等性校驗(yàn) ????if?checkRequestId(db,?requestId)?{ ????????return?nil ????} ?????//?更新用戶信息 ????err?:=?updateUser(db,?user,?requestId) ????if?err?!=?nil?{ ????????return?err ????} ?????return?nil }

在這個(gè)示例中,我們定義了一個(gè) User 結(jié)構(gòu)體,其中包含了用戶的 ID、姓名和版本號(hào)。

在更新用戶信息時(shí),我們首先查詢當(dāng)前版本號(hào),然后將要更新的數(shù)據(jù)的版本號(hào)設(shè)置為當(dāng)前版本號(hào)+1,然后執(zhí)行更新操作。如果更新成功,則說明當(dāng)前數(shù)據(jù)沒有被其他線程修改,否則說明當(dāng)前數(shù)據(jù)已經(jīng)被其他線程修改過,更新失敗。

為了保證冪等性,我們?cè)?handleRequest()函數(shù)中增加了一個(gè)冪等性校驗(yàn),通過查詢請(qǐng)求日志表來判斷當(dāng)前請(qǐng)求是否已經(jīng)處理過。

如果當(dāng)前請(qǐng)求已經(jīng)處理過,則直接返回結(jié)果,否則執(zhí)行更新操作,并將請(qǐng)求的唯一標(biāo)識(shí)符記錄到數(shù)據(jù)庫中,以便下次校驗(yàn)。這樣就可以保證同一個(gè)請(qǐng)求只會(huì)被處理一次,從而實(shí)現(xiàn)冪等性。

方案比較

方案優(yōu)點(diǎn)缺點(diǎn)去重表實(shí)現(xiàn)簡(jiǎn)單,易于理解和維護(hù);可以避免重復(fù)提交和重復(fù)處理的問題1)需要占用額外的存儲(chǔ)空間;2) 只能用于插入和刪除操作;3)只能存在于唯一鍵場(chǎng)景狀態(tài)標(biāo)識(shí)實(shí)現(xiàn)簡(jiǎn)單,查詢效率高1)只適用于更新操作;2)表中需要增加額外的狀態(tài)標(biāo)識(shí)Token 機(jī)制實(shí)現(xiàn)相對(duì)復(fù)雜,但安全性高,可以避免重放攻擊和惡意請(qǐng)求1)需要生成唯一 Token;2)獲取 Token 可能需要與服務(wù)提供方交互;3)需要借助第三方存儲(chǔ)(比如 Redis)

綜上所述,選擇哪種冪等實(shí)現(xiàn)方案取決于具體的業(yè)務(wù)需求和實(shí)現(xiàn)環(huán)境。

如果對(duì)存儲(chǔ)空間和查詢效率要求較高,可以選擇狀態(tài)標(biāo)識(shí);如果對(duì)安全性要求較高,可以選擇 Token 機(jī)制;如果對(duì)實(shí)現(xiàn)簡(jiǎn)單和易于維護(hù)要求較高,可以選擇去重表。

總結(jié)

總之,接口冪等性是 Web API 設(shè)計(jì)中的重要考慮因素,可以確保同一個(gè)請(qǐng)求多次執(zhí)行時(shí),不會(huì)對(duì)系統(tǒng)造成任何負(fù)面影響。

實(shí)現(xiàn)接口冪等性需要注意請(qǐng)求的唯一性、重復(fù)執(zhí)行的處理、異常情況的處理以及請(qǐng)求日志的記錄等關(guān)鍵點(diǎn)。

通??梢允褂萌ブ乇怼顟B(tài)標(biāo)識(shí)、token 機(jī)制等方式來實(shí)現(xiàn)接口冪等性。

在工作中,接口冪等該如何做?的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
南和县| 依兰县| 奇台县| 蒙山县| 鲁山县| 阳谷县| 安化县| 堆龙德庆县| 印江| 玛曲县| 凉山| 祥云县| 炉霍县| 汤阴县| 长春市| 泗水县| 淮南市| 新绛县| 芜湖市| 太谷县| 双城市| 威远县| 横峰县| 深泽县| 临汾市| 苏尼特左旗| 涡阳县| 余江县| 麻江县| 涪陵区| 南川市| 碌曲县| 会宁县| 灵川县| 逊克县| 怀化市| 乐陵市| 安新县| 鸡东县| 昭苏县| 永吉县|