1. 接口的冪等性的N種考慮,你知道嗎?

        共 2949字,需瀏覽 6分鐘

         ·

        2020-12-10 05:34

        往期熱門文章:

        1、往期精選優(yōu)秀博文都在這里了!

        2、如果程序員和產(chǎn)品經(jīng)理都用凡爾賽文學(xué)對話......

        3、Spring Boot 2.4.0正式發(fā)布,全新的配置文件加載機制(不向下兼容)

        4、Insert into select語句引發(fā)的生產(chǎn)事故!

        5、如果MySQL磁盤滿了,會發(fā)生什么?還真被我遇到了!

        目錄

        • 目錄
        • 前言
        • 正文
          • 1 接口冪等性
          • 1.1 案例
          • 2 解決方案
          • 2.1 token機制
          • 2.2 去重表
          • 2.3 redis 的 SETNX鍵值
          • 2.4 狀態(tài)機冪
          • 2.5 樂觀鎖(更新操作)
          • 2.6 悲觀鎖(更新操作)
        • 結(jié)語

        前言

        今天的主題:接口冪等性的解決方案。本來是想把對象的存儲過程和內(nèi)存布局肝出來的,但是臨時產(chǎn)生了變化,哈哈,這部分內(nèi)容我們留在下一期吧,有句話說的好,好事多磨,對吧。
        在實際項目開發(fā)中接口是我們在開發(fā)中經(jīng)常接觸到的,而且是經(jīng)常經(jīng)常要寫,每一個項目可能都會伴隨著大量的接口開發(fā),在moon來涂鴉的這幾個月,基本上就是在與接口作斗爭了,新需求除了業(yè)務(wù)相關(guān)就是設(shè)計表和接口編寫了。
        當(dāng)然,在接口設(shè)計中我們要考慮很多問題,安全性,格式,設(shè)計等等,今天我們先來聊聊,在高并發(fā)環(huán)境下,接口冪等性的解決方案有哪些。

        正文

        1 接口冪等性

        就是說在多次相同的操作下保證最終的結(jié)果是一致的。
        其實這個概念還是比較簡單的,很容易理解,那我們思考一個問題,如果不保證接口冪等性會有什么問題?

        1.1 案例

        我們簡單的舉個例子,現(xiàn)在有一個接口,提供了轉(zhuǎn)賬的功能,a要給b轉(zhuǎn)賬1000元,正常情況下我們接口一次性就調(diào)用成功了,但是卻因為網(wǎng)絡(luò)抖動等其它原因沒有成功,于是就開始不停的重試,突然網(wǎng)絡(luò)好了,但是這時卻連續(xù)發(fā)出去了三個請求,但是這個接口沒有保證冪等性,于是從結(jié)果上來看就是a給b轉(zhuǎn)了3000元,這顯然是程序業(yè)務(wù)邏輯上不能接受的(其實moon可以當(dāng)b的)。

        2 解決方案

        2.1 token機制

        token機制其實是比較簡單的,我們先來簡單的說一下流程。
        • 首先客戶端先請求服務(wù)端,服務(wù)端生成token,每次請求生成的都是一個新的token(這個token一定要設(shè)置超時時間),將token存入redis當(dāng)中,然后將token返回給客戶端。
        • 客戶端攜帶剛剛返回的token請求服務(wù)端做業(yè)務(wù)請求。
        • 服務(wù)端收到請求,做判斷。
          • 如果token在redis中,則直接刪除該token,然后繼續(xù)做業(yè)務(wù)請求。
          • 如果token不在redis中,代表已經(jīng)執(zhí)行過當(dāng)前業(yè)務(wù)了,則不執(zhí)行業(yè)務(wù)。
        圖示如下:
        token機制實現(xiàn)方式還是比較簡單的,但是其實對于我們某些響應(yīng)速度要求很高的業(yè)務(wù)不太友好,缺點就是需要多一次請求獲取token的過程。
        正常來說是每次請都會生成一個新的token,如果有極限情況下,有兩個請求都帶著相同的token進(jìn)來,會存在都走入判斷是否存在的過程,可能都會同時查到存在,這樣也會有問題,針對這種情況,我們可以在刪除前判斷下是否存在,存在就刪除,為了保證原子性,這部分邏輯建議使用lua腳本完成。

        2.2 去重表

        去重表的機制是根據(jù)mysql唯一索引的特性來的,我們先來說下它的流程:
        • 首先客戶端先請求服務(wù)端,服務(wù)端先將這次的請求信息存入一張mysql的去重表中,這張表要根據(jù)這次請求的其中某個特殊字段建立唯一索引,或者主鍵索引。
        • 判斷是否插入成功
          • 如果插入成功,則繼續(xù)做后續(xù)業(yè)務(wù)請求。
          • 如果插入失敗,則代表已經(jīng)執(zhí)行過當(dāng)前請求。
        圖示如下:
        去重表機制的問題有兩點:
        • 1.mysql容錯性,也就是mysql本身如果不是高可用的那么業(yè)務(wù)可能會受到影響:
        • 2.既然是唯一索引,自然在寫表的時候就沒有辦法用到changbuffer,每次都要從磁盤查出來判斷再寫入,對于一個高并發(fā)的接口來說,這些都是需要考慮的因素。

        2.3 redis 的 SETNX鍵值

        過程如下:
        • 首先客戶端先請求服務(wù)端,服務(wù)端將能代表這次請求業(yè)務(wù)的唯一字段以 SETNX 的方式存入redis,并設(shè)置超時時間,超時時間可以根據(jù)業(yè)務(wù)權(quán)衡。
        • 判斷是否插入成功
          • 如果插入成功,則繼續(xù)做后續(xù)業(yè)務(wù)請求。
          • 如果插入失敗,則代表已經(jīng)執(zhí)行過當(dāng)前請求。
        這里我們是利用了redis setnx 的特性來完成的。
        setnx:只在鍵key不存在的情況下,將鍵key的值設(shè)置為value。若鍵key已經(jīng)存在,則SETNX命令不做任何動作。命令在設(shè)置成功時返回1,設(shè)置失敗時返回0。
        圖示如下:
        這種方案可以說是針對上一個方案改進(jìn)的,效率也會提高很多。

        2.4 狀態(tài)機冪

        這種機制適用于有不同狀態(tài)的業(yè)務(wù),moon的上一家公司就是這樣做的。
        我們的訂單系統(tǒng),一條訂單會有多個狀態(tài),如:待付款,鎖定,已付款等狀態(tài),而這些狀態(tài)都是有流程和邏輯的,我們可以根據(jù)這個狀態(tài)判斷是否執(zhí)行后續(xù)業(yè)務(wù)操作。

        2.5 樂觀鎖(更新操作)

        就是數(shù)據(jù)庫中增加版本號字段,每次更新根據(jù)版本號來判斷
        過程如下:
        • 首先客戶端先請求服務(wù)端,先查詢出當(dāng)前的version版本。
          • select version from .. where ..
        • 根據(jù)version版本來做sql操作
          • UPDATE .. SET ... version=(version+1) WHERE .. AND version=version;
        這個圖示我就不再畫了,還是比較簡單的

        2.6 悲觀鎖(更新操作)

        假設(shè)每一次拿數(shù)據(jù),都有認(rèn)為會被修改,所以給數(shù)據(jù)庫的行上鎖,也是基于數(shù)據(jù)庫特性來完成。
        當(dāng)數(shù)據(jù)庫執(zhí)行select for update時會獲取被select中的數(shù)據(jù)行的行鎖,因此其他并發(fā)執(zhí)行的select for update如果試圖選中同一行則會發(fā)生排斥(需要等待行鎖被釋放),因此達(dá)到鎖的效果。
        START?TRANSACTION;?#?開啟事務(wù)
        SELETE?*?FROM?TABLE?WHERE?..?FOR?UPDATE;
        UPDATE?TABLE?SET?...?WHERE?..;
        COMMIT;?#?提交事務(wù)

        結(jié)語

        關(guān)于接口冪等性這部分內(nèi)容,解決方案其實大同小異,很多方式的原理都是一樣的,更多的其實都是在業(yè)務(wù)鏈路中去過濾,也會有很多是有消息中間件去解決的,默認(rèn)在中間件這一層就直接過濾掉了,當(dāng)然每種方式都有各自的優(yōu)點和缺點,需要結(jié)合當(dāng)前的業(yè)務(wù)去選擇,今天的文章內(nèi)容,你get到了嗎?
        往期熱門文章:

        1、歷史文章分類導(dǎo)讀列表!精選優(yōu)秀博文都在這里了!》

        2、你以為JDK8之后用HashMap就沒事了?死循環(huán)問題依然存在!
        3、14 個 Spring MVC 頂級技巧,隨時用隨時爽,一直用一直爽
        4、交公糧了:十一在家我都逛了哪些技術(shù)網(wǎng)站?
        5、高并發(fā)和海量數(shù)據(jù)下的 9 個 Redis 經(jīng)典案例剖析!

        6、Docker 禁止被列入美國“實體名單”的國家、企業(yè)、個人使用

        7、日志框架到底是Logback 還是 Log4j2?
        8、IDEA 2020.2 重磅發(fā)布,動畫級新功能預(yù)覽!
        9、數(shù)據(jù)庫鏈接池終于搞對了,這次直接從100ms優(yōu)化到3ms!

        10、互聯(lián)網(wǎng)公司忽悠員工的黑話,套路太深了。。。
        瀏覽 67
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 和漂亮老师做爰2 | 欧美成人高清视频 | 肏逼大片 | 国产精品久久久久久久久久嫩草 | 深爱五月婷婷 |