国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

如何保證接口的冪等性?

這篇具有很好參考價值的文章主要介紹了如何保證接口的冪等性?。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

作者:小牛呼嚕嚕 | https://xiaoniuhululu.com

計算機內(nèi)功、源碼解析、科技故事、項目實戰(zhàn)、面試八股等更多硬核文章,首發(fā)于公眾號「小牛呼嚕?!?/p>

目錄
  • 什么是冪等性?
  • 為什么需要保證冪等性?
  • 接口冪等設計和防止重復提交可以等同嗎?
  • 常用保證冪等性的措施
    • 先select再insert
    • 數(shù)據(jù)庫設置唯一索引或唯一組合索引
    • 去重表
    • insert中加入exist條件判斷
    • 悲觀鎖
    • 樂觀鎖
    • 狀態(tài)機
    • 分布式鎖
    • token機制
  • 尾語

什么是冪等性?

大家好,我是呼嚕嚕,所謂冪等性就是:任意次數(shù)請求 同一個資源,對資源的狀態(tài)產(chǎn)生的影響和執(zhí)行一次請求是相同的。
比如對于接口來說,無論調(diào)用多少次同一個接口,對資源的狀態(tài)都只產(chǎn)生一次影響

為什么需要保證冪等性?

為什么需要做接口的冪等性?如果不做會發(fā)生什么事情?我們在實際企業(yè)開發(fā)過程中,如果僅是對數(shù)據(jù)庫進行查詢、刪除指定記錄操作,重復提交是沒啥問題的。但是如果是新增或者修改操作,就需要考慮重復提交的問題。
比如,如果一個訂單支付的時候,因各種原因重復提交多次,那如果沒有冪等性處理的話,這個訂單將會被支付多次的錢,這種和錢有關的錯誤是絕對不能容忍的。

經(jīng)常發(fā)生重復提交的場景:

  1. 當我們在公司的系統(tǒng)里面,提交表格,前端沒有對保存按鈕的做控制,可以多次點擊,然后我們又不小心快速點了多次,或者是網(wǎng)絡卡頓, 還是其他原因,以為沒有成功提交,就一直點擊保存按鈕,這樣都會產(chǎn)生重復提交表單請求。
  2. 在實際開發(fā)過程中,網(wǎng)絡波動是常有的事,所以很多時候 HTTP 客戶端工具都默認開啟超時重試的機制,這樣就無法避免產(chǎn)生重復的請求。
  3. 還有就是項目可能使用一些中間件,比如kafka消費生產(chǎn)者產(chǎn)生的消息時,可能讀到重復的消息,這樣也會產(chǎn)生重復的請求。
  4. ......

接口冪等設計和防止重復提交可以等同嗎?

接口冪等和防止重復提交有交集,但是嚴格來說并不完全等同

  1. 防重設計,主要從客戶端/前端的角度來解決,主要為了避免重復提交,對每次請求的返回結(jié)果無限制,前端常見的手段:點擊提交按鈕變灰、點擊后跳轉(zhuǎn)結(jié)果頁、每次頁面初始化生成隨機碼,提交時隨機碼緩存,后續(xù)重復的隨機碼請求直接不提交
  2. 冪等設計,強調(diào)更多地是當重復提交請求無法避免的時候,還能保證每次請求都返回一樣的結(jié)果。像我們上面對前端做的限制,是能繞過去的,抓包是能直接把接口給抓出來的,比如惡意批量調(diào)用接口,所以企業(yè)級系統(tǒng),前后端都需要做限制,特別是涉及到錢的業(yè)務。絕不能偷懶,后面我們來詳細講講對接口冪等的限制。

常用保證冪等性的措施

先select再insert

新手小白,在往數(shù)據(jù)庫插入數(shù)據(jù)時,為了防止重復插入,一般會在insert前,通過關鍵字去先select一下,如果查不到記錄就執(zhí)行insert操作,否則就不插入

如何保證接口的冪等性?
但如果并發(fā)場景下,這個就不行了。比如線程2,在線程1插入數(shù)據(jù)前,執(zhí)行select,最終它也會去執(zhí)行插入操作,這樣就會產(chǎn)生2條記錄。所以在實際開發(fā)過程中,是不建議如此操作的。

數(shù)據(jù)庫設置唯一索引或唯一組合索引

數(shù)據(jù)庫設置唯一索引是我們最常用的方式,一個非常簡單,并且有效的方案。當記錄多次插入數(shù)據(jù)庫,會由于Id或者關鍵字段索引唯一的限制,導致后續(xù)記錄插入失敗

