真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

架構(gòu)設(shè)計(jì)|接口冪等性原則,防重復(fù)提交Token管理

本文源碼: GitHub·點(diǎn)這里 || GitEE·點(diǎn)這里

成都創(chuàng)新互聯(lián)是專業(yè)的寶坻網(wǎng)站建設(shè)公司,寶坻接單;提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行寶坻網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!

一、冪等性概念

1、冪等簡介

編程中一個(gè)冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。就是說,一次和多次請求某一個(gè)資源會(huì)產(chǎn)生同樣的作用影響。

2、HTTP請求

遵循Http協(xié)議的請求,越來越強(qiáng)調(diào)Rest請求風(fēng)格,可以更好的規(guī)范和理解接口的設(shè)計(jì)。

GET:用于獲取資源,不應(yīng)有副作用,所以是冪等的;

POST:用于創(chuàng)建資源,重復(fù)提交POST請求可能產(chǎn)生兩個(gè)不同的資源,有副作用不滿足冪等性;

PUT:用于更新操作,重復(fù)提交PUT請求只會(huì)對其URL中指定的資源有副作用,滿足冪等性;

DELETE:用于刪除資源,有副作用,但它應(yīng)該滿足冪等性;

HEAD:和GET本質(zhì)是一樣的,但HEAD不含有呈現(xiàn)數(shù)據(jù),僅是HTTP頭信息,沒有副作用,滿足冪等性;

OPTIONS:用于獲取當(dāng)前URL所支持的請求方法,滿足冪等性;

二、場景業(yè)務(wù)分析

1、訂單支付

架構(gòu)設(shè)計(jì) | 接口冪等性原則,防重復(fù)提交Token管理

實(shí)際開發(fā)中,經(jīng)常會(huì)面對訂單支付問題,基本流程如下:

  • 客戶端發(fā)起訂單支付請求 ;
  • 支付前系統(tǒng)本地相關(guān)業(yè)務(wù)處理 ;
  • 請求第三方支付服務(wù)執(zhí)行扣款;
  • 第三方支付返回處理結(jié)果;
  • 本地服務(wù)基于支付結(jié)果響應(yīng)客戶端;

該業(yè)務(wù)流程中要處理相當(dāng)復(fù)雜的問題,比如事務(wù),分布式事務(wù),接口延遲超時(shí),客戶端重復(fù)提交等等,這里只基于冪等接口角度來看該流程,其他問題后續(xù)再聊。

2、冪等接口

當(dāng)上述流程的支付請求有明確結(jié)果的時(shí)候:失敗或成功,這樣業(yè)務(wù)流程都好處理,但是例如支付場景如果請求超時(shí),如何判斷服務(wù)的結(jié)果狀態(tài):客戶端請求超時(shí),本地服務(wù)超時(shí),請求支付超時(shí),支付回調(diào)超時(shí),客戶端響應(yīng)超時(shí)等等。

這就需要設(shè)計(jì)流程化的狀態(tài)管理。

3、基礎(chǔ)操作案例

模擬管理上述流程,設(shè)計(jì)冪等接口:

表結(jié)構(gòu)設(shè)計(jì)

