写点什么

被 Netty 搞昏了头,先学一下幂等性压压惊吧,java 程序员面试宝典 pdf

用户头像
极客good
关注
发布于: 刚刚

理解


所谓幂等性,就是无论执行多少次,都会只执行一次,多次的影响和一次是一样的,比如我们新增用户,无论执行多少次,都只会保存一次,避免了重复提交


幂等性运用的范围也是很多,比如以下几个:


  • 保存用户信息,前端重复提交相同的数据,后端接口对于这个数据只会保存一次,无论重新提交多少次,也只会完成一次

  • 用户支付,无论提交多少次,他只能有一次成功,只能扣一次钱

  • **验证码,**相同的的验证码只能发送一次,不能重复发送 等等


三、实现幂等性


=======


1、实现的方式


  • mysql 的唯一索引,如果索引存在,就会抛出异常,也就保证了重复提交问题

  • 悲观锁

  • 乐观锁

  • redis 实现,将生成的 token 保存在 redis 中,提交之后,将 token 删除,重复发起请求,获取不到 redis 存储的 token 就无法重复提交,直接提示用户:


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


重复保存


2、redis 和 lua 实现幂等性


我们这里使用 redis 和 Lua 的方式实现,为什么使用 lua?


因为 lua 是原子操作,原子操作从开始就要一直到执行结束,中间不会有线程切换,所以可以解决高并发的问题


2.1 不使用幂等性存在的问题


我们先看一下用户保存操作:


controller


@Log("用户创建")


@ApiOperation(value = "用户创建",notes = "用户创建")


@PostMapping("/createUser")


public GoflyResponse createUser(@Valid UserDto userDto) throws GoflyException {


User user = new User();


BeanUtils.copyProperties(userDto,user);


iUserService.createUser(user);


return new GoflyResponse().code(HttpStatus.OK).message("成功");


}


postman 多次提交用户保存操作


提交三次之后,新增了同样的三个用户,所以这个是错误的,需要修改


| USER_ID | USERNAME | PASSWORD | NAME


| 9 | yangyangyang | fcea920f7412b5da7be0cf42b8c93759 | 杨羽茉


| 10 | yangyangyang | fcea920f7412b5da7be0cf42b8c93759 | 杨羽茉


| 11 | yangyangyang | fcea920f7412b5da7be0cf42b8c93759 | 杨羽茉


2.2 redis 和 lua 解决问题


lua 脚本----> checkidem.lua


放在 resources 目录下


//获取 token----> redis: get token


local current = redis.call('GET',KEYS[1])


//如果不存在返回-1


if current == false then


return '-1'


end


//访问一次之后,删除 token


local isdel = redis.call('DEL',KEYS[1])


//如果删除成功就返回'1'


if isdel == 1 then


return '1'


else


//删除失败,说明 token 不存在


return '0'


end


redis 工具类------> RedisLuaUtil.java


用于运行 lua 脚本


/**


  • redis 解析 lua 工具类


*/


@Component


public class RedisLuaUtil {


//注入 stringRedisTemplate


@Resource


private StringRedisTemplate stringRedisTemplate;


//运行 lua 脚本


public String runLuaScript(String luaFileName, List<String> keys){


DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();


//读取 lua 脚本文件


redisScript.setScriptSource(


new ResourceScriptSource(


new ClassPathResource(luaFileName)


)


);


//lua 返回值类型


redisScript.setResultType(String.class);


//值参数


String argson = "none";


//执行 lua 脚本,获取返回值,lua 返回的内容主要看


String result = stringRedisTemplate.execute(redisScript, keys, argson);


return result;


}


/**


  • 校验 token

  • @param request


*/


public String checkToken(HttpServletRequest request) {


//获取 token,从 request 中获取


String token = request.getParameter("token");


//将 token 存储到 list 中


List<String> keys = new ArrayList<>();


keys.add(token);


//调用运行 lua 脚本的方法,并且传入 lua 文件的名称及 token 集合


String result = this.runLuaScript("checkidem.lua", keys);


//根据自定义的 lua 脚本,会返回三种返回值:


//1. GET token 不存在时返回-1


//2. DEL token 成功时,返回 1


//3. DEL token 不成功,返回 0


if(result.equals("1")){


return "保存成功";


}else{


return "请勿重复提交";


}


}

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
被Netty搞昏了头,先学一下幂等性压压惊吧,java程序员面试宝典pdf