--創(chuàng)建唯一索引
alter table `order` add UNIQUE KEY `索引名` (`字段`);

第一條記錄插入到數(shù)據(jù)庫中,當后面其他相同的請求,再插入時,數(shù)據(jù)庫會報異常Duplicate entry 'xx' for key 'xx_name',這個異常不會對數(shù)據(jù)庫中既有的數(shù)據(jù)有影響,我們只需對異常進行捕獲就行,直接返回,代表已經(jīng)執(zhí)行過當前請求。

筆者這里介紹一個騷操作:INSERT IGNORE

insert ignore INTO tableName VALUES ("id","xxx")

咦,會有讀者覺得,這樣哪怕索引沖突了,數(shù)據(jù)庫會忽略錯誤返回影響行數(shù)0,這樣就不用再在代碼中,手動捕捉異常了,又方便又省事!

但事實真這樣嗎???
如何保證接口的冪等性?
如果希望在每次插入新記錄時,自動地創(chuàng)建主鍵字段的值。一般會將主鍵id的屬性設為AUTO_INCREMENT,
如果我們使用INSERT IGNORE時,沒有成功新增記錄,但是AUTO_INCREMENT會自動+1,binlog中也沒有 INSERT IGNORE 語句日志。這個會導致主從數(shù)據(jù)一致性問題,如果線上環(huán)境數(shù)據(jù)庫是主從架構(gòu),從庫該字段的AUTO_INCREMENT值會和主庫不一致,切庫(從庫變成總庫)的時候會沖突。

當然,查詢Mysql官方手冊,發(fā)現(xiàn)innodb_autoinc_lock_mode用于平衡性能與主從數(shù)據(jù)一致性,innodb_autoinc_lock_mode=0可以解決這個問題,將其設為0后, 所有的insert語句都要在語句開始的時候得到一個表級的auto_inc鎖,在語句結(jié)束的時候才釋放這把鎖。也就是說在INSERT未成功執(zhí)行時AUTO_INCREMENT不會自增,但是其也有缺點,會影響到數(shù)據(jù)庫的并發(fā)插入性能。

Mysql官方手冊明確指出,The setting innodb_autoinc_lock_mode=0 should not be used except for compatibility purposes.除非出于兼容性目的,否則不應設置innodb_autoinc_lock_mode=0。所以我們還是老老實實手動捕捉異常,慎用insert ignore

**innodb_autoinc_lock_mode: **在MySQL8中, 默認值為 2 (輕量級鎖) , 在MySQL8之前, 5.1之后, 默認值為 1(混合使用這2種鎖), 在更早的版本是 0(auto_inc鎖)

去重表

去重表,其實也是唯一索引方案的一個變種,原表不太適合再新建唯一索引了,且數(shù)據(jù)量不大的話。我們可以再新建一張去重表,把唯一標識作為唯一索引,然后把對原表的操作和同時新增去重表 ,放在一個事務中,如果重復創(chuàng)建,去重表會拋出唯一約束異常,事務里所有的操作就會回滾。

insert中加入exist條件判斷

有時候我們會遇到非常復雜的表,表結(jié)構(gòu)確定了,比如已經(jīng)有了許多索引字段,不太適合再新建索引的時候,呼嚕嚕 在這里再提供一個"騷操作":可以通過insert中加入exist來解決重復插入的問題。
比如:

insert into order(id,code,password)
select ${id},${code},${password}
from order
where not exists(select 1 from order where code = ${code}) limit 0,1;

上面的sql注意思路就是將查詢和插入寫在同一個sql中,需要注意的是limit 0,1最后一定要加上,不然可能會出現(xiàn)重復插入的情況

悲觀鎖

悲觀鎖,顧名思義就是,對數(shù)據(jù)被外界或者內(nèi)部修改處理時,持"悲觀"態(tài)度,總認為會發(fā)生并發(fā)沖突,所以會在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)鎖定。
悲觀鎖的實現(xiàn),通常依靠數(shù)據(jù)庫提供的鎖機制實現(xiàn),在這里以mysql為例,最典型的就是"for update"。

select * from order where id = "xxxx" for update; 

需要注意的是:使用悲觀鎖,需要先關閉mysql的自動提交功能,將 set autocommit = 0;

