接口冪等性原理與方案總結

文章目錄

  • 接口冪等概念
  • 典型場景
  • 核心解決方案
    • 一鎖
    • 二判
    • 三更新
  • 方案選型對比

接口冪等概念

定義:無論調用接口多少次,對系統的影響與單次調用一樣

范疇:在后端開發中,通常更關注寫接口的冪等,因為寫接口才會對系統數據造成不良影響;讀接口多次調用,我們作為下游通常不好控制,讀取到的數據不一樣也更多是數據實時性導致的問題

典型場景

需要關注冪等性主要由于現代分布式系統面臨三大不可靠因素:

  • 用戶不可靠(手抖多點)
    用戶在表單頁多次點擊按鈕,前端提交了多次創建訂單的請求
  • 網絡不可靠(超時重傳)
    當接口超時的時候,由網關層或者業務層控制重試次數,比方說第三方支付接口超時后通過指數回退算法計算重試間隔再重試
  • 系統不可靠(服務重試)
    MQ 中間件消費消息的時候,由于 Rebalance 或者消費失敗重試等原因,同樣的消息在被舊的消費者處理過后,有可能被再次分配到新的消費者身上繼續處理

核心解決方案

核心流程可以總結為:一鎖二判三更新,這三步每一步涉及到的方案會有分布式鎖、樂觀鎖;Token機制、唯一索引、狀態機、請求序列號等。

偽代碼如下:

// 1. 一鎖: 加上悲觀鎖或者樂觀鎖
Lock.lock();
try {// 2. 二判:判斷請求是否已經被執行過Order order = orderService.queryOrder();if (order.hasSucceeded) {return;}// 3. 三更新:執行更新業務邏輯order.update();return;
} finally {Lock.unlock();
}

很多文章會將分布式鎖、樂觀鎖單獨拿出來作為一種冪等的方案,我覺得這樣理解有失偏頗,因為鎖本質上只是為了保證程序原子化互斥執行的手段,本身就不是專門用來保證冪等的,在這三步里就是為了保證「二判」、「三更新」這兩步的并發安全。舉個極端的例子理解,Redis 鎖確實在一定程度上保證了并發安全情況下的冪等,兩個相同的資源請求同時進來的時候,只有一個請求能夠競爭到鎖,另一個請求直接獲取不到鎖就不阻塞返回了;但假設這兩個請求錯開進入系統,此時不存在鎖的競爭,這個時候兩個請求就都能執行了,如果是創建訂單就創建了多筆訂單,仍然沒有達到冪等。所以將鎖作為「高并發」場景下保證冪等的一種手段,但不是實現冪等的通用范式。

一鎖

鎖主要是為了保證并發場景下「二判」、「三更新」這兩步的并發安全,因為不鎖的話有可能多個線程都沒有用最新值去判斷,如果評估到業務場景下并發確實很低,其實這一步也可以省略。鎖的話就有多種實現方案了,悲觀鎖或者樂觀鎖,但是一定要是互斥鎖。

  1. 分布式悲觀鎖

分布式鎖的實現方式也比較多 Redis、Zk、MySQL,但業界主流的實現方式還是 Redis, 主要還是考慮到 Redis 的高性能、AP 高可用,通過非阻塞的方式實現并發校驗。

  1. 數據庫悲觀鎖

開啟事務并對數據庫中的記錄加上排他行鎖,其他事務必須等待本次事務提交后才能執行,同時需要記得行鎖都是基于索引的,如果不加索引可能會導致鎖表的不良后果。偽代碼如下:

// 1. 開啟事務
begin;
// 2. 查詢出商品信息并加行鎖
select quantity from items where id = 1 for update;
// 3. 修改商品信息
update items set quantity = 2 where id = 1;
// 4. 提交事務
commit;

數據庫悲觀鎖效率低,串行執行,更新失敗概率低,適用于并發寫入比較頻繁的場景。

  1. 數據庫樂觀鎖

并未顯示加鎖,通過版本號 version 實現 CAS 機制

// 1. 查詢原版本號
select quantity, origin_version from item where id = 1;
// 2. CAS 更新
update item set quantity = 2 and version = origin_version + 1 where id = 1 and version = origin_version

數據庫樂觀鎖在讀多寫少的場景效率高,一旦放到并發沖突比較多的場景下或者鎖的粒度沒有掌控好,更新失敗的概率就會變高

二判

判斷這一步主要是進行冪等判斷,Token機制、業務字段、請求序列號(前三種其實都是生成業務冪等號的方式)、唯一索引、狀態機、都能實現類似的功能

  1. 冪等號 - Token 機制

