redisson看门狗锁及RedissonLockUtil

文章详细描述了如何使用Redisson库在Java中实现分布式锁功能,包括获取、尝试获取、解锁等操作,以及不同方法的参数含义和应用场景。
public void updateHordeAdvertClickNum(HordeAdvertInfoRequest request) {
        String lockKey = "HORDE_ADVERT_CLICK_KEY:" + request.getAdvertId();
        RLock rLock = redisson.getLock(lockKey);
        try {
            boolean lock = rLock.tryLock(2L, TimeUnit.SECONDS);
            if (lock) {
                HordeAdvertInfoEntity entity = hordeAdvertInfoMapper.findById(request.getAdvertId());
                MongoUpdateWrapper wrapper = new MongoUpdateWrapper();
                Integer clickNum = entity.getClickNum()==null?0:entity.getClickNum();
                wrapper.set(HordeAdvertInfoEntity::getClickNum, clickNum+1);
                wrapper.and(HordeAdvertInfoEntity::getId).eq(request.getAdvertId());
                hordeAdvertInfoMapper.update(wrapper);
            } else {
                throw BusinessException.newParamsException("获取锁失败!");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            rLock.unlock();
        }
    }



​
public void test() throws Exception{
        RLock lock = redissonClient.getLock("guodong");    // 拿锁失败时会不停的重试
        
        // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s
        lock.lock();
         
        // 具有Watch Dog 自动延期机制 默认续30s
        // 尝试拿锁10s后停止重试,返回false 具有Watch Dog 自动延期机制 默认续30s
        boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); 
       
       // 没有Watch Dog 
       // 尝试获取锁10秒,如果获取不到则放弃
        lock.lock(10, TimeUnit.SECONDS);
       
       // 没有Watch Dog 
       // 尝试获取锁,等待100秒,持有锁10秒钟
        boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);
        
        
        Thread.sleep(40000L);
       
        
        lock.unlock();
    }

​
package com.flhy.redis.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author zengqingyu
 * @Desc: 分布式锁工具类
 */
@Component
@Slf4j
public class RedissonLockUtil {

    @Resource
    private RedissonClient client;


    private final static String SPLIT = "/";

    /**
     * 超时时间15秒(占有锁的最长时间)
     */
    private final static Integer TIMEOUT = 15;


    /**
     * 获取业务锁
     *
     * @param prefix     业务号锁前缀
     * @param businessNo 业务号
     */
    public void lock(String prefix, String businessNo) {
        RLock rLock = client.getLock(prefix + SPLIT + businessNo);
        rLock.lock(TIMEOUT, TimeUnit.SECONDS);
    }

    /**
     * 解锁
     *
     * @param prefix     业务号锁前缀
     * @param businessNo 业务号
     */
    public void unlock(String prefix, String businessNo) {
        RLock rLock = client.getLock(prefix + SPLIT + businessNo);
        try {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        } catch (IllegalMonitorStateException e) {
            log.error("RedissonLockUtil lock timeout unlock", e);
        }
    }


    /**
     * 尝试获取分布式锁,带有尝试获取锁的等待时间 和 占有锁之后的释放时间
     *
     * @param prefix     业务号前缀
     * @param businessNo 业务号
     * @param waitTime   这个参数决定了在尝试获取锁时的最长等待时间。当你的程序尝试获取锁时,如果锁已经被其他进程或线程持有,它会等待一段时间,直到锁变为可用状态。
     *                   waitTime就是这个等待时间的上限。如果在这段时间内锁没有被释放,则获取锁的尝试会失败。这个参数有助于防止在锁资源上的长时间阻塞。
     * @param leaseTime  这个参数表示一旦获取了锁之后,锁将会被保持多长时间。在leaseTime期满后,锁会自动被释放。
     *                   这是一种安全措施,以防止某个进程或线程在持有锁之后忘记释放它,或因为崩溃而无法释放,从而导致其他线程或进程永远无法获取这个锁。
     *                   设置合理的leaseTime可以确保即使出现这样的情况,锁最终也会被释放,从而使得其他进程能够获取锁。
     * @return 是否抢锁成功,true:抢锁成功,false: 抢锁失败
     */
    public boolean tryLockWithWaitTimeAndLeaseTime(String prefix, String businessNo, int waitTime, int leaseTime) {
        RLock lock = client.getLock(prefix + SPLIT + businessNo);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }


