一、幂等性概念

1、幂等简介

编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。就是说,一次和多次请求某一个资源会产生同样的作用影响。

2、HTTP请求

遵循Http协议的请求,越来越强调Rest请求风格,可以更好的规范和理解接口的设计。

GET:用于获取资源,不应有副作用,所以是幂等的;

POST:用于创建资源,重复提交POST请求可能产生两个不同的资源,有副作用不满足幂等性;

PUT:用于更新操作,重复提交PUT请求只会对其URL中指定的资源有副作用,满足幂等性;

DELETE:用于删除资源,有副作用,但它应该满足幂等性;

HEAD:和GET本质是一样的,但HEAD不含有呈现数据,仅是HTTP头信息,没有副作用,满足幂等性;

OPTIONS:用于获取当前URL所支持的请求方法,满足幂等性;

二、场景业务分析

1、订单支付

实际开发中,经常会面对订单支付问题,基本流程如下:

  • 客户端发起订单支付请求 ;
  • 支付前系统本地相关业务处理 ;
  • 请求第三方支付服务执行扣款;
  • 第三方支付返回处理结果;
  • 本地服务基于支付结果响应客户端;

该业务流程中要处理相当复杂的问题,比如事务,分布式事务,接口延迟超时,客户端重复提交等等,这里只基于幂等接口角度来看该流程,其他问题后续再聊。

2、幂等接口

当上述流程的支付请求有明确结果的时候:失败或成功,这样业务流程都好处理,但是例如支付场景如果请求超时,如何判断服务的结果状态:客户端请求超时,本地服务超时,请求支付超时,支付回调超时,客户端响应超时等等。

这就需要设计流程化的状态管理。

3、基础操作案例

模拟管理上述流程,设计幂等接口:

表结构设计

