Redis实现分布式锁通用工具


import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.qunar.flight.qmonitor.QMonitor;
import com.qunar.redis.storage.Sedis;

@Service
public class RedisDistributedLockService {
    private final Logger logger = LoggerFactory.getLogger(RedisDistributedLockService.class);

    // redis的lock的key的最长有效时间
    public static final long LOCK_VALIDITY_TIME = 60 * 1000l;
    // 获取redis集群锁的重试时间
    public static final int ETRY_REDIS_LOCK_MILLIS = 10;

    public static final long LOCK_VALIDITY_ONE_MINUTES = 30 * 1000L;

    public static final int RETRY_MAX_TIMES = 10;// 获取锁 做多重试
    @Resource
    private Sedis sedis;

    /**
     * 获取锁,一直等待到取到锁后返回
     * 
     * @param key
     * @return
     */
    public String getUntilHaveLock(String key) {
        String randomLockValue = computeLockRandomValue();
        int times = 0;
        Long nowT = System.currentTimeMillis();
        Long endT = nowT + LOCK_VALIDITY_ONE_MINUTES;// 超过一分钟 也放弃
        while (true) {
            times++;
            String ret = getLock(key, randomLockValue, LOCK_VALIDITY_TIME);
            if (StringUtils.isBlank(ret)) {
                try {
                    logger.info("waiting redis lock. the key is {}", key);
                    Thread.sleep(ETRY_REDIS_LOCK_MILLIS);
                } catch (InterruptedException e) {
                    logger.error("get lock thread sleep error. {}", e);
                }
            } else {
                QMonitor.recordOne("get_redis_lock");
                return ret;
            }
            
            if (times >= RETRY_MAX_TIMES) { // 超过放弃
                logger.error("获取锁超过重试次数 放弃获取");
                return null;
            }

            if (System.currentTimeMillis() > endT) {
                logger.error("获取锁时间超过截止时间  放弃获取");
                return null;
            }
        }
    }

    /**
     * 获取锁
     * 
     * @param key
     * @param expireMillis 锁过期时间(毫秒)
     * @return
     */
    public String getLock(String key, long expireMillis) {
        return getLock(key, computeLockRandomValue(), expireMillis);
    }

    private String getLock(String key, String randomLockValue, long expireMillis) {
        logger.info("key:{},randomLockValue:{}", key, randomLockValue);
        String ret = sedis.set(key, randomLockValue, "NX", "PX", expireMillis);
        if (StringUtils.isNotEmpty(ret) && ret.equalsIgnoreCase("OK")) {
            QMonitor.recordOne("get_redis_lock");
            return randomLockValue;
        }
        return StringUtils.EMPTY;
    }

    /**
     * 由于get和del不在一个事物里面,所以是存在风险的 但是tc的sedis客户端不支持eval这样的功能,太蛋疼了。
     * 
     * @param key
     * @param value
     * @return
     */
    public int releaseLock(String key, String value) {
        String redisValue = sedis.get(key);
        if (StringUtils.isNotEmpty(redisValue) && redisValue.equalsIgnoreCase(value)) {
            return sedis.del(key).intValue();
        } else {
            QMonitor.recordOne("redis_lock_value_empty");
            logger.error("release redis lock error. redis lock value empty. key is {}, value is {}", key, value);
            return 0;
        }
    }

    private String computeLockRandomValue() {
        String hostname = getHostname();
        if (StringUtils.isNotEmpty(hostname)) {
            return hostname + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();
        } else {
            QMonitor.recordOne("hostname_empty");
            logger.error("hostname is empty");
            return "host" + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();
        }
    }

    private String getHostname() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            logger.error("get local hostname error. the error is {}", e);
        }
        return null;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小雄哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值