    /**
     * 尝试获取分布式锁,带有尝试获取锁的等待时间
     *
     * @param prefix     业务号前缀
     * @param businessNo 业务号
     * @param waitTime   使用场景:例如,抽奖,在1秒内若该用户没有抢锁成功,则给用户返回系统繁忙,请稍后再试的文案, 单位:秒
     *                   这个参数决定了在尝试获取锁时的最长等待时间。当你的程序尝试获取锁时,如果锁已经被其他进程或线程持有,它会等待一段时间,直到锁变为可用状态。
     *                   waitTime就是这个等待时间的上限。如果在这段时间内锁没有被释放,则获取锁的尝试会失败。这个参数有助于防止在锁资源上的长时间阻塞。
     * @return 是否抢锁成功, true: 抢锁成功, false: 抢锁失败
     */
    public boolean tryLockWithWaitTime(String prefix, String businessNo, int waitTime) {
        RLock lock = client.getLock(prefix + SPLIT + businessNo);
        try {
            return lock.tryLock(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }

    /**
     * 尝试获取分布式锁 抢锁失败后,不会等待,立即返回成功或失败
     *
     * @param prefix     业务号前缀
     * @param businessNo 业务号,
     * @return 使用场景:例如,抽奖,若该用户没有抢锁成功,则立即给返回用户系统繁忙,请稍后再试的文案
     * 是否抢锁成功, true: 抢锁成功, false: 抢锁失败
     */
    public boolean tryLock(String prefix, String businessNo) {
        RLock lock = client.getLock(prefix + SPLIT + businessNo);
        return lock.tryLock();
    }

}

Redisson看门狗(Watchdog)机制是为了确保在持有期间不会因为超时而被释放,特别是在分布式环境中,网络延迟或任务执行时间不确定的情况下,这一机制非常有用。然而,在某些情况下,如果任务执行时间过长,且看门狗持续续,可能会导致无法释放,从而引发死问题。 为了避免 Redisson 看门狗机制导致的死问题,可以采取以下几种策略: ### 1. 合理设置的超时时间 在获取时,可以明确指定的最大持有时间(lease time),这样即使看门狗未能及时续也会在指定时间后自动释放。这种方式可以有效防止死的发生。 ```java RLock lock = redisson.getLock("myLock"); // 设置的最大持有时间为10秒 boolean isLocked = lock.tryLock(0, 10, TimeUnit.SECONDS); ``` ### 2. 关闭看门狗机制 如果你的应用场景中不需要自动续功能,或者你希望自行管理的生命周期,可以通过配置关闭看门狗机制。在 Redisson 的配置中设置 `lockWatchdogTimeout` 为 0 即可禁用看门狗。 ```yaml singleServerConfig: lockWatchdogTimeout: 0 ``` ### 3. 使用异步任务处理长时间操作 如果某个任务确实需要较长时间执行,建议将其放入异步任务队列中处理,而不是直接在持有的线程中执行。这样可以避免被长时间占用,减少死的可能性。 ### 4. 异常处理与超时控制 在获取时,可以设置等待时间(wait time),如果在指定时间内无法获取,则放弃获取,避免无限期等待。此外,在任务执行过程中,应加入异常处理逻辑,确保即使发生错误也能及时释放。 ```java RLock lock = redisson.getLock("myLock"); // 等待时间为5秒,的最大持有时间为10秒 boolean isLocked = lock.tryLock(5, 10, TimeUnit.SECONDS); ``` ### 5. 使用 Redisson 的 `leaseTime` 参数 在调用 `tryLock` 方法时,可以传入 `leaseTime` 参数来指定的自动释放时间。即使看门狗没有续也会在该时间后自动释放,从而避免死。 ```java RLock lock = redisson.getLock("myLock"); // 等待时间为5秒,的最大持有时间为20秒 boolean isLocked = lock.tryLock(5, 20, TimeUnit.SECONDS); ``` ### 6. 优化业务逻辑 尽量减少的持有时间,确保在获取后尽快完成关键操作并释放。可以通过优化业务逻辑、拆分任务或使用缓存等方式来减少的持有时间。 ### 7. 使用 Redisson 的 `forceUnlock` 方法 在某些特殊情况下,如果确认某个已经不再需要,可以使用 `forceUnlock` 方法强制释放,即使该仍在有效期内。 ```java RLock lock = redisson.getLock("myLock"); lock.forceUnlock(); ``` 通过以上方法,可以有效地避免 Redisson 看门狗机制导致的死问题,确保分布式的安全性和可靠性[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值