for update僅適用于Mysql中l(wèi)nnoDB引擎,默認是行級鎖,如果sql中有明確指定的主鍵時候,是行級鎖,如果沒有,會鎖表(非常危險的操作)。for update一般和事務配合使用,一旦用戶對某個行施加了行級加鎖,則該用戶可以查詢也可以更新被加鎖的數(shù)據(jù)行,其它用戶只能查詢但不能更新被加鎖的數(shù)據(jù)行。直到顯示提交事務(由于關閉了mysql的自動提交)時,for update獲取的鎖會自動釋放。

悲觀鎖雖然保證了數(shù)據(jù)處理的安全性,但會嚴重影響并發(fā)效率,降低系統(tǒng)吞吐量。適用于并發(fā)量不大、又對數(shù)據(jù)一致性比較高的場景。

樂觀鎖

樂觀鎖,和悲觀鎖相反,對數(shù)據(jù)被外界或者內(nèi)部修改處理時,持"樂觀"態(tài)度,總認為不會發(fā)生并發(fā)沖突,所以不會上鎖,只需在更新的時候會去判斷一下在此期間有沒有去更新這個數(shù)據(jù)。

一般是使用版本號或者時間戳,比如

  1. 我們在數(shù)據(jù)庫中,給訂單表增加一個version 字段
  2. select數(shù)據(jù)時,將version一起讀出,當提交數(shù)據(jù)更新時,判斷版本號是否和取出來的是否一致。如果不一致就代表,已更新,那就不更新。如果一致就繼續(xù)執(zhí)行更新操作。
  3. 每次更新時,除了更新指定的字段,也要將version進行+1操作
update order set name=#{xxx},version=#{version} where id=#{id} and version < ${version}

不加鎖就能保證冪等性,又增加了系統(tǒng)吞吐量,如果頻繁觸發(fā)版本號不一致的情況,反而降低了性能。

狀態(tài)機

狀態(tài)機也是樂觀鎖的一種,比如企業(yè)級貨品管理系統(tǒng)中,訂單的轉(zhuǎn)單流程,將訂單的狀態(tài),設置為有限的幾個(1-下單、2-已支付、3-完成、4-發(fā)貨、5-退貨),通過各個狀態(tài)依次執(zhí)行轉(zhuǎn)換,來控制訂單轉(zhuǎn)單的流程,是非常好的選擇。

分布式鎖

上面介紹了許多方案,在單體應用中是沒啥問題的,但是隨著時代的發(fā)展,現(xiàn)在微服務大行其道,以上方法就不太適應了。

在分布式系統(tǒng)中,上面唯一索引對于全局來說,是無法確定的,我們可以引入第三方分布式鎖來保證冪等性設計。分布式鎖,主要是用來 當多個進程不在同一個系統(tǒng)中,用分布式鎖控制多個進程對資源的訪問

實現(xiàn)分布式鎖常見的方法有:基于redis實現(xiàn)分布式鎖,基于 Consul 實現(xiàn)分布式鎖,基于 zookeeper實現(xiàn)分布式鎖等等,本文重點介紹最常見的基于redis實現(xiàn)分布式鎖,set NX PX + Lua
如何保證接口的冪等性?

  1. 在分布式系統(tǒng)中,插入或者更新的請求,業(yè)務邏輯中先獲取唯一業(yè)務字段,比如訂單id之類的,接著需要獲取分布式鎖,對redis執(zhí)行下述命令
SET key value NX PX 30000

各參數(shù)的含義:

  • SET: 在Redis 2.6.12之后,set命令整合了setex命令 的功能,支持了原子命令加鎖和設置過期時間的功能
  • key:業(yè)務邏輯中先獲取唯一業(yè)務字段,比如訂單id,code之類,也可以在前面加一些系統(tǒng)參數(shù)當前綴,這個完全可以自定義
  • value: 填入是一串隨機值,必須保證全局唯一性(在釋放鎖時,我們需要對value進行驗證,防止誤釋放),一般用uuid來實現(xiàn)
  • NX: 表示key不存在時才設置,如果存在則返回 null。還有另一個參數(shù)XX,表示key存在時才設置,如果不存在則返回NULL
  • PX 30000: 表示過期時間30000毫秒,指到30秒后,key將被自動刪除。這個非常的重要,如果設置過短,無法有效的防止重復請求,過長的話會浪費redis的空間
  1. 然后執(zhí)行插入或者更新,或者其他相關業(yè)務邏輯,在釋放鎖之前,如果有其他中心的服務來請求,由于key是一樣的,無法獲取鎖,就代表這些是重復請求,不操作,直接返回
  2. 執(zhí)行完插入或者更新后,需要釋放鎖,一定要判斷釋放的鎖的value和與Redis內(nèi)存儲的value是否一致,不然如果直接刪除的話,會把其他中心服務的鎖釋放調(diào)。

