Redis实现全局锁

        引论:Redis可以非常好地为各个微服务引用提供一个公共的数据交换空间,但是多个客户端(微服务应用)同时访问一个公共数据时,难免会相互竞争导致混乱。

         为了避免这一种情况发生,程序在访问数据之前先获取一个全局锁,以确保该数据在这一段时间内只允许有一个应用在操作,当操作完成后在释放锁.

         Redis的setnx命令天生适合用来实现锁的功能,这个命令只有在键不存在的情况下才能为键设置。获取锁之后,其他程序在设置值就会失败,即获取不到锁。获取锁失败,只需要不断地尝试获取锁,直到成功获取锁,或者到设置的超时时间为止。另外为了防止死锁,即某个程序获取锁之后,程序出错而没有释放,其他程序无法获取锁,从而导致整个分布式系统无法获取锁以至于引起一系列问题,甚至导致系统无法正常运行。这时需要给锁设置一超时时间,即setex命令,锁超时后,其他程序就可以获取锁了.

 

全局锁代码如下:

package org.jy.data.yh.bigdata.platform.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * redis实现全局锁
 */
@Component
public class RedisDistributedLockHandler {

    public final static long LOCK_EXPIRE = 30 * 1000L;
    public final static long LOCK_TRY_INTERVAL = 30L;
    public final static long LOCK_TRY_TIMEOUT= 20 *1000L;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 处理锁的逻辑,首先根据锁的Key值判断是否存在,如果不存在则返回true,
     * 并设置一个带有失效性的锁;当检测到锁存在时表示当前时间段内已经加锁,无法继续操作
     * 等待一端时间后再次判断key是否存在,在规定的时间内检测到锁一直存在,则直接返回false,
     * 表示当时资源已被锁住.
     * @param key
     * @param value
     * @return
     */
    public boolean getLock(String key,String value){
        try{
            if(StringUtils.isEmpty(key) || StringUtils.isEmpty(value)){
                return false;
            }
            long startTime = System.currentTimeMillis();
            do {
                if (!redisTemplate.hasKey(key)) {
                    redisTemplate.opsForValue().set(key, value, LOCK_EXPIRE, TimeUnit.MICROSECONDS);
                    return true;
                }
                if (System.currentTimeMillis() - startTime > LOCK_TRY_TIMEOUT) {
                    return false;
                }

                Thread.sleep(LOCK_TRY_INTERVAL);
            }while(redisTemplate.hasKey(key));
        } catch (InterruptedException e) {
                    e.printStackTrace();
                    return false;
        }
        return false;
    }

    /**
     * 显示释放掉指定的锁资源
     * @param key
     */
    public void releaseLock(String key){
        if (!StringUtils.isEmpty(key)){
            redisTemplate.delete(key);
        }
    }

}

 

测试代码如下:

package org.jy.data.yh.bigdata.platform.controller;

import org.jy.data.yh.bigdata.platform.service.RedisDistributedLockHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisDistributedLockController {

    @Autowired
    RedisDistributedLockHandler redisDistributedLockHandler;

    @RequestMapping("/getRedisLock")
    public String testLockRelease() throws InterruptedException {
        StringBuilder sb = new StringBuilder();

        // 初次尝试并未锁住,将获得锁
        String result01 = redisDistributedLockHandler.getLock("key","value") ? "获得到锁" : "被锁住";
        System.out.println(sb.append(result01).toString());
        sb.append("-->");
        // 再次尝试发现已经得到锁
        String result02 = redisDistributedLockHandler.getLock("key","value") ? "获得到锁":"被锁住";
        System.out.println(sb.append(result02).toString());
        sb.append("-->");
        // 模拟锁超时
        Thread.sleep(RedisDistributedLockHandler.LOCK_EXPIRE);
        // 再次请求锁已超时,将再次获得锁
        String result03 = redisDistributedLockHandler.getLock("key","value") ? "获得到锁" : "被锁住";
        System.out.println(sb.append(result03).toString());
        // 手动是否锁
        redisDistributedLockHandler.releaseLock("key");

        return sb.toString();
    }
}

 

http://localhost:8811/getRedisLock   查看锁经历的过程

 

 查看锁经历的阶段:

获得到锁-->被锁住-->获得到锁 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值