日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java的幂等性处理

發(fā)布時間:2023/12/14 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java的幂等性处理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

探討一下實現(xiàn)冪等性的幾種方式

什么是冪等性?

對于同一筆業(yè)務(wù)操作,不管調(diào)用多少次,得到的結(jié)果都是一樣的。

冪等性設(shè)計

我們以對接支付寶充值為例,來分析支付回調(diào)接口如何設(shè)計?

如果我們系統(tǒng)中對接過支付寶充值功能的,我們需要給支付寶提供一個回調(diào)接口,支付寶回調(diào)信息中會攜帶(out_trade_no【商戶訂單號】,trade_no【支付寶交易號】),trade_no在支付寶中是唯一的,out_trade_no在商戶系統(tǒng)中是唯一的。

回調(diào)接口實現(xiàn)有以下實現(xiàn)方式。

方式1(普通方式)

過程如下:

1.接收到支付寶支付成功請求
2.根據(jù)trade_no查詢當(dāng)前訂單是否處理過
3.如果訂單已處理直接返回,若未處理,繼續(xù)向下執(zhí)行
4.開啟本地事務(wù)
5.本地系統(tǒng)給用戶加錢
6.將訂單狀態(tài)置為成功
7.提交本地事務(wù)

上面的過程,對于同一筆訂單,如果支付寶同時通知多次,會出現(xiàn)什么問題?當(dāng)多次通知同時到達(dá)第2步時候,查詢訂單都是未處理的,會繼續(xù)向下執(zhí)行,最終本地會給用戶加兩次錢。

此方式適用于單機其,通知按順序執(zhí)行的情況,只能用于自己寫著玩玩。

方式2(jvm加鎖方式)

方式1中由于并發(fā)出現(xiàn)了問題,此時我們使用java中的Lock加鎖,來防止并發(fā)操作,過程如下:

1.接收到支付寶支付成功請求
2.調(diào)用java中的Lock加鎖
3.根據(jù)trade_no查詢當(dāng)前訂單是否處理過
4.如果訂單已處理直接返回,若未處理,繼續(xù)向下執(zhí)行
5.開啟本地事務(wù)
6.本地系統(tǒng)給用戶加錢
7.將訂單狀態(tài)置為成功
8.提交本地事務(wù)
9.釋放Lock鎖

分析問題:
Lock只能在一個jvm中起效,如果多個請求都被同一套系統(tǒng)處理,上面這種使用Lock的方式是沒有問題的,不過互聯(lián)網(wǎng)系統(tǒng)中,多數(shù)是采用集群方式部署系統(tǒng),同一套代碼后面會部署多套,如果支付寶同時發(fā)來多個通知經(jīng)過負(fù)載均衡轉(zhuǎn)發(fā)到不同的機器,上面的鎖就不起效了。此時對于多個請求相當(dāng)于無鎖處理了,又會出現(xiàn)方式1中的結(jié)果。此時我們需要分布式鎖來做處理。

方式3(悲觀鎖方式)

使用數(shù)據(jù)庫中悲觀鎖實現(xiàn)。悲觀鎖類似于方式二中的Lock,只不過是依靠數(shù)據(jù)庫來實現(xiàn)的。數(shù)據(jù)中悲觀鎖使用for update來實現(xiàn),過程如下:

1.接收到支付寶支付成功請求
2.打開本地事物
3.查詢訂單信息并加悲觀鎖

select * from t_order where order_id = trade_no for update;

4.判斷訂單是已處理
5.如果訂單已處理直接返回,若未處理,繼續(xù)向下執(zhí)行
6.給本地系統(tǒng)給用戶加錢
7.將訂單狀態(tài)置為成功
8.提交本地事物

重點在于for update,對for update,做一下說明:
1.當(dāng)線程A執(zhí)行for update,數(shù)據(jù)會對當(dāng)前記錄加鎖,其他線程執(zhí)行到此行代碼的時候,會等待線程A釋放鎖之后,才可以獲取鎖,繼續(xù)后續(xù)操作。
2.事物提交時,for update獲取的鎖會自動釋放。

方式3可以正常實現(xiàn)我們需要的效果,能保證接口的冪等性,不過存在一些缺點:
1.如果業(yè)務(wù)處理比較耗時,并發(fā)情況下,后面線程會長期處于等待狀態(tài),占用了很多線程,讓這些線程處于無效等待狀態(tài),我們的web服務(wù)中的線程數(shù)量一般都是有限的,如果大量線程由于獲取for update鎖處于等待狀態(tài),不利于系統(tǒng)并發(fā)操作。

