本文已参与「新人创作礼」活动,一起开启掘金创作之路。
业务背景:
- 使用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
opsForHash().increment(key,hashKey, 1)
这两个命令都可以实现,为了后续方便拓展,我们选用 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账号,不可重复新增");
}
}