常用在防重復下單的場景,用戶每次訪問頁面時都先向后端請求一個 token,之后在本頁面的操作都需要將此 token 帶過來,頁面不刷新 token 也不變。

Token 生成:

String token = UUID.randomUUID().toString();
jedis.setex(token, 60 * 60, "1");
jedis.close();

Token 校驗,del 會返回被刪除 key 的數量,返回1代表刪除了1條,一個操作來保證原子

Transaction tx = jedis.multi();
if (tx.del(token) == 1) {// 成功
} else {// 失敗
}
jedis.close();
  1. 冪等號 - 業務字段

常用在重復消費的場景下,上下游約定好一個冪等字段的生成方式,通過特定業務字段的拼接傳遞給下游,供下游做判斷

  1. 冪等號 - 請求序列號

通過操作流水來做冪等,常用在金融系統中,或者有流水記錄的業務系統中
在這里插入圖片描述

  1. 唯一索引

這一步是最終兜底方案,通過數據庫的唯一索引充當最后的防線

CREATE TABLE orders (id BIGINT PRIMARY KEY,order_no VARCHAR(32) UNIQUE,...
);

異常處理示例

try {orderDao.insert(order);
} catch (DuplicateKeyException e) {log.warn("重復訂單:{}", order.getOrderNo());return Result.error("訂單已存在");
}
  1. 狀態機(業務流程控制)

碰到這種多狀態的實體一定要設計好狀態流轉

Order order = orderService.queryOrder();
if (order.status != init) {return;
} else {order.setStatus(finished);
}

在這里插入圖片描述

三更新

這沒有什么好說的,就是在第二步判斷為首次操作的情況下,更新數據庫狀態

方案選型對比

  1. 并發較低的情況下不必要上鎖
  2. 請求序列號可靠性最高,但是實現復雜度也高;狀態機流轉適合多狀態業務,實現復雜度適中。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907281.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907281.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907281.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【已解決】windows gitbash 出現CondaError: Run ‘conda init‘ before ‘conda activate‘

在 Git Bash 中執行: source /c/Users/你的用戶名/miniconda3/etc/profile.d/conda.sh # 注意填入你自己的路徑 conda init bash關閉并重新打開 Git Bash 終端。測試激活環境: conda activate your_env_name注意事項 要把上述命令中的 你的用戶名 替…

軟件包管理系統的架構與生態機制

文章目錄 前言一、總結二、如何上傳自己的軟件包 前言 在日常軟件開發中,我們經常使用諸如apt install, pip install, npm install之類的命令,但有一個問題是,這些下載命令是從哪里下載的這些軟件包,以及我們是否能上傳自己的代碼…

Java線程池管理最佳實踐(設計模式)

引言 在多線程編程中,線程池是一種非常重要的資源管理工具。合理使用線程池可以顯著提高系統性能,避免頻繁創建和銷毀線程帶來的開銷。今天,我將為大家深入分析一個實用的ThreadPoolManager實現,它來自com.kingdee.eas.util包&am…

4.8.2 利用Spark SQL計算總分與平均分

在本次實戰中,我們的目標是利用Spark SQL計算學生的總分與平均分。首先,我們準備了包含學生成績的數據文件,并將其上傳至HDFS。接著,通過Spark的交互式編程環境,我們讀取了成績文件并將其轉換為結構化的DataFrame。然后…

HTML 文件路徑完全指南:相對路徑、絕對路徑解析與引用技巧