方式4(樂觀鎖方式)

依靠數(shù)據(jù)庫中的樂觀鎖來實現(xiàn)。

1.接收到支付寶支付成功請求
2.查詢訂單信息

select * from t_order where order_id = trade_no;

3.判斷訂單是已處理
4.如果訂單已處理直接返回,若未處理,繼續(xù)向下執(zhí)行
5.打開本地事物
6.給本地系統(tǒng)給用戶加錢
7.將訂單狀態(tài)置為成功,注意這塊是重點,偽代碼:

update t_order set status = 1 where order_id = trade_no where status = 0; //上面的update操作會返回影響的行數(shù)num if(num==1){//表示更新成功提交事務(wù); }else{//表示更新失敗回滾事務(wù); }

注意:
update t_order set status = 1 where order_id = trade_no where status = 0; 是依靠樂觀鎖來實現(xiàn)的,status=0作為條件去更新,類似于java中的cas操作;關(guān)于什么是cas操作,可以移步:什么是 CAS 機制?
執(zhí)行這條sql的時候,如果有多個線程同時到達(dá)這條代碼,數(shù)據(jù)內(nèi)部會保證update同一條記錄會排隊執(zhí)行,最終最有一條update會執(zhí)行成功,其他未成功的,他們的num為0,然后根據(jù)num來進(jìn)行提交或者回滾操作。

方式4(唯一約束方式)

依賴數(shù)據(jù)庫中唯一約束來實現(xiàn)。

我們可以創(chuàng)建一個表:

CREATE TABLE `t_uq_dipose` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`ref_type` varchar(32) NOT NULL DEFAULT '' COMMENT '關(guān)聯(lián)對象類型',`ref_id` varchar(64) NOT NULL DEFAULT '' COMMENT '關(guān)聯(lián)對象id',PRIMARY KEY (`id`),UNIQUE KEY `uq_1` (`ref_type`,`ref_id`) COMMENT '保證業(yè)務(wù)唯一性' ) ENGINE=InnoDB;

對于任何一個業(yè)務(wù),有一個業(yè)務(wù)類型(ref_type),業(yè)務(wù)有一個全局唯一的訂單號,業(yè)務(wù)來的時候,先查詢t_uq_dipose表中是否存在相關(guān)記錄,若不存在,繼續(xù)放行。

過程如下:

1.接收到支付寶支付成功請求
2.查詢t_uq_dipose(條件ref_id,ref_type),可以判斷訂單是否已處理

select * from t_uq_dipose where ref_type = '充值訂單' and ref_id = trade_no;

3.判斷訂單是已處理
4.如果訂單已處理直接返回,若未處理,繼續(xù)向下執(zhí)行
5.打開本地事物
6.給本地系統(tǒng)給用戶加錢
7.將訂單狀態(tài)置為成功
8.向t_uq_dipose插入數(shù)據(jù),插入成功,提交本地事務(wù),插入失敗,回滾本地事務(wù),偽代碼:

try{insert into t_uq_dipose (ref_type,ref_id) values ('充值訂單',trade_no);提交本地事務(wù): }catch(Exception e){回滾本地事務(wù); }

說明:
對于同一個業(yè)務(wù),ref_type是一樣的,當(dāng)并發(fā)時,插入數(shù)據(jù)只會有一條成功,其他的會違法唯一約束,進(jìn)入catch邏輯,當(dāng)前事務(wù)會被回滾,最終最有一個操作會成功,從而保證了冪等性操作。
關(guān)于這種方式可以寫成通用的方式,不過業(yè)務(wù)量大的情況下,t_uq_dipose插入數(shù)據(jù)會成為系統(tǒng)的瓶頸,需要考慮分表操作,解決性能問題。
上面的過程中向t_uq_dipose插入記錄,最好放在最后執(zhí)行,原因:插入操作會鎖表,放在最后能讓鎖表的時間降到最低,提升系統(tǒng)的并發(fā)性。

關(guān)于消息服務(wù)中,消費者如何保證消息處理的冪等性?
每條消息都有一個唯一的消息id,類似于上面業(yè)務(wù)中的trade_no,使用上面的方式即可實現(xiàn)消息消費的冪等性。

總結(jié)

1.實現(xiàn)冪等性常見的方式有:悲觀鎖(for update)、樂觀鎖、唯一約束
2.幾種方式,按照最優(yōu)排序:樂觀鎖 > 唯一約束 > 悲觀鎖
?

總結(jié)

以上是生活随笔為你收集整理的java的幂等性处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。