這種先查再刪的2步操作,我們可以使用lua腳本,把他們變成一個"原子操作"

Lua 是一種輕量小巧的腳本語言,Redis會將整個腳本作為一個整體執(zhí)行,中間不會被其他命令打斷插入(l類似與事務),可以減少網(wǎng)絡開銷,方便復用

以下是Lua腳本,通過 Redis 的 eval/evalsha 命令來運行:

if redis.call('get', KEYS[1]) == ARGV[1] //判斷value是否一致
    then
        return redis.call('del', KEYS[1])//刪除key
    else
        return 0
end

這樣依靠單體的redis實現(xiàn)的分布式鎖能夠很好的解決,微服務系統(tǒng)的冪等問題。但是有些公司的微服務更加龐大,redis也是集群的話,set NX PX + Lua就不夠看了,這里介紹Redis作者推薦的方法-Redlock算法,這里就先不展開講了,不然文章篇幅過長。先挖個坑,后面有空填一下:)

token機制

最后再補充一個方案利用token機制,每次調(diào)用接口時,使用token來標識請求的唯一性。token也叫令牌,天然適合微服務?;趖oken+redis來設計冪等的思路還是比較簡單的,和分布式鎖類似:

  1. 客戶端發(fā)送請求,得去服務端獲取一個全局唯一的一串隨機字符串作為Token 令牌(每次請求獲取到的都是一個全新的令牌),把令牌保存到 redis 中,需要有過期時間,同時把這個 ID 返回給客戶端
  2. 客戶端第二次調(diào)用業(yè)務請求的時候必須攜帶這個 token,服務端會去校驗redis中是否有該token。如果存在,表示這是第一次請求,刪除緩存中的token(這邊還是建議用lua腳本,保證操作的原子性);如果緩存中不存在,表示重復請求,直接返回。

尾語

冪等性是系統(tǒng)服務對外一種承諾,特別業(yè)務中涉及的錢的部分,一定要慎重再慎重。雖然前端做限制會更容易點,但前后端都需要做努力,除了本文介紹的常見的方案,大家也可以集思廣益,畢竟技術在發(fā)展,單體到集群分布式,還會繼續(xù)發(fā)展,還有有新的問題產(chǎn)生。
本文雖然通篇在將冪等的重要性和如何實現(xiàn)冪等,但不可否認,冪等肯定導致系統(tǒng)吞吐量、并發(fā)能力的下降,企業(yè)級應用還是得根據(jù)業(yè)務,權(quán)衡利弊,感謝大家的閱讀。

參考資料:
https://www.cnblogs.com/linjiqin/p/9678022.html


全文完,感謝您的閱讀,如果我的文章對你有所幫助的話,還請點個免費的,你的支持會激勵我輸出更高質(zhì)量的文章,感謝!

如何保證接口的冪等性?文章來源地址http://www.zghlxwxcb.cn/news/detail-711606.html

