java实现幂等_接口幂等性如何实现?
导读
现在这个时代大家可能最关心的就是钱了,那么有没有想过你银行转账给你没有一次是转多的,要么失败,要么成功,为什么不能失误一下多转一笔呢?醒醒吧年轻人,别做梦了,做银行的能那么傻x吗?
今天我们就来谈一谈为什么银行转账不能多给我转一笔?关乎到钱的问题,小伙伴们打起精神!!!
要想要理解上述的疑惑,不得不提的一个概念就是幂等性,至于什么是幂等性,如何通过代码实现幂等性,下面将会详细讲述。
什么是幂等性
所谓幂等性通俗的将就是一次请求和多次请求同一个资源产生相同的副作用。用数学语言表达就是f(x)=f(f(x))。
维基百科的幂等性定义如下:
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的,更复杂的操作幂等保证是利用唯一交易号(流水号)实现.
为什么需要幂等性
在系统高并发的环境下,很有可能因为网络,阻塞等等问题导致客户端或者调用方并不能及时的收到服务端的反馈甚至是调用超时的问题。总之,就是请求方调用了你的服务,但是没有收到任何的信息,完全懵逼的状态。比如订单的问题,可能会遇到如下的几个问题:
创建订单时,第一次调用服务超时,再次调用是否产生两笔订单?
订单创建成功去减库存时,第一次减库存超时,是否会多扣一次?
订单支付时,服务端扣钱成功,但是接口反馈超时,此时再次调用支付,是否会多扣一笔呢?
作为消费者,前两种能接受,第三种情况就MMP了,哈哈哈!!!这种情况一般有如下两种解决方式
服务方提供一个查询操作是否成功的api,第一次超时之后,调用方调用查询接口,如果查到了就走成功的流程,失败了就走失败的流程。
另一种就是服务方需要使用幂等的方式保证一次和多次的请求结果一致。
HTTP的幂等性
GET:只是获取资源,对资源本身没有任何副作用,天然的幂等性。
HEAD:本质上和GET一样,获取头信息,主要是探活的作用,具有幂等性。
OPTIONS:获取当前URL所支持的方法,因此也是具有幂等性的。
DELETE:用于删除资源,有副作用,但是它应该满足幂等性,比如根据id删除某一个资源,调用方可以调用N次而不用担心引起的错误(根据业务需求而变)。
PUT:用于更新资源,有副作用,但是它应该满足幂等性,比如根据id更新数据,调用多次和N次的作用是相同的(根据业务需求而变)。
POST:用于添加资源,多次提交很可能产生副作用,比如订单提交,多次提交很可能产生多笔订单。
幂等性的实现方式
对于客户端交互的接口,可以在前端拦截一部分,例如防止表单重复提交,按钮置灰,隐藏,不可点击等方式。但是前端进行拦截器显然是针对普通用户,懂点技术的都可以模拟请求调用接口,所以后端幂等性很重要。
后端的幂等性如何实现?将会从以下几个方面介绍。
数据库去重表
在往数据库中插入数据的时候,利用数据库唯一索引特性,保证数据唯一。比如订单的流水号,也可以是多个字段的组合。
实现比较简单,读者可以自己实现看看,这里不再提供demo了。
状态机
很多业务中多有多个状态,比如订单的状态有提交、待支付、已支付、取消、退款等等状态。后端可以根据不同的状态去保证幂等性,比如在退款的时候,一定要保证这笔订单是已支付的状态。
业务中常常出现,读者可以自己实现看看,不再提供demo。
TOKEN机制
针对客户端连续点击或者调用方的超时重试等情况,例如提交订单,此种操作就可以用Token的机制实现防止重复提交。
TOKEN机制如何实现?简单的说就是调用方在调用接口的时候先向后端请求一个全局ID(TOKEN),请求的时候携带这个全局ID一起请求,后端需要对这个全局ID校验来保证幂等操作,流程如下图:
主要的流程步骤如下:
客户端先发送获取token的请求,服务端会生成一个全局唯一的ID保存在redis中,同时把这个ID返回给客户端。
客户端调用业务请求的时候必须携带这个token,一般放在请求头上。
服务端会校验这个Token,如果校验成功,则执行业务。
如果校验失败,则表示重复操作,直接返回指定的结果给客户端。
通过以上的流程分析,唯一的重点就是这个全局唯一ID如何生成,在分布式服务中往往都会有一个生成全局ID的服务来保证ID的唯一性,但是工程量和实现难度比较大,UUID的数据量相对有些大,此处陈某选择的是雪花算法生成全局唯一ID,不了解雪花算法的读者下一篇文章会着重介绍。
代码实现
陈某选择的环境是SpringBoot+Redis单机环境+注解+拦截器的方式实现,只是演示一下思想,具体的代码可以参照实现。
redis如何实现,获取Token接口将全局唯一Id存入Redis(一定要设置失效时间,根据业务需求),业务请求的时候直接从redis中删除,根据delete的返回值判断,返回true表示第一次请求,返回false表示重复请求。代码如下:
@Service
public class TokenServiceImpl implements TokenService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public String getToken() {
//获取全局唯一id
long nextId = SnowflakeUtil.nextId();
//存入redis,设置10分钟失效
stringRedisTemplate.opsForValue().set(String.valueOf(nextId), UUID.randomUUID().toString(),10, TimeUnit.MINUTES);
return String.valueOf(nextId);
}
/**
* 删除记录,true表示第一次提交,false重复提交
*/
@Override
public Boolean checkToken(String token) {
return stringRedisTemplate.delete(token);
}
}
注解的实现如下,标注在controller类上表示当前类上全部接口都做幂等,标注单个方法上,表示单个接口做幂等操作。
/**
* @Description 幂等操作的注解
* @Author CJB
* @Date 2020/3/25 10:19
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatLimiter {
}
请求头的拦截器,用于提取请求头和校验请求头,如下:
/**
* @Description 获取请求头的信息,具体校验逻辑读者自己实现
* @Author CJB
* @Date 2020/3/25 11:09
*/
@Component
public class HeaderIntercept implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取token
String token = request.getHeader(HeaderConstant.TOKEN);
//校验逻辑
if (!validToken(token))
throw new TokenInvalidException("TOKEN失效");
//获取其他的参数.....
RequestHeader header = RequestHeader.builder()
.token(token)
.build();
//放入request中
request.setAttribute(HeaderConstant.HEADER_INFO,header);
return true;
}
/**
* 校验token,逻辑自己实现
* @param token
* @return
*/
private boolean validToken(String token){
return Boolean.TRUE;
}
}
保证幂等性的拦截器,直接从redis中删除token,成功则第一次提交,不成功则重复提交。
@Component
public class RepeatIntercept implements HandlerInterceptor {
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod){
//获取方法上的参数
RepeatLimiter repeatLimiter = AnnotationUtils.findAnnotation(((HandlerMethod) handler).getMethod(), RepeatLimiter.class);
if (Objects.isNull(repeatLimiter)){
//获取controller类上注解
repeatLimiter=AnnotationUtils.findAnnotation(((HandlerMethod) handler).getBean().getClass(),RepeatLimiter.class);
}
//使用注解,需要拦截验证
if (Objects.nonNull(repeatLimiter)){
//获取全局token,表单提交的唯一id
RequestHeader info = RequestContextUtils.getHeaderInfo();
//没有携带token,抛异常,这里的异常需要全局捕获
if (StringUtils.isEmpty(info.getToken()))
throw new RepeatException();
//校验token
Boolean flag = tokenService.checkToken(info.getToken());
//删除失败,表示
if (Boolean.FALSE.equals(flag))
//抛出重复提交的异常
throw new RepeatException();
}
}
return true;
}
}
接口幂等实现,代码如下:
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 下单
* @param order
* @return
*/
@PostMapping
@RepeatLimiter //幂等性保证
public CommenResult add(@RequestBody Order order){
orderService.save(order);
return new CommenResult("200","下单成功");
}
}
演示
发送getToken的请求获取Token
携带Token下单第一次:
第二次下单:
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[接口幂等性如何实现?]http://www.zyiz.net/tech/detail-121850.html
java实现幂等_接口幂等性如何实现?相关推荐
- Mysql实现幂等_过幂等性问题
一.什么是幂等? 幂等性:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致. 二.使用幂等的场景 1.前端重复提交 用户注册,用户创建商品等操作,前端都会提交一些数据给 ...
- Java声明定义抽象类_接口_继承_实现
文章目录 声明定义抽象类 声明定义接口 派生类.抽象类.接口的继承要点 声明定义抽象类 public abstract class CRMSystem {public abstract Client ...
- java接口测试框架搭建_接口自动化测试框架搭建
一.原理及特点 参数放在XML文件中进行管理 用httpClient简单封装一个httpUtils工具类 测试用例管理使用了testNg管理,使用了TestNG参数化测试,通过xml文件来执行case ...
- java幂等_幂等性学习及接口的幂等性
幂等性学习 一:什么是幂等性 在这里需要有以下几个问题需要注意: 1:幂等性的实质是一次或多次请求同一个资源,其结果是相同的.其关注的是对资源产生的影响(副作用)而不是结果,结果可以不同.比如列表查询 ...
- Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解
说明:Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解,实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果.例如: 前端重复提交选中的数据,应该后台只产生对应这 ...
- Java接口幂等性多种解决方案
Java接口幂等性的解决方案: java 语音中,同一个接口相同的参数多次和一次请求产生的效果是一样,这样的过程即被称为满足幂等性 //这中情况无论执行多少次,结果都不受影响,是幂等的. update ...
- redis 判断存在性_实战 | springboot+redis+拦截器 实现接口幂等性校验
来源:https://www.jianshu.com/p/6189275403ed 一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能 ...
- java幂等性原理_Java接口幂等性设计原理解析
在微服务架构下,我们在完成一个订单流程时经常遇到下面的场景: 一个订单创建接口,第一次调用超时了,然后调用方重试了一次 在订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次 当这笔订 ...
- java中接口幂等性解决方案总结
一.概念 一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同.幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数.这些函数不会影响系统状态,也不用担心重复执行 ...
最新文章
- 相册权限_苹果手机惊现漏洞?App在未获取相册权限的情况下成功读取照片
- android sharedpreferences工具类
- 排序算法java 简书_手撕算法---常见排序算法java实现
- java简单的事务处理_JAVA之JDBC简单事务处理
- vue.js 添加 fastclick的支持
- Kafka Streams 剖析
- javascript循环事件只响应最后一次的问题处理
- 凸优化笔记3(大M法)
- 虚拟化服务器安装方法,Citrix Xenserver:7.0虚拟化服务器安装详细图文教程
- mysql error 1213_webgame中Mysql Deadlock ERROR 1213 (40001)错误的排查历程
- oracle 父子级,oracle 父子关系
- CentOS 5.5安装 bluefish
- c basic library framework - simplec 2.0.0
- Dell戴尔笔记本电脑灵越Inspiron 5580原装出厂Windows10系统恢复原厂oem系统
- 老调重弹——你存储的密码做Hash了吗?
- (附源码)ssm体育课堂管理系统 毕业设计181626
- 微型计算机认证部件,节能产品认证-微型计算机用开关电源
- Java项目第11期-宠物医院管理系统【毕业设计】
- 用c语言实现简单的关机代码
- 某word文档 编辑很卡的可能原因