范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

实战教程使用自定义注解实现幂等性

  文章目录一、什么是幂等性?
  简单来说,就是对一个接口执行重复的多次请求,与一次请求所产生的结果是相同的,听起来非常容易理解,但要真正的在系统中要始终保持这个目标,是需要很严谨的设计的,在实际的生产环境下,我们应该保证任何接口都是幂等的,而如何正确的实现幂等,就是本文要讨论的内容。  二、哪些请求天生就是幂等的?
  首先,我们要知道查询类的请求一般都是天然幂等的,除此之外,删除请求在大多数情况下也是幂等的,但是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
  有了唯一标识以后,服务端只需要确保这个唯一标识只被使用一次即可,一种常见的方式就是利用数据库的唯一索引。  五、注解实现幂等性
  下面演示一种利用Redis来实现的方式。  1.自定义注解import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent {        /**      * 参数名,表示将从哪个参数中获取属性值。      * 获取到的属性值将作为KEY。      *      * @return      */     String name() default "";      /**      * 属性,表示将获取哪个属性的值。      *      * @return      */     String field() default "";      /**      * 参数类型      *      * @return      */     Class type();  }2.统一的请求入参对象@Data public class RequestData {        private Header header;      private T body;  }   @Data public class Header {        private String token;  }  @Data public class Order {        String orderNo;  }3.AOP处理import com.springboot.micrometer.annotation.Idempotent; import com.springboot.micrometer.entity.RequestData; import com.springboot.micrometer.idempotent.RedisIdempotentStorage; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import java.lang.reflect.Method; import java.util.Map;  @Aspect @Component public class IdempotentAspect {        @Resource     private RedisIdempotentStorage redisIdempotentStorage;      @Pointcut("@annotation(com.springboot.micrometer.annotation.Idempotent)")     public void idempotent() {       }      @Around("idempotent()")     public Object methodAround(ProceedingJoinPoint joinPoint) throws Throwable {           MethodSignature signature = (MethodSignature) joinPoint.getSignature();         Method method = signature.getMethod();         Idempotent idempotent = method.getAnnotation(Idempotent.class);          String field = idempotent.field();         String name = idempotent.name();         Class clazzType = idempotent.type();          String token = "";          Object object = clazzType.newInstance();         Map paramValue = AopUtils.getParamValue(joinPoint);         if (object instanceof RequestData) {               RequestData idempotentEntity = (RequestData) paramValue.get(name);             token = String.valueOf(AopUtils.getFieldValue(idempotentEntity.getHeader(), field));         }          if (redisIdempotentStorage.delete(token)) {               return joinPoint.proceed();         }         return "重复请求";     } }4.Token值生成import com.springboot.micrometer.idempotent.RedisIdempotentStorage; import com.springboot.micrometer.util.IdGeneratorUtil; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  import javax.annotation.Resource;  @RestController @RequestMapping("/idGenerator") public class IdGeneratorController {        @Resource     private RedisIdempotentStorage redisIdempotentStorage;      @RequestMapping("/getIdGeneratorToken")     public String getIdGeneratorToken() {           String generateId = IdGeneratorUtil.generateId();         redisIdempotentStorage.save(generateId);         return generateId;     }  }public interface IdempotentStorage {        void save(String idempotentId);      boolean delete(String idempotentId); }import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import java.io.Serializable; import java.util.concurrent.TimeUnit;  @Component public class RedisIdempotentStorage implements IdempotentStorage {        @Resource     private RedisTemplate redisTemplate;      @Override     public void save(String idempotentId) {           redisTemplate.opsForValue().set(idempotentId, idempotentId, 10, TimeUnit.MINUTES);     }      @Override     public boolean delete(String idempotentId) {           return redisTemplate.delete(idempotentId);     } }import java.util.UUID;  public class IdGeneratorUtil {        public static String generateId() {           return UUID.randomUUID().toString();     }  }5. 请求示例
  调用接口之前,先申请一个token,然后带着服务端返回的token值,再去请求。  import com.springboot.micrometer.annotation.Idempotent; import com.springboot.micrometer.entity.Order; import com.springboot.micrometer.entity.RequestData; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  @RestController @RequestMapping("/order") public class OrderController {        @RequestMapping("/saveOrder")     @Idempotent(name = "requestData", type = RequestData.class, field = "token")     public String saveOrder(@RequestBody RequestData requestData) {           return "success";     }  }
  请求获取token值。
  带着token值,第一次请求成功。
  第二次请求失败。

