一,什么是幂等性?

1,幂等:

幂等操作:不管执行多少次,所产生的影响都和一次执行的影响相同。

幂等函数或幂等方法:可以使用相同的参数重复执行,并能获得相同的结果的函数/方法。

这些函数/方法不用担心重复执行会对系统造成改变。

2,幂等操作的一些例子:

前端重复提交相同的数据,后台只产生对应这个数据的一个相同的反应结果

发送验证码短信:应该只发一次,相同的验证短信不能多次发送。

生成订单:一个业务请求只能创建一个订单,不能重复创建相同的订单

用户付款:只能扣用户一次钱,不能重复扣费

3,实现幂等操作的一些方法:

unique索引

悲观锁

乐观锁

token机制

...

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,关于演示代码的说明:

1,项目的原理:

我们这里的演示的是表单提交时要避免重复提交相同的内容,

前端在用户点击提交按钮后,需要在后端返回结果之前,禁止用户再次点击提交按钮,

假如有请求绕过了前端的控制,直接向后端发送重复的相同请求,

后端如何避免?

在用户打开表单时,后端会生成一个token字符串,保存在redis后,传递给表单,

当表单提交时,这个字符串会再次提交到后端接口,

后端接口需要判断这个字符串是否在redis中存在?

如果不存在不允许提交,如果存在删除时能成功删除,允许提交,

删除时报错:表示已被其他进程删除,也不能允许提交.

2,项目在github的地址:

https://github.com/liuhongdi/idempotent

3,代码结构截图:

三,lua代码的说明:

checkidem.lua

local current = redis.call('GET', KEYS[1])
if current == false then
--redis.log(redis.LOG_NOTICE,KEYS[1]..' is nil ')
return '-1'
end
local isdel = redis.call('DEL', KEYS[1])
if isdel == 1 then
--redis.log(redis.LOG_NOTICE,' del '..KEYS[1]..' success')
return '1';
else
--redis.log(redis.LOG_NOTICE,'del '..KEYS[1]..' failed')
return '0';
end

如果当前token在redis中不存在,返回 -1

如果token存在,删除成功,返回1

删除失败,返回0

说明:为什么使用lua脚本?

redis上的lua脚本的执行是原子性的,不存在多个线程的并发问题,

使用lua脚本能保证不会出现重复的提交

四,java代码的说明:

1,RedisLuaUtil

@Service
public class RedisLuaUtil {
@Resource
private StringRedisTemplate stringRedisTemplate; /*
run a lua script
luaFileName: lua file name,no path
keyList: list for redis key
return 0: delete fail
-1: no this key
1: delete success
*/
public String runLuaScript(String luaFileName,List<String> keyList) {
//System.out.println("redis script begin");
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/"+luaFileName)));
redisScript.setResultType(String.class); String argsone = "none";
//System.out.println("execute begin");
String result = stringRedisTemplate.execute(redisScript, keyList,argsone);
System.out.println("lua result:"+result); return result;
}
}

说明:

DefaultRedisScript:负责封装lua脚本

luaFileName: lua文件名

keyList:   redis中的key列表,我们只需要传递token即可

stringRedisTemplate:负责执行脚本

argsone:值参数,我们传一个空字串即可

2,TokenServiceImpl.java中对redisLuaUtil类的调用

    @Override
public void checkToken(HttpServletRequest request) { String token = request.getHeader(TOKEN_NAME); if (StringUtils.isBlank(token)) {// header中不存在token
token = request.getParameter(TOKEN_NAME);
if (StringUtils.isBlank(token)) {// parameter中也不存在token
//System.out.println("-----no token");
throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg());
}
} //System.out.println("runlua begin");
List<String> keyList = new ArrayList();
keyList.add(token);
String res = redisLuaUtil.runLuaScript("checkidem.lua",keyList); if (res.equals("1")) {
ServerResponseUtil.success("success");
} else {
throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
}
}

五,测试幂等性的检测是否生效?

提交表单前,先得到token

访问:

http://127.0.0.1:8080/order/gettoken

返回:

{"status":0,"msg":"8IgWPMtotKyzO13pnxCS9pc4","data":null}

用ab测试表单:

#-c:指定请求的并发数量

#-n:指定请求的总数量

[root@localhost etc]# ab -c 10 -n 10 http://127.0.0.1:8080/order/addorder?form_token=8IgWPMtotKyzO13pnxCS9pc4

查看代码中system.out.println的打印输出:

lua result:1
lua result:-1
lua result:-1
lua result:-1
lua result:-1
lua result:-1
lua result:-1
lua result:-1
lua result:-1
lua result:-1

可以看到只有一个是提交成功,其他的请求均给出了报错

六,查看spring boot的版本:

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.0.RELEASE)

