需求
发送短信的接口,高并发的情况下,第一条请求进来,判断redis里近一分钟没有发过短信,还在逻辑处理的时候,第二条请求也进来了,这个时候第一个进程还没提交更改redis缓存中的数据,第二条获取的状态近一分钟内依旧没有发送,所以逻辑判断允许,此处需改进。
场景复现:用JMeter模拟请求短信发送,指定30个线程,每个线程循环3次,间隔0秒
类似场景如购物平台抢购,然后超卖了,不过解决方案还可以用MQ来处理,这里仅处理短信发送校验,又不想引框架,所以通过简易Redis代码方式解决。
实现代码:
//获取redis锁
Boolean islock = redisTemplate.opsForValue().setIfAbsent(smsToken + "-lock", "1", 5, TimeUnit.SECONDS);
if (islock) {
try {
/*--业务逻辑---*/
} catch (Exception e) {
Trace.logError(Trace.COMPONENT_ACTION, e.getMessage(), e);
throw e;
} finally {
//释放锁
String lockId = (String) redisTemplate.opsForValue().get(smsToken + "-lock");
if (lockId != null) {
redisTemplate.delete(smsToken + "-lock");
}
}
} else {
//未获取锁,发送频繁
throw new Exception("短信发送频繁");
}
这样处理的前提是多台服务连接的是同一个redis服务器或者同一个redis集群,才能保证获取到的锁的唯一性。
再次通过JMeter测试复现:
如果业务流程可能会很长,而且超时时间不好确认,又担心锁过期或被其他的线程错误解锁,这里可以做一个优化,将锁的value设置成自定义随机值,然后解锁的时候在判断一次仅解锁自己上的锁,这样就不会被其他线程异常解锁。