当前位置: 首页 > news >正文

网站改版申请广告营销公司

网站改版申请,广告营销公司,软件开发费,门店装修设计一、什么是幂等性 简单来说,就是对一个接口执行重复的多次请求,与一次请求所产生的结果是相同的,听起来非常容易理解,但要真正的在系统中要始终保持这个目标,是需要很严谨的设计的,在实际的生产环境下&…

一、什么是幂等性

简单来说,就是对一个接口执行重复的多次请求,与一次请求所产生的结果是相同的,听起来非常容易理解,但要真正的在系统中要始终保持这个目标,是需要很严谨的设计的,在实际的生产环境下,我们应该保证任何接口都是幂等的,而如何正确的实现幂等,就是本文要讨论的内容。

二、哪些请求天生就是幂等的

首先,我们要知道查询类的请求一般都是天然幂等的,除此之外,删除请求在大多数情况下也是幂等的,但是ABA场景下除外。

举一个简单的例子

比如,先请求了一次删除A的操作,但由于响应超时,又自动请求了一次删除A的操作,如果在两次请求之间,又插入了一次A,而实际上新插入的这一次A,是不应该被删除的,这就是ABA问题,不过,在大多数业务场景中,ABA问题都是可以忽略的。

除了查询和删除之外,还有更新操作,同样的更新操作在大多数场景下也是天然幂等的,其例外是也会存在ABA的问题,更重要的是,比如执行update table set a = a + 1 where v = 1这样的更新就非幂等了。

最后,就还剩插入了,插入大多数情况下都是非幂等的,除非是利用数据库唯一索引来保证数据不会重复产生。

三、为什么需要幂等

1.超时重试

当发起一次RPC请求时,难免会因为网络不稳定而导致请求失败,一般遇到这样的问题我们希望能够重新请求一次,正常情况下没有问题,但有时请求实际上已经发出去了,只是在请求响应时网络异常或者超时,此时,请求方如果再重新发起一次请求,那被请求方就需要保证幂等了。

2.异步回调

异步回调是提升系统接口吞吐量的一种常用方式,很明显,此类接口一定是需要保证幂等性的。

3.消息队列

现在常用的消息队列框架,比如:Kafka、RocketMQ、RabbitMQ在消息传递时都会采取At least once原则(也就是至少一次原则,在消息传递时,不允许丢消息,但是允许有重复的消息),既然消息队列不保证不会出现重复的消息,那消费者自然要保证处理逻辑的幂等性了。

四、实现幂等的关键因素

关键因素1

幂等唯一标识,可以叫它幂等号或者幂等令牌或者全局ID,总之就是客户端与服务端一次请求时的唯一标识,一般情况下由客户端来生成,也可以让第三方来统一分配。

关键因素2

有了唯一标识以后,服务端只需要确保这个唯一标识只被使用一次即可,一种常见的方式就是利用数据库的唯一索引。