到了這里,關于如何保證接口的冪等性?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 騰訊二面:如何保證接口冪等性?高并發(fā)下的接口冪等性如何實現(xiàn)?

    什么是接口冪等性 接口冪等性這一概念源于數(shù)學,原意是指一個操作如果連續(xù)執(zhí)行多次所產(chǎn)生的結(jié)果與僅執(zhí)行一次的效果相同,那么我們就稱這個操作是冪等的。在互聯(lián)網(wǎng)領域,特別是在Web服務、API設計和分布式系統(tǒng)中,接口冪等性具有非常重要的意義。 具體到HTTP接口或者

    2024年03月19日
    瀏覽(30)
  • redis+token+分布式鎖確保接口的冪等性

    redis+token+分布式鎖確保接口的冪等性

    目錄 1.冪等性是什么? 2.如何實現(xiàn)冪等性呢? 1.新增管理員,出彈窗的同時,請求后臺。 2.后端根據(jù)雪花算法生成唯一標識key,以雪花數(shù)為key存到redis。并返回key給前端。 3.前端保存后端傳過來的key。 4.前端輸入完成信息,點擊【保存】,攜帶key請求后端。 5.請求到達后端,

    2024年02月14日
    瀏覽(21)
  • 114 接口中冪等性的保證

    114 接口中冪等性的保證

    同樣是?面試問題? 如何確保接口的?冪等性? 冪等是一個?較為抽象的概念,?多次重復訪問,?不會導致業(yè)務邏輯的異常? 這里從增刪改查,?幾個方面列一下? 一般來說,?我們核心需要關注的就是?新增 和 更新 對于?增加元素,?首先針對唯一約束進行校驗, 然后再處理新增的相

    2024年04月23日
    瀏覽(25)
  • RabbitMQ如何保證冪等性

    RabbitMQ如何保證冪等性

    一、簡介 冪等性是分布式中比較重要的一個概念,是指在多作業(yè)操作時候避免造成重復影響,其實就是保證同一個消息不被消費者重復消費兩次,但是可能存在網(wǎng)絡波動等問題,生產(chǎn)者無法接受消費者發(fā)送的ack信息,因此這條消息將會被重復發(fā)送給其他消費者進行消費,實際

    2024年02月15日
    瀏覽(22)
  • RabbitMQ-業(yè)務的冪等性

    生產(chǎn)者和消費者都需要添加配置類: 消費者拿到id之后,保存到數(shù)據(jù)庫,后續(xù)消費時,需要查數(shù)據(jù)庫進行比較,因此這種方案的缺點就是有業(yè)務的入侵,對性有一定的影響。 (1)查詢和刪除操作本身就是冪等性操作。 (2)可以使用分布式鎖,對單據(jù)id鎖定,防止多次提交,

    2024年01月21日
    瀏覽(21)
  • 談談 Kafka 的冪等性 Producer

    使用消息隊列,我們肯定希望不丟消息,也就是消息隊列組件,需要保證消息的可靠交付。消息交付的可靠性保障,有以下三種承諾: 最多一次(at most once):消息可能會丟失,但絕不會被重復發(fā)送。 至少一次(at least once):消息不會丟失,但有可能被重復發(fā)送。 精確一次

    2024年02月14日
    瀏覽(16)
  • 【RabbitMQ】RabbitMQ如何確認消息被消費、以及保證消息的冪等

    【RabbitMQ】RabbitMQ如何確認消息被消費、以及保證消息的冪等

    目錄 一、如何保證消息被消費 二、如何保證消息冪等性 RabbitMQ提供了消息補償機制來保證消息被消費,當一條消費被發(fā)送后,到達隊列后發(fā)給消費者。消費者消費成功后會給MQ服務器的隊列發(fā)送一個確認消息,此時會有一個回調(diào)檢測服務監(jiān)聽該接收確認消息的隊列,然將消費

    2024年02月16日
    瀏覽(24)
  • Java接口冪等性,如何重試?

    Java接口冪等性,如何重試?

    當我們寫好一個項目時,有沒有深深的思考過,如何處理接口冪等性問題呢?今天我們以屈原這句著名詩句“路漫漫其修遠兮,吾將上下而求索”的精神來探索一下這個問題。 冪等性:簡單來說就是一個操作多次執(zhí)行的結(jié)果和一次執(zhí)行產(chǎn)生的結(jié)果一致。 答:在計算機應用中

    2024年02月10日
    瀏覽(21)
  • 高并發(fā)下如何保證接口冪等

    接口冪等性問題,對于開發(fā)人員來說,是一個跟語言無關的公共問題。本文分享了一些解決這類問題非常實用的辦法,絕大部分內(nèi)容我在項目中實踐過的,給有需要的小伙伴一個參考。 不知道你有沒有遇到過這些場景: 有時我們在填寫某些form表單時,保存按鈕不小心快速點

    2023年04月09日
    瀏覽(22)
  • kafka-保證數(shù)據(jù)不重復-生產(chǎn)者開啟冪等性和事務的作用?

    kafka-保證數(shù)據(jù)不重復-生產(chǎn)者開啟冪等性和事務的作用?

    適用于消息在寫入到服務器日志后,由于網(wǎng)絡故障,生產(chǎn)者沒有及時收到服務端的 ACK 消息,生產(chǎn)者誤以為消息沒有持久化到服務端,導致生產(chǎn)者重復發(fā)送該消息,造成了消息的重復現(xiàn)象,而冪等性就是為了解決該問題。 通過3個值的唯一性去重: PID:生產(chǎn)者ID 分區(qū)號 seq:單

    2024年02月14日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包