在springcloud项目开发中redis分布式锁使用主要有两个场景
1.订单重复提交或支付提交等,防止刷单 
2.对某个业务进行锁定,例如:当用户同一时间,进行对账户充值和提现操作,那么这里需要根据用户ID对账户进行锁定,只有一个完成了才可以进行第二个。
开发实现方式
1.pom.xml中引入jar包,最好引入到基础模块中,其他模块通用
<!-- redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
创建redis操作类RedisGlobalLock(自定义)
redis提供RedisTemplate方法
redis提供三个方法:
(1)lock  获取锁并锁定   本方法是立即获取锁状态,如果获取成功并锁定,如果获取失败
(2)tryLock   尝试获取锁并锁定  本方式是在指定时间尝试获取锁
(3)unlock  释放锁   当业务处理完毕必须释放锁
重点:
lock和tryLock区别:lock是实时获取,tryLock是尝试在一段时间内一直在获取
@Service public class RedisGlobalLock { private static Log log = LogFactory.
getLog(RedisGlobalLock.class); private static final String TYPE_NAME =
RedisGlobalLock.class.getTypeName(); /** 默认30ms尝试一次 */ private final static long
LOCK_TRY_INTERVAL= 30L; /** 默认尝试20s */ private final static long
LOCK_TRY_TIMEOUT= 20 * 1000L; /** 单个业务持有锁的时间30s,防止死锁 */ private final static
longLOCK_EXPIRE = 30 * 1000L; @Autowired private RedisTemplate<String, Object>
redisTemplate; /** * 获取锁 * @param key 锁Key * @return 是否获取锁 */ public boolean
lock(String key) {return getLock(key, 0, LOCK_EXPIRE, TimeUnit.MILLISECONDS); }
/** * 获取锁 * @param key 锁Key * @param expire 有效期 * @param expireUnit 有效期时间单位
*@return 是否获取锁 */ public boolean lock(String key, long expire, TimeUnit
expireUnit) {return getLock(key, 0, expire, expireUnit); } /** * 尝试获取锁 *
@paramkey 锁Key * @return 是否获取锁 */ public boolean tryLock(String key) { return
tryLock(key,LOCK_TRY_TIMEOUT, TimeUnit.MILLISECONDS); } /** * 尝试获取锁 * @param
key锁Key * @param timeout 等待超时时间 * @param unit 等待超时时间单位 * @return 是否获取锁 */
public booleantryLock(String key, long timeout, TimeUnit unit) { // 超时时间转成毫秒
timeout = TimeUnit.MILLISECONDS.convert(timeout, unit); return
getLock(key,timeout,LOCK_EXPIRE, TimeUnit.MILLISECONDS); } /** * 尝试获取锁 *
@paramkey 锁Key * @param timeout 等待超时时间 * @param timeoutUnit 等待超时时间单位 * @param
expire有效期 * @param expireUnit 有效期时间单位 * @return */ public boolean
tryLock(String key,long timeout, TimeUnit timeoutUnit, long expire, TimeUnit
expireUnit) {// 超时时间转成毫秒 timeout = TimeUnit.MILLISECONDS.convert(timeout,
timeoutUnit);return getLock(key,timeout, expire, expireUnit); } /** * 释放锁 *
@paramkey 锁Key */ public void unlock(String key) { key = getPrefix(TYPE_NAME)
+ key; Long oldExpireTime = (Long)redisTemplate.opsForValue().get(key); if(null
!= oldExpireTime && oldExpireTime >= System.currentTimeMillis()) { //
大于过期时间,则删除key redisTemplate.delete(key); } } /** * 获取锁 * @param key 锁键值 *
@paramtimeout 超时时间 * @param time 全局锁生命周期 * @param unit 时间单位 * @return 是否获取到锁
*/ private boolean getLock(String key, long timeout, long time, TimeUnit unit)
{ key = getPrefix(TYPE_NAME) + key; try { long startTimeMillis = System.
currentTimeMillis(); do { long newValue = System.currentTimeMillis() + TimeUnit.
MILLISECONDS.convert(time, unit); Boolean isOk = redisTemplate
.opsForValue().setIfAbsent(key, newValue);if(isOk) { // 获得锁 redisTemplate
.expire(key, time, unit);return true; } // 获取过期时间 Long oldExpireTime = (Long)
redisTemplate.opsForValue().get(key); if(null == oldExpireTime) { oldExpireTime
=0L; } if(oldExpireTime >= System.currentTimeMillis()) { //
不小于系统时间并且过了超时时间,则不获取锁 if((System.currentTimeMillis() - startTimeMillis) >
timeout) {return false; } // 休眠 Thread.sleep(LOCK_TRY_INTERVAL); } // 新的过期时间
longnewExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS
.convert(time, unit); Long currentExpireTime = (Long)redisTemplate
.opsForValue().getAndSet(key, newExpireTime);if(null == currentExpireTime) {
currentExpireTime =0L; } if(currentExpireTime.equals(oldExpireTime)) { // 获取到锁
redisTemplate.expire(key, time, unit); return true; } } while (true); } catch
(Exception e) {return false; } } /** * 获取缓存标识前缀 * @param typeName 类名 *
@return前缀 */ protected final String getPrefix(String typeName) { return
typeName; } }

在业务逻辑层引入redis操作类
@Resource
private RedisGlobalLock redisGlobalLock;

// 1、获取分布式锁防止重复调用 ===================================================== String
key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId; if(redisGlobalLock
.lock(key)) {try{ System.out.println("--处理业务---"); }catch (Exception e){ throw
e; }finally { // 4、释放分布式锁
================================================================ redisGlobalLock
.unlock(key); } }else{ // 如果没有获取锁 Ensure.that(true).isTrue("17000706"); }
所有锁业务必须释放锁,防止死锁
但是以下业务可以不释放锁:
1.定时任务:每日执行一次,或者每个月执行一次,就不需要释放锁,我们要对锁的时间加长。
2.支付场景:
接通易宝支付,首先用户要绑定银行卡,但是绑卡过程中,我们这边要调用易宝支付绑卡接口,如果因网络等原因APP对重复点击没有得到控制,那么会调用后台多次接口,那么直接的结果是:后台调用易宝支付第一次是成功,后台第二次是返回系统异常,但是易宝支付平台照样收取费用,是我们平台没有控制好,易宝支付调用绑卡接口两次时间间隔在40秒以上。


友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信