五、注解实现幂等性

  1.  定义DistributedLock注解

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DistributedLock {/*** 保证业务接口的key的唯一性,否则失去了分布式锁的意义 锁key* 支持使用spEl表达式*/String key();/*** 保证业务接口的key的唯一性,否则失去了分布式锁的意义 锁key 前缀*/String keyPrefix() default "";/*** 是否在等待时间内获取锁,如果在等待时间内无法获取到锁,则返回失败*/boolean tryLok() default false;/*** 获取锁的最大尝试时间 ,会尝试tryTime时间获取锁,在该时间内获取成功则返回,否则抛出获取锁超时异常,tryLok=true时,该值必须大于0。**/long tryTime() default 0;/*** 加锁的时间,超过这个时间后锁便自动解锁*/long lockTime() default 30;/*** tryTime 和 lockTime的时间单位*/TimeUnit unit() default TimeUnit.SECONDS;/*** 是否公平锁,false:非公平锁,true:公平锁*/boolean fair() default false;
    }
  2. 定义DistributedLockAspect Lock切面

    @Aspect
    @Slf4j
    public class DistributedLockAspect {@Resourceprivate IDistributedLock distributedLock;/*** SpEL表达式解析*/private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();/*** 用于获取方法参数名字*/private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();@Pointcut("@annotation(com.yt.bi.common.redis.distributedlok.annotation.DistributedLock)")public void distributorLock() {}@Around("distributorLock()")public Object around(ProceedingJoinPoint pjp) throws Throwable {// 获取DistributedLockDistributedLock distributedLock = this.getDistributedLock(pjp);// 获取 lockKeyString lockKey = this.getLockKey(pjp, distributedLock);ILock lockObj = null;try {// 加锁,tryLok = true,并且tryTime > 0时,尝试获取锁,获取不到超时异常if (distributedLock.tryLok()) {if(distributedLock.tryTime() <= 0){throw new IdempotencyException("tryTime must be greater than 0");}lockObj = this.distributedLock.tryLock(lockKey, distributedLock.tryTime(), distributedLock.lockTime(), distributedLock.unit(), distributedLock.fair());} else {lockObj = this.distributedLock.lock(lockKey, distributedLock.lockTime(), distributedLock.unit(), distributedLock.fair());}if (Objects.isNull(lockObj)) {throw new IdempotencyException("Duplicate request for method still in process");}return pjp.proceed();} catch (Exception e) {throw e;} finally {// 解锁this.unLock(lockObj);}}/*** @param pjp* @return* @throws NoSuchMethodException*/private DistributedLock getDistributedLock(ProceedingJoinPoint pjp) throws NoSuchMethodException {String methodName = pjp.getSignature().getName();Class clazz = pjp.getTarget().getClass();Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();Method lockMethod = clazz.getMethod(methodName, par);DistributedLock distributedLock = lockMethod.getAnnotation(DistributedLock.class);return distributedLock;}/*** 解锁** @param lockObj*/private void unLock(ILock lockObj) {if (Objects.isNull(lockObj)) {return;}try {this.distributedLock.unLock(lockObj);} catch (Exception e) {log.error("分布式锁解锁异常", e);}}/*** 获取 lockKey** @param pjp* @param distributedLock* @return*/private String getLockKey(ProceedingJoinPoint pjp, DistributedLock distributedLock) {String lockKey = distributedLock.key();String keyPrefix = distributedLock.keyPrefix();if (StringUtils.isBlank(lockKey)) {throw new IdempotencyException("Lok key cannot be empty");}if (lockKey.contains("#")) {this.checkSpEL(lockKey);MethodSignature methodSignature = (MethodSignature) pjp.getSignature();// 获取方法参数值Object[] args = pjp.getArgs();lockKey = getValBySpEL(lockKey, methodSignature, args);}lockKey = StringUtils.isBlank(keyPrefix) ? lockKey : keyPrefix + lockKey;return lockKey;}/*** 解析spEL表达式** @param spEL* @param methodSignature* @param args* @return*/private String getValBySpEL(String spEL, MethodSignature methodSignature, Object[] args) {// 获取方法形参名数组String[] paramNames = nameDiscoverer.getParameterNames(methodSignature.getMethod());if (paramNames == null || paramNames.length < 1) {throw new IdempotencyException("Lok key cannot be empty");}Expression expression = spelExpressionParser.parseExpression(spEL);// spring的表达式上下文对象EvaluationContext context = new StandardEvaluationContext();// 给上下文赋值for (int i = 0; i < args.length; i++) {context.setVariable(paramNames[i], args[i]);}return expression.getValue(context).toString();}/*** SpEL 表达式校验** @param spEL* @return*/private void checkSpEL(String spEL) {try {ExpressionParser parser = new SpelExpressionParser();parser.parseExpression(spEL, new TemplateParserContext());} catch (Exception e) {log.error("spEL表达式解析异常", e);throw new IdempotencyException("Invalid SpEL expression [" + spEL + "]");}}
    }
  3. 定义分布式锁注解版启动元注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({DistributedLockAspect.class})
    public @interface EnableDistributedLock {
    }
  4. 定义IDistributedLock分布式锁接口

    public interface IDistributedLock {/*** 获取锁,默认30秒失效,失败一直等待直到获取锁** @param key 锁的key* @return 锁对象*/ILock lock(String key);/*** 获取锁,失败一直等待直到获取锁** @param key      锁的key* @param lockTime 加锁的时间,超过这个时间后锁便自动解锁; 如果lockTime为-1,则保持锁定直到显式解锁* @param unit     {@code lockTime} 参数的时间单位* @param fair     是否公平锁* @return 锁对象*/ILock lock(String key, long lockTime, TimeUnit unit, boolean fair);/*** 尝试获取锁,30秒获取不到超时异常,锁默认30秒失效** @param key     锁的key* @param tryTime 获取锁的最大尝试时间* @return* @throws Exception*/ILock tryLock(String key, long tryTime) throws Exception;/*** 尝试获取锁,获取不到超时异常** @param key      锁的key* @param tryTime  获取锁的最大尝试时间* @param lockTime 加锁的时间* @param unit     {@code tryTime @code lockTime} 参数的时间单位* @param fair     是否公平锁* @return* @throws Exception*/ILock tryLock(String key, long tryTime, long lockTime, TimeUnit unit, boolean fair) throws Exception;/*** 解锁** @param lock* @throws Exception*/void unLock(Object lock);/*** 释放锁** @param lock* @throws Exception*/default void unLock(ILock lock) {if (lock != null) {unLock(lock.getLock());}}}
  5. IDistributedLock实现类

    @Slf4j
    @Component
    public class RedissonDistributedLock implements IDistributedLock {@Resourceprivate RedissonClient redissonClient;/*** 统一前缀*/@Value("${redisson.lock.prefix:bi:distributed:lock}")private String prefix;@Overridepublic ILock lock(String key) {return this.lock(key, 0L, TimeUnit.SECONDS, false);}@Overridepublic ILock lock(String key, long lockTime, TimeUnit unit, boolean fair) {RLock lock = getLock(key, fair);// 获取锁,失败一直等待,直到获取锁,不支持自动续期if (lockTime > 0L) {lock.lock(lockTime, unit);} else {// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30slock.lock();}return new ILock(lock, this);}@Overridepublic ILock tryLock(String key, long tryTime) throws Exception {return this.tryLock(key, tryTime, 0L, TimeUnit.SECONDS, false);}@Overridepublic ILock tryLock(String key, long tryTime, long lockTime, TimeUnit unit, boolean fair)throws Exception {RLock lock = getLock(key, fair);boolean lockAcquired;// 尝试获取锁,获取不到超时异常,不支持自动续期if (lockTime > 0L) {lockAcquired = lock.tryLock(tryTime, lockTime, unit);} else {// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30slockAcquired = lock.tryLock(tryTime, unit);}if (lockAcquired) {return new ILock(lock, this);}return null;}/*** 获取锁** @param key* @param fair* @return*/private RLock getLock(String key, boolean fair) {RLock lock;String lockKey = prefix + ":" + key;if (fair) {// 获取公平锁lock = redissonClient.getFairLock(lockKey);} else {// 获取普通锁lock = redissonClient.getLock(lockKey);}return lock;}@Overridepublic void unLock(Object lock) {if (!(lock instanceof RLock)) {throw new IllegalArgumentException("Invalid lock object");}RLock rLock = (RLock) lock;if (rLock.isLocked()) {try {rLock.unlock();} catch (IllegalMonitorStateException e) {log.error("释放分布式锁异常", e);}}}
    }
  6. 定义ILock锁对象

    import lombok.AllArgsConstructor;
    import lombok.Getter;import java.util.Objects;/*** <p>* RedissonLock 包装的锁对象 实现AutoCloseable接口,在java7的try(with resource)语法,不用显示调用close方法* </p>* @since 2023-06-08 16:57*/
    @AllArgsConstructor
    public class ILock implements AutoCloseable {/*** 持有的锁对象*/@Getterprivate Object lock;/*** 分布式锁接口*/@Getterprivate IDistributedLock distributedLock;@Overridepublic void close() throws Exception {if(Objects.nonNull(lock)){distributedLock.unLock(lock);}}
    }

六、使用示例 

 启动类添加@EnableDistributedLock启用注解支持

@SpringBootApplication
@EnableDistributedLock
public class BiCenterGoodsApplication {public static void main(String[] args) {SpringApplication.run(BiCenterGoodsApplication.class, args);}
}

@DistributedLock标注需要使用分布式锁的方法 

    @ApiOperation("编辑SKU供应商供货信息")@PostMapping("/editSupplierInfo")//@DistributedLock(key = "#dto.sku + '-' + #dto.skuId", lockTime = 10L, keyPrefix = "sku-")@DistributedLock(key = "#dto.sku", lockTime = 10L, keyPrefix = "sku-")public R<Boolean> editSupplierInfo(@RequestBody @Validated ProductSkuSupplierInfoDTO dto) {return R.ok(productSkuSupplierMeasureService.editSupplierInfo(dto));}
#dto.sku是 SpEL表达式。Spring中支持的它都支持的。比如调用静态方法,三目表达式。SpEL 可以使用方法中的任何参数。SpEL表达式参考

从原理到实践,分析 Redis 分布式锁的多种实现方案(一)

从原理到实践,分析 Redisson 分布式锁的实现方案(二)

Spring Boot 集成 Redisson分布式锁


参考文章:一个注解,优雅的实现接口幂等性! 


文章转载自:
http://paronychia.fzLk.cn
http://cerium.fzLk.cn
http://electrocution.fzLk.cn
http://coowner.fzLk.cn
http://filthify.fzLk.cn
http://judgmatical.fzLk.cn
http://masonite.fzLk.cn
http://abate.fzLk.cn
http://yearling.fzLk.cn
http://plagioclase.fzLk.cn
http://apa.fzLk.cn
http://yomp.fzLk.cn
http://eluviate.fzLk.cn
http://redhead.fzLk.cn
http://blate.fzLk.cn
http://catalyzer.fzLk.cn
http://quality.fzLk.cn
http://tyrannize.fzLk.cn
http://sobeit.fzLk.cn
http://tapa.fzLk.cn
http://northwards.fzLk.cn
http://kennebec.fzLk.cn
http://chukchi.fzLk.cn
http://sandglass.fzLk.cn
http://orbiter.fzLk.cn
http://misdemeanor.fzLk.cn
http://putrescibility.fzLk.cn
http://fiz.fzLk.cn
http://coprecipitation.fzLk.cn
http://forecastleman.fzLk.cn
http://fructifier.fzLk.cn
http://moppie.fzLk.cn
http://examples.fzLk.cn
http://paleocrystic.fzLk.cn
http://unsaid.fzLk.cn
http://nonbusiness.fzLk.cn
http://availablein.fzLk.cn
http://installment.fzLk.cn
http://microfluorometry.fzLk.cn
http://bugle.fzLk.cn
http://disestablishmentarian.fzLk.cn
http://respire.fzLk.cn
http://marage.fzLk.cn
http://aspheric.fzLk.cn
http://rhodolite.fzLk.cn
http://rheme.fzLk.cn
http://sootily.fzLk.cn
http://caitiff.fzLk.cn
http://intellectuality.fzLk.cn
http://incipience.fzLk.cn
http://cloven.fzLk.cn
http://urban.fzLk.cn
http://terylene.fzLk.cn
http://dicephalous.fzLk.cn
http://petrify.fzLk.cn
http://apsidiole.fzLk.cn
http://rgt.fzLk.cn
http://academical.fzLk.cn
http://bandh.fzLk.cn
http://precordial.fzLk.cn
http://paul.fzLk.cn
http://proprieter.fzLk.cn
http://evidential.fzLk.cn
http://aare.fzLk.cn
http://balefire.fzLk.cn
http://misknowledge.fzLk.cn
http://sided.fzLk.cn
http://expectative.fzLk.cn
http://thetatron.fzLk.cn
http://recalcitration.fzLk.cn
http://natalist.fzLk.cn
http://unsuitability.fzLk.cn
http://dulotic.fzLk.cn
http://crackers.fzLk.cn
http://oligodendroglia.fzLk.cn
http://prothalamion.fzLk.cn
http://mishmash.fzLk.cn
http://sibilate.fzLk.cn
http://oilcup.fzLk.cn
http://dome.fzLk.cn
http://reflexly.fzLk.cn
http://imperscriptible.fzLk.cn
http://retroussage.fzLk.cn
http://cassis.fzLk.cn
http://mew.fzLk.cn
http://opencast.fzLk.cn
http://delaware.fzLk.cn
http://thiobacteria.fzLk.cn
http://begats.fzLk.cn
http://gateleg.fzLk.cn
http://inerasable.fzLk.cn
http://sphygmophone.fzLk.cn
http://vrd.fzLk.cn
http://reimprison.fzLk.cn
http://sump.fzLk.cn
http://farewell.fzLk.cn
http://uglify.fzLk.cn
http://audiometrically.fzLk.cn
http://bracteate.fzLk.cn
http://precompiler.fzLk.cn
http://www.dt0577.cn/news/92984.html

相关文章:

  • 深圳坪山网站制作公司网络营销策划总结
  • 天津公司网站推广企业qq
  • 网站制作的常见问题百度推广有哪些形式
  • 长沙有哪个学校可以学网站建设网站制作模板
  • 企业建设网站作用设计师网站
  • 兰山网站建设公司广告推广平台网站有哪些
  • 达建网站的需要6个好用的bt种子搜索引擎
  • 平顶山建设局网站谷歌浏览器安卓下载
  • 更换网站域名 推广国际足联世界排名
  • 火星建站免费wap自助建站软件发布网
  • 电商平台图片素材济南seo培训
  • 做数据结构基础的网站成免费crm软件有哪些优点
  • 单页面网站制作技术外包公司有哪些
  • 深圳布吉做网站搜索关键词是什么意思
  • 视频网站用什么cms凡科建站怎么导出网页
  • wordpress用插件备份台州网站seo
  • 代理ip做网站流量新媒体运营培训
  • wordpress主题修改seo还可以做哪些推广
  • php网站怎么做测试工具西安百度推广优化托管
  • 域名注册和网站哪个好广告竞价推广
  • 站内搜索本网站怎么做上海抖音seo
  • 泗阳做网站的怎么开一个网站平台
  • 做标书需要用到哪些网站查资料seo优化分析
  • 网站建设缺乏个性地推平台去哪里找
  • 怎么在网上做公司的网站搜索引擎优化seo多少钱
  • 权威的大连网站建设北京网站seowyhseo
  • 做期货要关注哪些网站宁波seo排名费用
  • 外贸搜索网站百度推广产品有哪些
  • wordpress 获取插件路径win10必做的优化
  • 郑州便民核酸采样屋正在搭建中360优化大师app下载