SpringBoot实战 之 RedisTemplate 实现判重计数

431 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

业务背景:

  • 使用Redis分布式缓存的特性:解决新增账号重复调用的问题;
  • 一个用户只能新增一次;

使用的技术有:

  • SpringBoot
  • RedisTemplate
  • commons-pool2连接池

SpringBoot 配置文件

spring: 
	redis:
        database: 10
        timeout: 3000  # redis 同一个session只等待3s
        host: 127.0.0.1
        port: 6379
        password:
    #    cluster:  
    #      nodes: 127.0.0.1  # Redis集群
        lettuce:
          pool:
            max-idle: 10 # 连接池中的最大空闲连接
            min-idle: 5  # 连接池中的最小空闲连接
            max-active: 100 # 连接池最大连接数(使用负值表示没有限制)
            max-wait: 1000  # 连接池最大阻塞等待时间(使用负值表示没有限制)
            
       
  

代码1:先验证key是否存在,根据结果进行新增或停止业务


@RequestMapping("/test_sentinel")
    public void testSentinel() throws InterruptedException {
        String name = "test01";
        Boolean exist = Boolean.FALSE;

        try {
           
            //key是否存在,不存在则记录
            exist = stringRedisTemplate.hasKey(name);
            if (!exist) {
                stringRedisTemplate.opsForValue().set(name, "新增AD账户前,记录用户名", 20, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            System.out.println("调用redis后打印"+System.currentTimeMillis()+";Redis异常打印:" + e);
            return;
        }

        //key已存在,不可重复新增
        if (exist) {
            throw new Http400Exception("UserName-repeat", name + "用户已创建过AD账号,不可重复新增");
        }
    }


代码2:考虑业务执行失败,redis 数据回滚问题;加入redis事务


@RequestMapping("/test_sentinel")
    public void testSentinel() throws InterruptedException {
        String name = "test01";
        Boolean exist = Boolean.FALSE;

        try {
            System.out.println("调用redis前打印:"+System.currentTimeMillis());
            //key是否存在,不存在则记录
            exist = stringRedisTemplate.hasKey(name);
            if (!exist) {
            
                //开启事务
                stringRedisTemplate.setEnableTransactionSupport(true);
                stringRedisTemplate.multi();

                stringRedisTemplate.opsForValue().set(name, "新增AD账户前,记录用户名", 20, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            System.out.println("调用redis后打印"+System.currentTimeMillis()+";Redis异常打印:" + e);
        }

        //key已存在,不可重复新增
        if (exist) {
            throw new Http400Exception("UserName-repeat", name + "用户已创建过AD账号,不可重复新增");
        }


        //业务代码执行
        try {
//            int a =  1/0;
            System.out.println("执行业务代码...");
            Thread.sleep(5000);
        } catch (Exception e) {
            System.out.println("业务代码执行异常,Redis开始回滚,时间戳打印:"+System.currentTimeMillis()+";异常打印:" + e);
            //开启回滚
            stringRedisTemplate.discard();
            return;
        }


        //提交
        stringRedisTemplate.exec();

    }

代码3:使用 increment 命令,实现判重并计数;(推荐使用)

opsForValue().increment

image.png

opsForHash().increment(key,hashKey, 1)

image.png

这两个命令都可以实现,为了后续方便拓展,我们选用 hash

 @RequestMapping("/test_ad")
    public void testAD() throws InterruptedException {
        String name = "test02";
        String key = "ad_"+name;
        String hashKey = "call_num";
        Long codeNum = 0L;

        //key 是否存在
        try {
//          codeNum = stringRedisTemplate.opsForValue().increment(key, 1);
            codeNum = stringRedisTemplate.opsForHash().increment(key,hashKey, 1);
            System.out.println("codeNum打印:"+ codeNum);
            if (1 == codeNum) {
                stringRedisTemplate.expire(key, 3600, TimeUnit.SECONDS);
            }
        } catch (Exception e) {
            System.out.println("异常");
        }

        //key已存在,不可重复新增
        if (1 != codeNum) {
            throw new Http400Exception("UserName-repeat", name + "用户已创建过AD账号,不可重复新增");
        }


    }