今年的双十一静悄悄12年前,为刺激线上消费,阿里打造了双十一这个电商购物节。双十一自此成为中国乃至全世界一年一次的商业盛会。这场盛会在近几年都会以一场众星云集的晚会开场,然后以数千亿成交额的商业奇迹今年双11,新消费熄火了吗?这可能是史上变化最大的一届双11,阿里今年取消了双11GMV大屏。24点,阿里依旧公布了双11总GMV数字5403亿,但显然数字已经不是关注的重点。阿里创造了双11GMV大屏这种形中国再次创造奇迹,中欧班列订单暴涨3倍,新加坡为何心急火燎?中欧班列已经开通过近十年,随着两地关系合作联系更为密切,如今的中欧班列订单暴涨了三倍,让许多人见之眼红,其中就有新加坡,直呼自己的饭碗快要保不住了。毕竟中国之前大都通过海运和欧洲国四川首富不再是刘永好,新首富身价1250亿,被称为中国饲料大王文有鱼审核子扬校对知秋提及四川首富,不少人都会在第一时间想到新希望集团的董事长刘永好。资料介绍,刘永好是新希望集团的董事长,而新希望则是我国最大的饲料生产企业之一,同时还拥有国内最原始股骗局所有公开向大众发售原始股的都是骗局,没有例外!一心想着暴富的石女士还是没能禁住诱惑,她决定发展自己的老公和儿子一起参加华尔街之路项目。丈夫了解到这个项目的具体内容和盈利方式后,立即赶往公安局报案。随后长沙警方在全国多地展开第二古诗词最美白话古诗词最美的情话1原文我超级想你古风文山河远阔,人间星河,无一是你,无一不是你2原文我非常爱你古风文血染江山的画,怎敌你眉间一点朱砂,负了天下也罢,始终不过一场繁华3原文我失去你了最坏不过余生都是回忆秋日好时光最好不过余生只有你,最坏不过余生都是回忆,你可曾想过就这样孤独不行。曾听一枚枫叶说过,你喜欢它的飘逸。但我知道,落叶的那一瞬间,已经完全与我无关。叶不是无情物,化作春泥更看完上甘岭战报后,蒋介石沉默良久,说出15字评价1946年蒋介石撕毁双十协定,解放战争正式拉开序幕。经过三年的旷世大战,人民解放军势如破竹,百万国民党军队兵败如山倒,解放军一路直捣,南京国民政府瞬间土崩瓦解。当中华大地已无老将的八国联军侵华照片曝光,从下水道爬进京城,疯狂程度令人发指1900年,中华大地爆发了声势浩大,打着扶清灭洋旗号的义和团运动。八国借口保护本国使馆为名,阴谋组织联军攻陷天津,炮轰北京城门,慈禧挟光绪丢下大清子民慌忙西逃,北京沦陷,八国疯狂洗师徒重逢!郎平携魏秋月拍微电影,对视笑开花,郎导戴墨镜气色好11月12日消息,从女排主帅职位上卸任的郎平于今日亮相,拍摄了微电影,爱徒魏秋月也亮相助阵。师徒两人重逢,戴着墨镜现身的郎导笑容满面,气色很好。在郎平第二次执教中国女排的过程中,携只要大白菜上有黑点的,都不能吃,要转给家人朋友?不妨看看真相从穷苦日子过来的人,总能记得来自生活上的教训,即使物质生活慢慢好了,生活还是要继续。比如一到冬天,就有不少人会囤积白菜萝卜苹果。可就在最近有人发现一种不太寻常的现象,为什么这几年购
折叠屏iPhone太冒险!苹果或于2024年先推出折叠屏iPad从今年iPhone14ProMax手机AirPodsPro2的火爆程度,以及历年来iPadAppleWatch等产品在各自领域的市场表现来看,苹果基本上都做到了业内顶尖对于国内一众苹果macOS13Ventura正式版将于10月25日推送IT之家10月19日消息,苹果官网更新信息显示,macOS13Ventura正式版将于10月25日推出,与iPadOS16正式版推送时间相同。macOSVentura带来了台前调度音响知识音响升级容易陷入的三大误区,你避开了吗?玩音响是兴趣是折腾。当烧友的眼界与硬件条件丰富了后,就会考虑升级音响的事情。这是一个由浅入深由俭入奢的漫长过程,需要付出大量的精力体力和金钱,这样才能真切地感受到玩音响的乐趣,这正一起看TV既能直播又能点播,双播电视应用新体验安卓系统不仅仅存在于我们的手机之中,一般现在家里的智能电视系统基本也都是安卓的操作系统,它可以让用户们按照自己的需求来下载并安装第三方的电视应用,开放性很好。从电视应用种类上来说,男单首轮焦点!王楚钦遭遇世界第5,00后劲敌碰上世界冠军WTT澳门冠军赛男单签表揭晓,国乒世界第1樊振东所在的14区强敌不多,有望直通半决赛。比较有悬念的3场比赛,林高远和梁靖崑不得不在首轮就要内耗一人出局,00后的中华台北名将林昀儒在人到苏州必有为丨刷新世界,玩转高科技微纳印刷膜法视频加载中如今的手机,不只是拼性能拼价格,还要拼颜值。昆山一家企业,在手机后盖上加上一层纳米印刷装饰膜,就能让它呈现出流光溢彩的效果。这些五颜六色的手机盖板,看似金属抛光而成,其实联想局中局丨人类失去联想,世界将会怎样?文搜扬管理咨询有限责任公司董事总经理仲景本文共9993字,预计阅读时间30分钟人类失去联想,世界将会怎样?这句曾经响彻神州,影响了一代人的广告语,如今看来,更像是一种对联想命运的诘Re从零开始的异世界生活无限和同伴一起打败幕后黑手Re从零开始的异世界生活无限是一款由天相互动旗下的红茶兔工作室开发的IP改编卡牌收集手游。游戏改编为自动绘制从零开始的异世界生活。游戏延续了动画的风格。画风比较好看,人物刻画非常还重达300多克拉的世界上最大无瑕钻石,预计售价至少为1300万英镑世界上最大的无暇钻石将以至少1300万英镑的价格出售。重量超过300克拉的梨形金丝雀GoldenCanary,昨天在迪拜展出。20世纪80年代,一个小女孩在她叔叔的花园里玩耍时,在恭喜孔令辉!收获世界乒联点名祝福,参与乒乓球推广,初心未变北京时间10月18日,前国乒名将孔令辉迎来了自己47岁的生日,在这重要的人生时刻,WTT世界乒联并没有忘记这位曾经为国际乒乓球运动的发展作出过贡献的乒坛巨星。WTT世界乒联也是在第韩媒乐疯了!狂吹孙兴慜别提亚洲最佳,他已经是世界级球员2022年金球奖排名揭晓,韩国前锋孙兴慜列第11位,刷新了他在3年前创下的亚洲球员在金球奖历史上的最高排名。韩国媒体也纷纷聚焦此事,首尔体育更是直接吹爆孙兴慜,称亚洲最佳这个称号对