当前位置:首页 > 数码 > SpringBoot-Redis-处置海量重复提交疑问的片面指南 (springboot启动)

SpringBoot-Redis-处置海量重复提交疑问的片面指南 (springboot启动)

admin8个月前 (04-26)数码55

前言:

在实践的开发名目中,一个对外泄露的接口往往会面临很屡次恳求,咱们来解释一下幂等的概念: 恣意屡次口头所发生的影响均与一次性口头的影响相反 。依照这个含意,最终的含意就是对数据库的影响只能是一次性性的,不能重复处置。如何保障其幂等性,通常有以下手腕:

redis成功智能幂等的原理图:

图片

一:搭建redis的服务Api

1:首先是搭建redis主机。

2:引入springboot中到的redis的stater,或许Spring封装的jedis也可以,前面关键用到的api就是它的set方法和exists方法,这里咱们经常使用springboot的封装好的redisTemplate

/***redis工具类*/@ComponentpublicclassRedisService{@AutowiredprivateRedisTemplateredisTemplate;/***写入缓存*@paramkey*@paramvalue*@return*/publicbooleanset(finalStringkey,Objectvalue){booleanresult=false;try{ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();operations.set(key,value);result=true;}catch(Exceptione){e.printStackTrace();}returnresult;}/***写入缓存设置时效期间*@paramkey*@paramvalue*@return*/publicbooleansetEx(finalStringkey,Objectvalue,LongexpireTime){booleanresult=false;try{ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();operations.set(key,value);redisTemplate.expire(key,expireTime,TimeUnit.SECONDS);result=true;}catch(Exceptione){e.printStackTrace();}returnresult;}/***判别缓存中能否有对应的value*@paramkey*@return*/publicbooleanexists(finalStringkey){returnredisTemplate.hasKey(key);}/***读取缓存*@paramkey*@return*/publicObjectget(finalStringkey){Objectresult=null;ValueOperations<Serializable,Object>operations=redisTemplate.opsForValue();result=operations.get(key);returnresult;}/***删除对应的value*@paramkey*/publicbooleanremove(finalStringkey){if(exists(key)){Booleandelete=redisTemplate.delete(key);returndelete;}returnfalse;}}

二:自定义注解AutoIdempotent

自定义一个注解,定义此注解的关键目标是把它参与在须要成功幂等的方法上,凡是某个方法注解了它,都会成功智能幂等。后盾应用反射假设扫描到这个注解,就会处置这个方法成功智能幂等,经常使用元注解ElementType.METHOD示意它只能放在方法上,etentionPolicy.RUNTIME示意它在运转时

@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public@interfaceAutoIdempotent{}

三:token创立和测验

1:token服务接口

咱们新建一个接口,创立token服务,外面关键是两个方法,一个用来创立token,一个用来验证token。创立token关键发生的是一个字符串,测验token的话关键是传达request对象,为什么要传request对象呢?关键作用就是失掉header外面的token,而后测验,经过抛出的Exception来失掉详细的报错消息前往给前端

publicinterfaceTokenService{/***创立token*@return*/publicStringcreateToken();/***测验token*@paramrequest*@return*/publicbooleancheckToken(HttpServletRequestrequest)throwsException;}

2:token的服务虚现类

处置海量重复提交疑问的片面指南

token援用了redis服务,创立token驳回随机算法工具类生成随机uuid字符串,而后放入到redis中(为了防止数据的冗余保管,这里设置过时期间为10000秒,详细可视业务而定),假设放入成功,最后前往这个token值。checkToken方法就是从header中失掉token到值(假设header中拿不到,就从paramter中失掉),如若不存在,间接抛出意外。这个意外消息可以被阻拦器捕捉到,而后前往给前端。

