注解实现幂等组件分享

easy-idempotent(easy-idempotent: easyIdempotent,一款配置简单,只需要打上注解就可轻松实现幂等性的框架。支持多种幂等标识的存储方式,既支持redis实现的分布式解决方案(推荐),也支持仅单机应用的Local模式(如果只是想写简单的demo,不想配置redis的话)。本框架还设计了两种使用场景,默认为RestApi场景,即用户使用url访问该路径时使用到的幂等性解决方案,也支持MQ场景,即极端情况下消息消费过却重复投递消息的场景。本框架可以由开发人员自主自定义幂等标识的构成,支持3种类型(param,token,spel)任意组合。本框架可以由开发人员自行决定在请求或消息处理结束后是否清除幂等标识(支持spel语法),足以应付各种复杂场景。icon-default.png?t=N7T8https://gitee.com/cai-zhiyuDaKeLe/easy-idempotent

简介

easyIdempotent 一款配置简单,只需要打上注解就可轻松实现幂等性的组件。该组件提供多种存储(支持分布式 和 单机)方式,多种自定义幂等标识的方式,多种场景下的适配使用。打造一个简单而功能丰富的幂等框架。

特性

  1. 配置简单,即插即用

  2. 可基于spel表达式组成幂等标识,可基于spel标识自行决定在方法成功返回后是否删除幂等标识,适应多种复杂场景

  3. 支持redis,如果只想写个简单demo,不想启动redis,也可使用Local存储方式存储幂等标识

安装教程

            <dependency>
                <groupId>io.github.kkkele</groupId>
                <artifactId>easy-idempotent-spring-boot3-starter</artifactId>
                <version>${easy-idempotent.version}</version>  <!--选择版本 (目前最新为1.0.1) (1.0.0的yml配置有bug)-->
            </dependency>

使用说明

  1. application.yml配置 (默认为redis存储)

    --- #配置idempotent 
    idempotent:
      prefix: kkkele  #幂等标识前缀
      using-type: redis #幂等标识存储方式
      enable-log: true #是否开启日志打印
      mq:    #mq场景下
        interval: 600s #默认存储幂等标识的时间,即 如果mq消费成功,则600s内对于同样的幂等标识会采取跳过策略
        message: '消息重复消费' #重复消费默认消息
      rest-api: #使用接口请求场景下
        interval: 5s #默认存储时间,即如果设置了幂等标识自动清理,则在该时间内,只能处理一个请求
        message: '请求正在处理,请勿重复提交' #重复提交默认消息
  2. 在需要幂等标识的地方打上注解@Idempotent

        //默认使用方法参数进行幂等标识的组成    
        @GetMapping("/demo")
        @Idempotent 
        public String test(TestDemo testDemo) {
            // .... 处理方法
            return "success";
        }
  3. 如果需要用户token来组成我们的幂等标识,需要先实现RepeatToken接口,并交给spring容器进行管理

    public class RepeatTokenImpl implements RepeatToken {
        @Override
        public String getToken() {
        //...自定义获取token
            return TokenUtil.getToken();
        }
    }
    @Configuration
    public class IdempotentConfig {
    ​
        @Bean
        public RepeatToken repeatToken() {
            return new RepeatTokenImpl();
        }
    }
    ​
    //------------------或者
    @Compotent
    public class RepeatTokenImpl implements RepeatToken {
        @Override
        public String getToken() {
             //...自定义获取token
            return TokenUtil.getToken();
        }
    }
  4. 完全使用示例

        //完全配置
        @GetMapping("/demo2")
        @Idempotent(type = {IdempotentType.PARAM,IdempotentType.SPEL,IdempotentType.TOKEN},
                scene = IdempotentScene.RESTAPI,
                spelKey = "#testDemo.id",
                interval = 5,
                timeUnit = TimeUnit.MINUTES,
                clean = "#result != null")
        public TestDemo test2(TestDemo testDemo){
            // .... 处理方法
            return testDemo;
        }

高级使用

  1. 自定义清理策略

        @GetMapping("/demo2")
        @Idempotent(type = IdempotentType.PARAM,
                interval = 500,
                timeUnit = TimeUnit.MINUTES,
                clean = "#result.id != null")
        public TestDemo test2(TestDemo testDemo){
            // .... 处理方法
            return testDemo;
        }

    demo演示

    GET http://localhost:8080/idempotent/demo2

    第一次返回json

    {
      "id": null,
      "title": null,
      "content": null,
      "loginUser": null
    }

    5分钟内返回json

    {
      "code": 500,
      "msg": "请求正在处理,请勿重复提交",
      "data": null
    }

    更改url

    GET http://localhost:8080/idempotent/demo2?id=1

    第一次返回json

    {
      "id": 1,
      "title": null,
      "content": null,
      "loginUser": null
    }

    5分钟内返回json

    {
      "id": 1,
      "title": null,
      "content": null,
      "loginUser": null
    }

日志打印信息

2.使用spel表达式自定义幂等标识的组成

    @GetMapping("/demo3")
    @Idempotent(type = IdempotentType.SPEL,
            spelKey = "#testDemo.id + '_' + #testDemo.title",
            interval = 600,
            timeUnit = TimeUnit.MINUTES,
            clean = "false")
    public TestDemo test3(TestDemo testDemo){
        return testDemo;
    }

demo演示

GET http://localhost:8080/idempotent/demo3?id=2&title=testdemo3

多次请求日志打印情况

image-20231119135650547

3.混合使用幂等标识type,自定义幂等标识

//token实现类
public class RepeatTokenImpl implements RepeatToken {
    @Override
    public String getToken() {
        return "123456";
    }
}

//测试接口
    @GetMapping("/demo4")
    @Idempotent(type = {IdempotentType.PARAM,IdempotentType.TOKEN},
            interval = 600,
            timeUnit = TimeUnit.MINUTES,
            clean = "false")
    public TestDemo test4(TestDemo testDemo){
        return testDemo;
    }

日志打印情况

image-20231119140132038

  1. mq场景下使用 (清理策略不适用于Mq场景)

    @Slf4j
    @Component
    @RequiredArgsConstructor
    @RocketMQMessageListener(
            topic = "test_test-demo_topic",
            consumerGroup = "test_test-demo_cg"
    )
    public class TestDemoConsumer implements RocketMQListener<MessageWrapper<TestDemo>> {
        @Override
        @Idempotent(scene = IdempotentScene.MQ,type = IdempotentType.SPEL,spelKey = "#wrapper.uuid + #wrapper.keys")
        public void onMessage(MessageWrapper<TestDemo> wrapper) {
            if (new Random().nextBoolean()){
                log.error("消费失败");
                throw new RuntimeException();
            }
            System.out.printf("消费成功:[%s]\n",wrapper);
        }
    }
    
    
    //发送消息
        @PostMapping("/produce")
        public void produce() {
            produce.sendResult(new TestDemo(1l, "test-title", null, null));
        }
    

    模拟发送

    POST http://localhost:8080/idempotent/produce

    日志信息

    image-20231119143557118

    image-20231119143618298

可以看到,在消息消费失败后,仍然可以再次消费,以保证mq能够正常工作

本组件在消息消费成功后,会将mq场景下的幂等标识对应的值改为 1

image-20231119143748522

这样,如果发生了极端情况的重复消费,本组件会跳过之后的消费来保证mq场景下的幂等性

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值