一、為什么必須學會文件路徑?—— 網頁引用資源的 “地址規則” 在 HTML 中,引用圖片、CSS、JS 等外部文件時,必須通過文件路徑告訴瀏覽器資源的位置。路徑錯誤會導致資源無法加載(頁面出現 broken image 圖標或樣式丟失&#xf…

keepalived兩臺設備同時出現VIP問題

目錄 問題背景: 日志分析如下: 原因和解決方案總結: 問題背景: keepalived-master和keepalived-slave同時出現了VIP,出現了非對稱路由和雙主現象 日志分析如下: master能夠接受到來自slave的通告消息…

【開源解析】基于PyQt5+Folium的谷歌地圖應用開發:從入門到實戰

🌐【開源解析】基于PyQt5Folium的谷歌地圖應用開發:從入門到實戰 🌈 個人主頁:創客白澤 - CSDN博客 🔥 系列專欄:🐍《Python開源項目實戰》 💡 熱愛不止于代碼,熱情源自每…

篇章五 數據結構——鏈表(一)

目錄 1.ArrayList的缺陷 2. 鏈表 2.1 鏈表的概念及結構 2.2 鏈表結構 1. 單向或者雙向 2.帶頭或者不帶頭 3.循環或者非循環 2.3 鏈表的實現 1.完整代碼 2.圖解 3.顯示方法 4.鏈表大小 5. 鏈表是否存在 key 值 6.頭插法 7.尾插法 8.中間插入 9.刪除key值節點 10.…

數據庫相關面試

數據庫相關面試 Mysql MySQL中的事務隔離級別及其特點 參考:事務的隔離級別:可重復讀 未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據 已提交讀(Read Committed):只能讀取到…

基于Scrapy的天貓商品數據爬取與分析實戰(含API簽名破解與可視化)

基于Scrapy的天貓商品數據爬取與分析實戰(含API簽名破解與可視化) 本文以華為Mate 60 Pro為例,詳細介紹如何使用Scrapy框架爬取天貓商品數據,涵蓋API簽名破解、反爬應對、數據存儲及可視化全流程,適合爬蟲進階學習者實…

【C++進階篇】哈希表的模擬實現(賦源碼)

這里寫目錄標題 前言一. 開放地址法實現哈希表1.1 閉散列結構定義1.2 構造函數1.3 插入(線性探測)1.3.1 傳統寫法1.3.2 現代寫法 1.4 查找1.5 刪除 二. 鏈地址法實現哈希表(哈希桶)2.1 開散列結構定義2.2 構造函數2.3 插入2.4 查找…

07-后端Web實戰(部門管理)

5. 修改部門 對于任何業務的修改功能來說,一般都會分為兩步進行:查詢回顯、修改數據。 5.1 查詢回顯 5.1.1 需求 當我們點擊 "編輯" 的時候,需要根據ID查詢部門數據,然后用于頁面回顯展示。 5.1.2 接口描述 參照參照…

深度解析項目集方向或目標根本性轉變的應對策略 —— 項目集管理實戰指南

在復雜多變的商業環境中,項目集管理面臨著重重挑戰,而項目集方向或目標的根本性轉變無疑是其中最具沖擊力的問題之一。本文將深度剖析這一難題,為項目集管理從業者提供實用、新穎且富有價值的應對策略,助力大家在項目集管理的復雜…

JAVA面試復習知識點

面試中遇到的題目,記錄復習(持續更新) Java基礎 1.String的最大長度 https://www.cnblogs.com/wupeixuan/p/12187756.html 2.集合 Collection接口的實現: List接口:ArraryList、LinkedList、Vector Set接口&#xff1a…

【燒腦算法】定長滑動窗口:算法題中的“窗口”智慧

目錄 引言 定長滑動窗口習題剖析 3439. 重新安排會議得到最多空余時間 I 2134. 最少交換次數來組合所有的 1 II 1297. 子串的最大出現次數 2653. 滑動子數組的美麗值 567. 字符串的排列 438. 找到字符串中所有字母異位詞 30. 串聯所有單詞的子串 220. 存在重復元素 II…

JWT安全:接收無簽名令牌.【簽名算法設置為none繞過驗證】

JWT安全:假密鑰【簽名隨便寫實現越權繞過.】 JSON Web 令牌 (JWT)是一種在系統之間發送加密簽名 JSON 數據的標準化格式。理論上,它們可以包含任何類型的數據,但最常用于在身份驗證、會話處理和訪問控制機制中發送有關用戶的信息(“聲明”)。…

XGBoost與SHAP深度解析:從算法原理到實戰價值

在機器學習領域,XGBoost以其卓越的性能長期占據Kaggle競賽和工業界的主流地位,而SHAP(SHapley Additive exPlanations)則成為模型可解釋性的標桿工具。本文將深度解析兩者的技術內核,并通過實戰案例揭示其結合應用的實…

Java SE Cloneable接口和深/淺拷貝

Java為我們提供了各種各樣功能的接口,Clonable接口就是其中之一。 它通常配合Object類的 clone方法使用。這個方法可以為我們創建一個對象的拷貝,即復制一個對象。在進入本文的主要內容之前,先來對訪問限定符 protected進行一個解剖。 1.再…

Python學習(3) ----- Python的函數定義及其使用

Python 中函數是組織好的、可重復使用的代碼塊,用于實現單一或相關聯的功能。下面是函數定義和使用的完整說明: 📌 一、函數定義語法 def 函數名(參數1, 參數2默認值, *args, **kwargs):"""函數說明文檔"""函…

vue2使用el-tree實現兩棵樹間節點的拖拽復制

原文鏈接&#xff1a;兩棵el-tree的節點跨樹拖拽實現 參照這篇文章&#xff0c;把它做成組件&#xff0c;新增左側樹&#xff08;可拖出&#xff09;被拖節點變灰提示&#xff1b; 拖拽中&#xff1a; 拖拽后&#xff1a; TreeDragComponent.vue <template><!-- …