spring boot:用redis+lua实现表单接口的幂等性(spring boot 2.2.0)的更多相关文章

  1. Spring Security默认的用户登录表单 页面源代码

    Spring Security默认的用户登录表单 页面源代码 <html><head><title>Login Page</title></hea ...

  2. spring boot:用redis+lua限制短信验证码的发送频率(spring boot 2.3.2)

    一,为什么要限制短信验证码的发送频率? 1,短信验证码每条短信都有成本制约, 肯定不能被刷接口的乱发 而且接口被刷会影响到用户的体验, 影响服务端的正常访问, 所以既使有图形验证码等的保护, 我们仍然 ...

  3. Spring Boot 2 + Thymeleaf:服务器端表单验证

    表单验证分为前端验证和服务器端验证.服务器端验证方面,Java提供了主要用于数据验证的JSR 303规范,而Hibernate Validator实现了JSR 303规范.项目依赖加入spring-b ...

  4. spring boot学习(7) SpringBoot 之表单验证

    第一节:SpringBoot 之表单验证@Valid 是spring-data-jpa的功能:   下面是添加学生的信息例子,要求姓名不能为空,年龄大于18岁.   贴下代码吧: Student实体: ...

  5. 【Spring学习笔记-MVC-11--】Spring MVC之表单标签

    一.使用方法 1.要使用Spring MVC提供的表单标签,首先需要在视图页面添加: <%@ taglib prefix="form" uri="http://ww ...

  6. spring-security-4 (5)spring security Java配置实现自定义表单认证与授权

    前面三篇讲解了spring security的搭建以及简单的表单认证与授权原理.本篇将实现我们自定义的表单登录与认证.  本篇不会再讲项目的搭建过程,因为跟第二节的搭建如出一辙.本篇也不会将项目中所有 ...

  7. SpringBoot集成Spring Security(4)——自定义表单登录

    通过前面三篇文章,你应该大致了解了 Spring Security 的流程.你应该发现了,真正的 login 请求是由 Spring Security 帮我们处理的,那么我们如何实现自定义表单登录呢, ...

  8. Spring MVC(十四)--SpringMVC验证表单

    在Spring MVC中提供了验证器可以进行服务端校验,所有的验证都必须先注册校验器,不过校验器也是Spring MVC自动加载的,在使用Spring MVC校验器之前首先要下载相关的jar包,下面是 ...

  9. spring mvc Controller与jquery Form表单提交代码demo

    1.JSP表单 <% String basePath = request.getScheme() + "://" + request.getServerName() +&qu ...

随机推荐

  1. sudo提权,ansible批量给所有主机创建系统账户,授权

    sudo(superuser or another do)让普通用户可以以超级管理员或其他人的身份执行命令. sudo基本流程如下: 1.管理员需要先授权(修改/etc/sudoers文件) 2.普通 ...

  2. liunx之firewalld&SELinux

    1.firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status f ...

  3. Linux:less and Aix:more

    在运维工作中,经常要查询应用日志,有Linux和Aix系统,个人感觉,Linux查询日志用less命令比较方便,Aix查询日志用more命令比较方便,在此总结一下两个命令的使用方法 AIX more命 ...

  4. goto 语法在 PHP 中的使用

    在C++.Java及很多语言中,都存在着一个神奇的语法,就是goto.顾名思义,它的使用是直接去到某个地方.从代码的角度来说,也就是直接跳转到指定的地方.PHP中也有这个功能,我们先来看看它是如何使用 ...

  5. Video.js + HLS 在production环境下webpack打包后出错的解决方案

    Video.js是一个非常强大的视频播放库,能在微信下完美提供inline小窗口播放模式,但当涉及到hls格式视频播放时就比较麻烦,出现的数种现象都不好解决. 错误现象:  1.  PC Chrome ...

  6. Linux实战(7):centos7安装xrdp

    系统环境:最小化安装,无安装桌面化 操作 yum更新 yum -y update 安装依赖. tigervnc-server.xrdp .GNOME Desktop yum -y install ep ...

  7. Vue开发电子书app

    ebook-app 在根目录下创建vue.config.js文件 module.export = { baseUrl: process.env.NODE_ENV === 'production' ? ...

  8. ASP.NET Core 3.x启动时运行异步任务(二)

    这一篇是接着前一篇在写的.如果没有看过前一篇文章,建议先去看一下前一篇,这儿是传送门   一.前言 前一篇文章,我们从应用启动时异步运行任务开始,说到了必要性,也说到了几种解决方法,及各自的优缺点.最 ...

  9. 坚果云+svn实现异地非局域网个人代码版本管理

    原理大概是A地的设备作为服务端创建仓库,将仓库传上坚果云,同步到B地,再拉取仓库的代码

  10. (转载)什么是B树?

    本文转载自网络. 如有侵权,请联系处理!