什么是冪等性?
對于同一筆業務操作,不管調用多少次,得到的結果都是一樣的。
冪等性設計
我們以對接支付寶充值為例,來分析支付回調接口如何設計?
如果我們系統中對接過支付寶充值功能的,我們需要給支付寶提供一個回調接口,支付寶回調信息中會攜帶(out_trade_no【商戶訂單號】,trade_no【支付寶交易號】),trade_no在支付寶中是唯一的,out_trade_no在商戶系統中是唯一的。
回調接口實現有以下實現方式。
方式1(普通方式)
過程如下:
1.接收到支付寶支付成功請求
上面的過程,對于同一筆訂單,如果支付寶同時通知多次,會出現什么問題?當多次通知同時到達第2步時候,查詢訂單都是未處理的,會繼續向下執行,最終本地會給用戶加兩次錢。
此方式適用于單機其,通知按順序執行的情況,只能用于自己寫著玩玩。
方式2(jvm加鎖方式)
方式1中由于并發出現了問題,此時我們使用JAVA中的Lock加鎖,來防止并發操作,過程如下:
1.接收到支付寶支付成功請求
分析問題:
方式3(悲觀鎖方式)
使用數據庫中悲觀鎖實現。悲觀鎖類似于方式二中的Lock,只不過是依靠數據庫來實現的。數據中悲觀鎖使用for update來實現,過程如下:
1.接收到支付寶支付成功請求
select * from t_order where order_id = trade_no for update;
4.判斷訂單是已處理
重點在于for update,對for update,做一下說明:
方式3可以正常實現我們需要的效果,能保證接口的冪等性,不過存在一些缺點:
方式4(樂觀鎖方式)
依靠數據庫中的樂觀鎖來實現。
1.接收到支付寶支付成功請求
select * from t_order where order_id = trade_no;
3.判斷訂單是已處理
update t_order set status = 1 where order_id = trade_no where status = 0; //上面的update操作會返回影響的行數num if(num==1){ //表示更新成功 提交事務; }else{ //表示更新失敗 回滾事務; }
方式5(唯一約束方式)
依賴數據庫中唯一約束來實現。
我們可以創建一個表:
CREATE TABLE `t_uq_dipose` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `ref_type` varchar(32) NOT NULL DEFAULT '' COMMENT '關聯對象類型', `ref_id` varchar(64) NOT NULL DEFAULT '' COMMENT '關聯對象id', PRIMARY KEY (`id`), UNIQUE KEY `uq_1` (`ref_type`,`ref_id`) COMMENT '保證業務唯一性' );
對于任何一個業務,有一個業務類型(ref_type),業務有一個全局唯一的訂單號,業務來的時候,先查詢t_uq_dipose表中是否存在相關記錄,若不存在,繼續放行。
過程如下:
1.接收到支付寶支付成功請求
select * from t_uq_dipose where ref_type = '充值訂單' and ref_id = trade_no;
3.判斷訂單是已處理
try{ insert into t_uq_dipose (ref_type,ref_id) values ('充值訂單',trade_no); //提交本地事務: }catch(Exception e){ //回滾本地事務; }
說明:
關于消息服務中,消費者如何保證消息處理的冪等性?
總結
1.實現冪等性常見的方式有:悲觀鎖(for update)、樂觀鎖、唯一約束