@ServicepublicclassTokenServiceImplimplementsTokenService{@AutowiredprivateRedisServiceredisService;/***创立token**@return*/@OverridepublicStringcreateToken(){Stringstr=RandomUtil.randomUUID();StrBuildertoken=newStrBuilder();try{token.end(Constant.Redis.TOKEN_PREFIX).append(str);redisService.setEx(token.toString(),token.toString(),10000L);booleannotEmpty=StrUtil.isNotEmpty(token.toString());if(notEmpty){returntoken.toString();}}catch(Exceptionex){ex.printStackTrace();}returnnull;}/***测验token**@paramrequest*@return*/@OverridepublicbooleancheckToken(HttpServletRequestrequest)throwsException{Stringtoken=request.getHeader(Constant.TOKEN_NAME);if(StrUtil.isBlank(token)){//header中不存在tokentoken=request.getParameter(Constant.TOKEN_NAME);if(StrUtil.isBlank(token)){//parameter中也不存在tokenthrownewServiceException(Constant.ResponseCode.ILLEGAL_ARGUMENT,100);}}if(!redisService.exists(token)){thrownewServiceException(Constant.ResponseCode.REPETITIVE_OPERATION,200);}booleanremove=redisService.remove(token);if(!remove){thrownewServiceException(Constant.ResponseCode.REPETITIVE_OPERATION,200);}returntrue;}}

四:阻拦器的性能

1:web性能类,成功WebMvcConfigurerAdapter,关键作用就是参与autoIdempotentInterceptor到性能类中,这样咱们到阻拦器能力失效,留意经常使用@Configuration注解,这样在容器启动是时刻就可以参与进入context中

@ConfigurationpublicclassWebConfigurationextendsWebMvcConfigurerAdapter{@ResourceprivateAutoIdempotentInterceptorautoIdempotentInterceptor;/***参与阻拦器*@paramregistry*/@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(autoIdempotentInterceptor);super.addInterceptors(registry);}}

2:阻拦处置器:关键的性能是阻拦扫描到AutoIdempotent到注解到方法,而后调用tokenService的checkToken()方法校验token能否正确,假设捕捉到意外就将意外消息渲染成json前往给前端

/***阻拦器*/@ComponentpublicclassAutoIdempotentInterceptorimplementsHandlerInterceptor{@AutowiredprivateTokenServicetokenService;/***预处置**@paramrequest*@paramresponse*@paramhandler*@return*@throwsException*/@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!(handlerinstanceofHandlerMethod)){returntrue;}HandlerMethodhandlerMethod=(HandlerMethod)handler;Methodmethod=handlerMethod.getMethod();//被ApiIdempotment标志的扫描AutoIdempotentmethodAnnotation=method.getAnnotation(AutoIdempotent.class);if(methodAnnotation!=null){try{returntokenService.checkToken(request);//幂等性校验,校验经过则放行,校验失败则抛出意外,并经过一致意外处置前往友好揭示}catch(Exceptionex){ResultVofledResult=ResultVo.getFailedResult(101,ex.getMessage());writeReturnJson(response,JSONUtil.toJsonStr(failedResult));throwex;}}//肯定前往true,否则会被阻拦一切恳求returntrue;}@OverridepublicvoidpostHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,ModelAndViewmodelAndView)throwsException{}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{}/***前往的json值*@paramresponse*@paramjson*@throwsException*/privatevoidwriteReturnJson(HttpServletResponseresponse,Stringjson)throwsException{PrintWriterwriter=null;response.setCharacterEncoding("UTF-8");response.setContentType("text/;charset=utf-8");try{writer=response.getWriter();writer.print(json);}catch(IOExceptione){}finally{if(writer!=null)writer.close();}}}

五:测试用例

1:模拟业务恳求类

首先咱们须要经过/get/token门路经过getToken()方法去失掉详细的token,而后咱们调用testIdempotence方法,这个方法上方注解了@AutoIdempotent,阻拦器会阻拦一切的恳求,当判别四处置的方法上方有该注解的时刻,就会调用TokenService中的checkToken()方法,假设捕捉到意外会将意外抛出调用者,上方咱们来模拟恳求一下:

@RestControllerpublicclassBusinessController{@ResourceprivateTokenServicetokenService;@ResourceprivateTestServicetestService;@PostMapping("/get/token")publicStringgetToken(){Stringtoken=tokenService.createToken();if(StrUtil.isNotEmpty(token)){ResultVoresultVo=newResultVo();resultVo.setCode(Constant.code_success);resultVo.setMessage(Constant.SUCCESS);resultVo.setData(token);returnJSONUtil.toJsonStr(resultVo);}returnStrUtil.EMPTY;}@AutoIdempotent@PostMapping("/test/Idempotence")publicStringtestIdempotence(){StringbusinessResult=testService.testIdempotence();if(StrUtil.isNotEmpty(businessResult)){ResultVosuccessResult=ResultVo.getSuccessResult(businessResult);returnJSONUtil.toJsonStr(successResult);}returnStrUtil.EMPTY;}}

2:经常使用postman恳求

首先访问get/token门路失掉到详细到token:

应用失掉到到token,而后放到详细恳求到header中,可以看到第一次性恳求成功,接着咱们恳求第二次:

第二次恳求,前往到是重复性操作,可见重复性验证经过,再屡次恳求到时刻咱们只让其第一次性成功,第二次就是失败:

六:总结

本篇博客引见了经常使用springboot和阻拦器、redis来优雅的成功接口幂等,关于幂等在实践的开发环节中是十分关键的,由于一个接口或许会被有数的客户端调用,如何保障其不影响后盾的业务处置,如何保障其只影响数据一次性是十分关键的,它可以防止发生脏数据或许乱数据,也可以缩小并发量,实乃十分有益的一件事。而传统的做法是每次判别数据,这种做法不够智能化和智能化,比拟费事。而当天的这种智能化处置也可以优化程序的伸缩性。


Springboot 使用AOP实现防止接口重复提交

在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端。 页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。 思路没有问题,但是需要前后端都稍加改动,如果在业务开发完再加这个的话,改动量未免有些大了。 无需前端配合,纯后端处理,是最清爽的。 设计思路如下: 自定义注解@RreventReSubmit标记所有Controller中的提交请求。 通过AOP 对所有标记@RreventReSubmit的方法拦截。 在业务方法执行前,获取当前用户的 token(或者JSessionId)+ 当前请求地址,作为一个唯一 KEY,去获取 Redis 分布式锁(如果此时并发获取,只有一个线程会成功获取锁)。 当有请求调用接口时,到redis中查找相应的key,如果能找到,则说明重复提交,如果找不到,则执行操作。 业务方法执行后,释放锁。 切面类需要使用@Aspect和@Component这两个注解做标注。 在想要防止重复提交的接口上添加注解即可使用。

SpringBoot进阶之缓存中间件Redis

大家好,一直以来我都本着 用最通俗的话理解核心的知识点, 我认为所有的难点都离不开「基础知识」 的铺垫

「大佬可以绕过 ~」

本节给大家讲讲「Java的SpringBoot框架」 , 之前我们学习的都是java的基础知识和底层提供的一些能力,我们日常工作都是在写接口。在我们在产品开发中,一般我们都会选择比较稳定的框架来帮我们加速开发,不会自己去造轮子,而在java众多框架中,spring框架表现的非常好,大部分公司都会首选它作为开发框架,而至今,大部分企业都是以springboot 来构建项目了,一个稳健的系统需要引入稳定的技术~

如果你是一路看过来的,很高兴你能够耐心看完。前几期都是带大家学习了 SpringBoot 的基础使用以及集成mybatis 开发,这也是我们写业务的基础,如果你还不熟悉这些,请先看完它们。接下来的几期内容将会带大家进阶使用,会先讲解基础中间件 的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有收获 ,本期将会给大家讲解最热门的缓存中间件技术Redis ,同样的,我们集成到Springboot 中。最近github可能会被墙,所以我把源码放到了国内gitee上,本节我们依然使用上期的代码

Redis 是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。全称叫Remote Dictionary Server(远程数据服务) 是由C语言 编写的,Redis是一个key-value 存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash 。

它本质上是一种键值对数据库,我们之前学习的 mysql 它是持久层的关系型数据库,而redis 它的存储主要存在内存 中。我们都知道在内存 中的数据读取是非常快的,就好比你把一个变量存到磁盘读取和直接放到代码中运行,肯定是在代码中拿到的速度快,因为运行时期,都是直接存到内存的。

给大家总结一下:

有了基本的概念之后,我们下面进行环境搭建,在学习阶段,安装 redis 很简单,生产环境一般我们也会选择云产品,一切为了服务保障,虽说它只是做缓存用,但也是系统的一把保护伞

如果你是 mac 用户,你可以运行如下命令:

安装完成后会提示你运行命令,运行即可。

win 用户也很简单,直接下载redis 软件,双击运行即可,运行之后它会有一个小方块的图案,和locahost:6379 的log,说明运行成功了。初始阶段没有配置的redis 默认host 就是本地,port 就是6379 , 而且是没有密码 就可以访问的。

推荐一个客户端软件 Redis Desktop Manager ,它是redis 的客户端界面软件,方便面我们学习的时候清理缓存 使用,生产慎连。

我们不给大家讲它的基本命令使用,它也有语法,可以通过类似命令执行,如果想学习的小伙伴,可以自行搜索。本期重点内容是在 sprinboot 中的使用,我们平时开发不可能是去命令行里敲的,都是代码里执行,而目前市面上有很多封装好的库,我们可以直接调用它的方法,很方便的就可以操作它了,不用记一些繁琐的命令,下面我们就实际操作一下:

修改

修改 :

redis 默认是有16 个库,不是15 个啊,从0 开始算的,我们随便连一个

通过代码很好理解, 首先需要引入 StringRedisTemplate ,然后需要设置一个key ,那么思考一下,这个key 允许重复吗

我们进客户端看一下,发现 key 还是只有一个,但是值变成了新的值了,所以可以得知key 是唯一的,我们重新设置的时候相当于刷新了它。

在 redis 中删除缓存有两种方式,一种是自我消亡,也就是过期 销毁,还有有一种是主动 销毁,我们先看一下,过期时间如何设置

我们设置了 10s 后过期,过完10s后发现,这个```key data``消失了。我们在看看如何主动删除

我们可以利用 Redis 做一个计数器,实现自增功能,你可以用它做网站访问统计

通常做法,我们会把它封装一下,后续使用直接引入封装好的即可,把它直接交给 Springboot容器 管理

其实这个类,你还可以继续进一步封装,比如约束 key 的规范,约束过期时间,约束数据类型等等,这一切也都是为了规范和后期维护,防止滥用缓存

缓存的主要场景是用于解决热点数据问题,因为这些数据是访问频率比较高的,当大量的请求进来, mysql 可能压力很大,这样一来,数据查询效率就很慢,用户肯不高兴等了,这样用户体验很不好。所以我们一般做法,都是把这些热点数据放到缓存里,因为缓存读取速度很快。当有新数据的时候,我们再及时更新它,一般流程是先查询缓存,查到了直接返回缓存数据,查不到再走数据库,然后再刷回缓存。

但是并发足够大的时候,还是会暴露出很多问题,比如面试常问的一些高频问题 缓存雪崩、缓存穿透、缓存雪崩 ,这些问题后边会给大家专门讲,和如何去防范。所以总的来说,引入任何一门技术并不是万事大吉,还需我们不断的在实践中积累经验

本期到这里就结束了,总结一下,我们了解了什么是 redis ,以及在springboot 中如何去使用它们,很简单,没什么复杂的东西。但这里想多说一点的是,缓存的设计却是很复杂的,因为工具是死的,人是活的,我们如何正确设计,需要我们在项目中不断的积累。

我们之前教大家查询列表数据,都是所有数据返回,还没有教大家如何去做分页,下期将带大家学习一下 mybatis 分页插件的使用 ,下期不见不散, 关注我,不迷路~

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: SpringBoot

“SpringBoot-Redis-处置海量重复提交疑问的片面指南 (springboot启动)” 的相关文章

揭秘其强大功能背后的秘密-深入剖析SpringBoot底层原理 (揭秘其强大功夫的成语)

揭秘其强大功能背后的秘密-深入剖析SpringBoot底层原理 (揭秘其强大功夫的成语)

SpringBoot原理详解 简介 SpringBoot是一个开源框架,它简化了Java应用程序开发,特别是Web应用的开发。通过使用SpringBoot,开发者可以轻松创建和配置Spring应...

SpringBoot接口参数校验N种适用技巧大揭秘 (springernature)

SpringBoot接口参数校验N种适用技巧大揭秘 (springernature)

环境:SpringBoot2.6.12 实践的开发上班中大部分的接口都是须要启动参数有效性校验的,参数或者是便捷的基本数据类型,也或者是对象类型,基本上一切接纳参数的接口都是须要对这些参数启...

Boot的Java全栈项目-我在前端编写基于Spring (boot的jar包无法启动)

Boot的Java全栈项目-我在前端编写基于Spring (boot的jar包无法启动)

前言 本文将使用 NestJs + Sequelize + MySQL 完成基础运行,带大家了解 Node 服务端的基础搭建,同时也会对比 Java SpringBoot 项目的基础结构。...

SpringBoot-中的热部署和热加载 (springernature)

SpringBoot-中的热部署和热加载 (springernature)

在SpringBoot开发调试中,假设咱们每修正一行代码都须要重启调试,或许会比拟耗时。 SpringBoot团队针对这个疑问提供了spring-boot-devtools(简称:Devto...

资深开发者的机密战略-SpringBoot中经常使用ObjectMapper的终极指南 (资深开发者的工作内容)

资深开发者的机密战略-SpringBoot中经常使用ObjectMapper的终极指南 (资深开发者的工作内容)

1.每次new一个 在SpringBoot名目中要成功对象与Json字符串的互转,每次都须要像如下一样new一个ObjectMer对象: publicUserEntitystring2Ob...

SpringBoot-虚构线程的强强联结-接口吞吐量直线回升-效率飙升体验惊艳! (springboot)

SpringBoot-虚构线程的强强联结-接口吞吐量直线回升-效率飙升体验惊艳! (springboot)

在这篇博客中,咱们将看到如何在spring-boot中应用loom虚构线程。咱们还将在JMeter的协助下做一些负载测试,看看虚构线程和普通线程的照应期间如何。 首先,虚构线程是Projec...

深入理解其工作原理和最佳实践-SpringBoot的配置加载机制 (深入理解yii2)

深入理解其工作原理和最佳实践-SpringBoot的配置加载机制 (深入理解yii2)

概述 SpringBoot 的配置加载机制基于 Environment 的属性源管理,它提供了一种非常灵活的方式来管理应用程序的配置信息。Environment 支持多种配置格式,可以根据不同...