CREATE TABLE `dp_order_state` (`order_id` BIGINT (20) NOT NULL AUTO_INCREMENT COMMENT '订单id',`token_id` VARCHAR (50) DEFAULT NULL COMMENT '防重复提交',`state` INT (1) DEFAULT '1' COMMENT '1创建订单,2本地业务,3支付业务',PRIMARY KEY (`order_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '订单状态表';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 '状态描述',PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '状态记录表';

模拟业务流程

将订单创建,本地业务,支付业务,分开分段管理提交。分阶段测试异常熔断的业务。

@Service
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderStateMapper orderStateMapper ;@Resourceprivate StateRecordMapper stateRecordMapper ;@Overridepublic OrderState queryOrder(OrderState orderState) {Map<String,Object> paramMap = new HashMap<>() ;paramMap.put("order_id",orderState.getOrderId());List<OrderState> orderStateList = orderStateMapper.selectByMap(paramMap);if (orderStateList != null && orderStateList.size()>0){return orderStateList.get(0) ;}return null ;}@Overridepublic boolean createOrder(OrderState orderState) {int saveRes = orderStateMapper.insert(orderState);if (saveRes > 0){saveStateRecord(orderState.getOrderId(),"订单创建成功");}return saveRes > 0 ;}@Overridepublic boolean localBiz(OrderState orderState) {orderState.setState(2);int updateRes = orderStateMapper.updateState(orderState) ;if (updateRes > 0){saveStateRecord(orderState.getOrderId(),"本地业务成功");}return updateRes > 0;}@Overridepublic boolean paymentBiz(OrderState orderState) {orderState.setState(3);int updateRes = orderStateMapper.updateState(orderState) ;if (updateRes > 0){saveStateRecord(orderState.getOrderId(),"支付业务成功");}return updateRes > 0;}private void saveStateRecord (Long orderId,String stateDec){StateRecord stateRecord = new StateRecord() ;stateRecord.setOrderId(orderId);stateRecord.setStateDec(stateDec);stateRecordMapper.insert(stateRecord) ;}
}

测试接口

根据订单状态,分段补偿执行未完成的业务,如果该订单已经完成,多次提交不影响最终结果。

@Api(value = "OrderController")
@RestController
public class OrderController {@Resourceprivate OrderService orderService ;@PostMapping("/submitOrder")public String submitOrder (OrderState orderState){OrderState orderState01 = orderService.queryOrder(orderState) ;if (orderState01 == null){// 正常业务流程orderService.createOrder(orderState) ;orderService.localBiz(orderState) ;orderService.paymentBiz(orderState) ;} else {switch (orderState01.getState()){case 1:// 订单创建成功:后推执行本地和支付业务orderService.localBiz(orderState01) ;orderService.paymentBiz(orderState01) ;break ;case 2:// 订单本地业务成功:后推执行支付业务orderService.paymentBiz(orderState01) ;break ;default:break ;}}return "success" ;}
}

絮叨一句:实际开发中,该流程是不会由页面多次提交完成,订单是不能重复提交的,下面会演示如何控制,这里业务是执行后推到完成,也可能业务向前清理,把整个流程置为失败,这里涉及关键状态判断,要选取一个状态作为成功或失败的标识,判断后续操作流程。在分布式系统中这种复杂流程最难处理的是分布式事务,最终一致性问题,后续再聊。

三、接口重复提交

1、表单重复提交

在实际情况中,接口如果处理时间过长,用户可能会点击多次提交按钮,导致数据重复。

常见的一个解决方案:在表单提交中隐藏一个token_id参数,一起提交到接口服务中,数据库存储订单和关联的tokenId,如果多次提交,直接返回页面提示信息即可。

2、演示案例

订单关联Token查询

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

测试接口

@RestController
public class OrderController {@Resourceprivate OrderService orderService ;@PostMapping("/repeatSub")public String repeatSub (OrderState orderState){boolean flag = orderService.queryToken(orderState) ;if (flag){return "请勿重复提交订单" ;}return "success" ;}
}


<END>

处理接口超时_架构设计 | 接口幂等性原则,防重复提交Token管理相关推荐

  1. 架构设计 | 接口幂等性原则,防重复提交Token管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.幂等性概念 1.幂等简介 编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.就是说,一次和多次请求某一个资源会产 ...

  2. 处理接口超时_架构设计|异步请求如何同步处理?

    本文创意来自一次业务需求,这次需要接入一个第三方外部服务.由于这个服务只提供异步 API,为了不影响现有系统同步处理的方式,接入该外部服务时,应用对外屏蔽这种差异,内部实现异步请求同步. 全文摘要: ...

  3. elk如何同步到es 方案靠谱吗_架构设计:微服务架构如何划分?这6个标准原则让你一目了然...

    互联网应用架构:专注编程教学,架构,JAVA,Python,微服务,机器学习等领域,欢迎关注,一起学习. 前几天在InfoQ看到一篇文章,讲微服务架构如何设计,结合笔者用3年多摸索出来的经验,看了之后 ...

  4. 软件系统架构设计的六大原则

    软件系统架构设计的六大原则 1.单一职责原则(SRP) 2.开放封闭原则(OCP) 3.里氏替换原则(LSP) 4.最少知识原则(LKP) 5.接口隔离原则(ISP) 6.依赖倒置原则(DIP) 1. ...

  5. 架构设计——接口设计

    开发中接口常用方式:前后端交互(Rest),系统交互(RPC)最普遍的一种方式. 接口文档详细,异常定义清晰 满足最小需求原则:尽可能的减少参数,更不允许添加无用的参数. 单一职责:接口粒度应该尽量小 ...

  6. 处理接口超时_开发中那些事儿:为啥update会超时呢?

    前一段时间,生产环境碰到一个异常,更新数据库的时候,提示锁等待超时(Lock wait timeout exceeded; try restarting transaction),超时时间是50秒,修 ...

  7. 如何处理接口幂等性问题(重复提交)

    接口幂等:多次请求,结果一致. 同样的请求参数,多次去访问同一个接口,得到的结果是一致的.且服务端(针对于数据入库或数据修改)只处理一次.通俗点讲就是:防止重复提交. 以下演示相关案例 案例1: 数据 ...

  8. java bundle管理_架构设计——OSGI规范

    架构设计--OSGI简介 一.OSGI简介 1.OSGI简介 OSGI(Open Service Gateway Initiative),即开放服务网关协议,是面向Java的动态模型系统. OSGI是 ...

  9. 设置接口超时的时间,接口超时的排查方法

    一.如何设置接口超时的时间 一.如果发现接口调用超时,排查方法. 1.首先在for循环,或者比较耗时操作的方法开始和结束处打断点,看处理所耗的时间. 比较耗时的操作: -----二次调接口 ----- ...

最新文章

  1. 转:在线框架引用 bootstrap/jq/jqmobile/css框架
  2. 利用组策略防止计算机访问共享资源
  3. matlab中repmat的用法,Matlab: sum的用法、每一行求和、repmat的用法、sum和repmat结合使用减少循环...
  4. 学习笔记27—python中numpy.ravel() 和 flatten()函数
  5. JZOJ 5689. 【GDOI2018Day2模拟4.25】二进制
  6. linux培训笔记1
  7. SpringBoot简要
  8. 崇天老师python123测验6_嵩天老师python123测验1: Python基本语法元素 (第1周)
  9. arctime工程文件怎么打开_怎么办?Vegas的项目工程文件打不开
  10. dubbo之rmi协议使用
  11. 2019-07-22
  12. js数组中判断是否有重复项
  13. GO语言实战之函数与方法
  14. face_recognition实现人脸相似度比较
  15. 终于,高考的三只靴子落下了...
  16. 数学建模学习(101):车辆路线规划问题
  17. 平板作为主机扩展屏的实现
  18. 仿网易云音乐网站(加入个人社区)
  19. 科研篇一:NeurIPS2019 分类整理-对抗样本Meta-Learning
  20. Unity3D灯光与渲染学习之(一):天空盒、灯光以及色彩空间

热门文章

  1. Spring Boot : Spring Boot 开启 debug=true 查看哪些自动配置加载了
  2. Spring : @Value注解
  3. SpringBoot : Consider defining a bean of type xxx in your configuration.
  4. spark学习-38-Spark的MemoryManager
  5. php require 输出乱码,php输出乱码
  6. 五分钟,带你彻底掌握 MyBatis缓存 工作原理
  7. Java同构渲染,从零开始构建react应用(五)同构之服务端渲染
  8. 2019.8.6原型链与继承
  9. Jenkins持续集成之小试牛刀
  10. JSP中URL路径获取问题