CREATE TABLE `dp_order_state` (
    `order_id` BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT '訂單id',
    `token_id` VARCHAR (50) DEFAULT NULL COMMENT '防重復(fù)提交',
    `state` INT (1) DEFAULT '1' COMMENT '1創(chuàng)建訂單,2本地業(yè)務(wù),3支付業(yè)務(wù)',
    PRIMARY KEY (`order_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '訂單狀態(tài)表';
CREATE TABLE `dp_state_record` (
    `id` INT (11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
    `order_id` BIGINT (20) NOT NULL COMMENT '訂單id',
    `state_dec` VARCHAR (50) DEFAULT NULL COMMENT '狀態(tài)描述',
    PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '狀態(tài)記錄表';

模擬業(yè)務(wù)流程

將訂單創(chuàng)建,本地業(yè)務(wù),支付業(yè)務(wù),分開分段管理提交。分階段測試異常熔斷的業(yè)務(wù)。

@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderStateMapper orderStateMapper ;
    @Resource
    private StateRecordMapper stateRecordMapper ;
    @Override
    public OrderState queryOrder(OrderState orderState) {
        Map paramMap = new HashMap<>() ;
        paramMap.put("order_id",orderState.getOrderId());
        List orderStateList = orderStateMapper.selectByMap(paramMap);
        if (orderStateList != null && orderStateList.size()>0){
            return orderStateList.get(0) ;
        }
        return null ;
    }
    @Override
    public boolean createOrder(OrderState orderState) {
        int saveRes = orderStateMapper.insert(orderState);
        if (saveRes > 0){
            saveStateRecord(orderState.getOrderId(),"訂單創(chuàng)建成功");
        }
        return saveRes > 0 ;
    }
    @Override
    public boolean localBiz(OrderState orderState) {
        orderState.setState(2);
        int updateRes = orderStateMapper.updateState(orderState) ;
        if (updateRes > 0){
            saveStateRecord(orderState.getOrderId(),"本地業(yè)務(wù)成功");
        }
        return updateRes > 0;
    }
    @Override
    public boolean paymentBiz(OrderState orderState) {
        orderState.setState(3);
        int updateRes = orderStateMapper.updateState(orderState) ;
        if (updateRes > 0){
            saveStateRecord(orderState.getOrderId(),"支付業(yè)務(wù)成功");
        }
        return updateRes > 0;
    }
    private void saveStateRecord (Long orderId,String stateDec){
        StateRecord stateRecord = new StateRecord() ;
        stateRecord.setOrderId(orderId);
        stateRecord.setStateDec(stateDec);
        stateRecordMapper.insert(stateRecord) ;
    }
}

測試接口

根據(jù)訂單狀態(tài),分段補(bǔ)償執(zhí)行未完成的業(yè)務(wù),如果該訂單已經(jīng)完成,多次提交不影響最終結(jié)果。

@Api(value = "OrderController")
@RestController
public class OrderController {
    @Resource
    private OrderService orderService ;
    @PostMapping("/submitOrder")
    public String submitOrder (OrderState orderState){
        OrderState orderState01 = orderService.queryOrder(orderState) ;
        if (orderState01 == null){
            // 正常業(yè)務(wù)流程
            orderService.createOrder(orderState) ;
            orderService.localBiz(orderState) ;
            orderService.paymentBiz(orderState) ;
        } else {
            switch (orderState01.getState()){
                case 1:
                    // 訂單創(chuàng)建成功:后推執(zhí)行本地和支付業(yè)務(wù)
                    orderService.localBiz(orderState01) ;
                    orderService.paymentBiz(orderState01) ;
                    break ;
                case 2:
                    // 訂單本地業(yè)務(wù)成功:后推執(zhí)行支付業(yè)務(wù)
                    orderService.paymentBiz(orderState01) ;
                    break ;
                default:
                    break ;
            }
        }
        return "success" ;
    }
}

絮叨一句:實(shí)際開發(fā)中,該流程是不會(huì)由頁面多次提交完成,訂單是不能重復(fù)提交的,下面會(huì)演示如何控制,這里業(yè)務(wù)是執(zhí)行后推到完成,也可能業(yè)務(wù)向前清理,把整個(gè)流程置為失敗,這里涉及關(guān)鍵狀態(tài)判斷,要選取一個(gè)狀態(tài)作為成功或失敗的標(biāo)識(shí),判斷后續(xù)操作流程。在分布式系統(tǒng)中這種復(fù)雜流程最難處理的是分布式事務(wù),最終一致性問題,后續(xù)再聊。

三、接口重復(fù)提交

1、表單重復(fù)提交

在實(shí)際情況中,接口如果處理時(shí)間過長,用戶可能會(huì)點(diǎn)擊多次提交按鈕,導(dǎo)致數(shù)據(jù)重復(fù)。

常見的一個(gè)解決方案:在表單提交中隱藏一個(gè)token_id參數(shù),一起提交到接口服務(wù)中,數(shù)據(jù)庫存儲(chǔ)訂單和關(guān)聯(lián)的tokenId,如果多次提交,直接返回頁面提示信息即可。

2、演示案例

訂單關(guān)聯(lián)Token查詢

@Service
public class OrderServiceImpl implements OrderService {
    @Override
    public Boolean queryToken(OrderState orderState) {
        Map paramMap = new HashMap<>() ;
        paramMap.put("order_id",orderState.getOrderId());
        paramMap.put("token_id",orderState.getTokenId());
        List orderStateList = orderStateMapper.selectByMap(paramMap);
        return orderStateList.size() > 0 ;
    }
}

測試接口

@RestController
public class OrderController {
    @Resource
    private OrderService orderService ;
    @PostMapping("/repeatSub")
    public String repeatSub (OrderState orderState){
        boolean flag = orderService.queryToken(orderState) ;
        if (flag){
            return "請勿重復(fù)提交訂單" ;
        }
        return "success" ;
    }
}

四、源代碼地址

GitHub·地址
https://github.com/cicadasmile/data-manage-parent
GitEE·地址
https://gitee.com/cicadasmile/data-manage-parent

網(wǎng)站題目:架構(gòu)設(shè)計(jì)|接口冪等性原則,防重復(fù)提交Token管理
網(wǎng)頁鏈接:http://weahome.cn/article/gsgoij.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部