【代码学习】lua+redis分布式锁代码实现实例

分布式锁场景和介绍:

分布式锁演进-基本原理
在这里插入图片描述

在这里插入图片描述
分布式锁演进-阶段一
在这里插入图片描述
分布式锁演进-阶段二
在这里插入图片描述
分布式锁演进-阶段三
在这里插入图片描述
分布式锁演进-阶段四
在这里插入图片描述
分布式锁演进-阶段五
在这里插入图片描述

分布式锁一般有如下的特点:

互斥性: 同一时刻只能有一个线程持有锁
可重入性: 同一节点上的同一个线程如果获取了锁之后能够再次获取锁
锁超时:和J.U.C中的锁一样支持锁超时,防止死锁
高性能和高可用: 加锁和解锁需要高效,同时也需要保证高可用,防止分布式锁失效
具备阻塞和非阻塞性:能够及时从阻塞状态中被唤醒

分布式锁模板类:

/**
 * 分布式锁模板类
 * Created by sunyujia@aliyun.com on 2016/2/23.
 */
public interface DistributedLockTemplate {

    /**
     *
     * @param lockId 锁id(对应业务唯一ID)
     * @param timeout 单位毫秒
     * @param callback 回调函数
     * @return
     */
    public Object execute(String lockId,int timeout,Callback callback);
}

public interface DistributedReentrantLock {
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;

    public void unlock();
}

回调接口:

/**
 * Created by sunyujia@aliyun.com on 2016/2/23.
 */
public interface Callback {

    public Object onGetLock() throws InterruptedException;

    public Object onTimeout() throws InterruptedException;
}

锁实现类


/**
 * Created by sunyujia@aliyun.com on 2016/2/26.
 */
public class RedisDistributedLockTemplate implements DistributedLockTemplate {
    private static final org.slf4j.Logger log = LoggerFactory.getLogger(RedisDistributedLockTemplate.class);

    private JedisPool jedisPool;


    public RedisDistributedLockTemplate(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }



    @Override
    public Object execute(String lockId, int timeout, Callback callback) {
        RedisReentrantLock distributedReentrantLock = null;
        boolean getLock=false;
        try {
            distributedReentrantLock = new RedisReentrantLock(jedisPool,lockId);
            if(distributedReentrantLock.tryLock(new Long(timeout), TimeUnit.MILLISECONDS)){
                getLock=true;
                return callback.onGetLock();
            }else{
                return callback.onTimeout();
            }
        }catch(InterruptedException ex){
            log.error(ex.getMessage(), ex);
            Thread.currentThread().interrupt();
        }catch (Exception e) {
            log.error(e.getMessage(), e);
        }finally {
            if(getLock) {
                distributedReentrantLock.unlock();
            }
        }
        return null;
    }
}


/**
 * Created by sunyujia@aliyun.com on 2016/2/26.
 */
public class HHRedisDistributedLockTemplate implements DistributedLockTemplate {
    private static final org.slf4j.Logger log = LoggerFactory.getLogger(HHRedisDistributedLockTemplate.class);

    private JedisPool jedisPool;
    private Jedis jedis ;

    public HHRedisDistributedLockTemplate(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
        this.jedis = jedisPool.getResource();
    }


    public boolean tryLock(String key, String ran, int timout){
        System.out.println("tryLock key:"+key+"ran:"+ran);
        Long val = jedis.setnx(key, ran);
        System.out.println("tryLock key:"+key+"ran:"+ran+"val:"+val);
        jedis.pexpire(key,timout);

        return  jedis.get(key).equals(ran);
    }

    public boolean unLock(String key, String value){
        if (value.equals(jedis.get(key))){
            jedis.del(key);
            System.out.println("unLock key:"+key+"val:"+value);
        }else{
            jedis.close();
            System.out.println("unlockERROR:"+"key:"+key+"expectVal:"+value+"val:"+jedis.get(key));
            return false;
        }
        jedis.close();
        return true;
    }

    @Override
    public Object execute(String lockId, int timeout, Callback callback) {
        String ran = Thread.currentThread().getName();

        boolean getLock=false;
        try {
            if(tryLock(lockId,ran, timeout)){
                getLock=true;
                return callback.onGetLock();
            }else{
                return callback.onTimeout();
            }
        }catch(InterruptedException ex){
            log.error(ex.getMessage(), ex);
            Thread.currentThread().interrupt();
        }catch (Exception e) {
            log.error(e.getMessage(), e);
        }finally {
            if(getLock) {
                unLock(lockId,ran);
            }else{
                jedis.close();
            }
        }
        return null;
    }
}

lua脚本加锁和解锁

class RedisLockInternals {
    private static final org.slf4j.Logger log = LoggerFactory.getLogger(RedisLockInternals.class);

    private JedisPool jedisPool;

    /**
     * 重试等待时间
     */
    private int retryAwait=300;

    private int lockTimeout=2000;


    RedisLockInternals(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    String tryRedisLock(String lockId,long time, TimeUnit unit) {
        final long startMillis = System.currentTimeMillis();
        final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;
        String lockValue=null;
        while (lockValue==null){
            lockValue=createRedisKey(lockId);
            if(lockValue!=null){
                break;
            }
            if(System.currentTimeMillis()-startMillis-retryAwait>millisToWait){
                break;
            }
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(retryAwait));
        }
        return lockValue;
    }

    private String createRedisKey(String lockId) {
        Jedis jedis = null;
        boolean broken = false;
        try {
            String value=lockId+randomId(1);
            jedis = jedisPool.getResource();
                        即:
//            - 获取锁(unique_value可以是UUID等)
//            SET resource_name unique_value NX PX 30000
//
//                    - 释放锁(lua脚本中,一定要比较value,防止误解锁)
//            if redis.call("get",KEYS[1]) == ARGV[1] then
//            return redis.call("del",KEYS[1])
//else
//            return 0
//            end
            String luaScript = ""
                    + "\nlocal r = tonumber(redis.call('SETNX', KEYS[1],ARGV[1]));"
                    + "\nredis.call('PEXPIRE',KEYS[1],ARGV[2]);"
                    + "\nreturn r";
            List<String> keys = new ArrayList<String>();
            keys.add(lockId);
            List<String> args = new ArrayList<String>();
            args.add(value);
            args.add(lockTimeout+"");
            Long ret = (Long) jedis.eval(luaScript, keys, args);
            if( new Long(1).equals(ret)){
                return value;
            }
        }finally {
            if(jedis!=null) jedis.close();
        }
        return null;
    }

    void unlockRedisLock(String key,String value) {
        Jedis jedis = null;
        boolean broken = false;
        try {
//String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return
//redis.call('del', KEYS[1]) else return 0 end";
            jedis = jedisPool.getResource();
            String luaScript=""
                    +"\nlocal v = redis.call('GET', KEYS[1]);"
                    +"\nlocal r= 0;"
                    +"\nif v == ARGV[1] then"
                    +"\nr =redis.call('DEL',KEYS[1]);"
                    +"\nend"
                    +"\nreturn r";
            List<String> keys = new ArrayList<String>();
            keys.add(key);
            List<String> args = new ArrayList<String>();
            args.add(value);
            Object r=jedis.eval(luaScript, keys, args);
        } finally {
            if(jedis!=null) jedis.close();
        }
    }

    private final static char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
            '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
            'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
            'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
            'Z'};

    private String randomId(int size) {
        char[] cs = new char[size];
        for (int i = 0; i < cs.length; i++) {
            cs[i] = digits[ThreadLocalRandom.current().nextInt(digits.length)];
        }
        return new String(cs);
    }

    public static void main(String[] args){
        System.out.println(System.currentTimeMillis());
        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(300));
        System.out.println(System.currentTimeMillis());
    }
}

分布式速度限制

/**
 * 分布式速率限制 例如:限制n秒钟请求x次

 */
public class AccessSpeedLimit {

    private static final org.slf4j.Logger log = LoggerFactory.getLogger(AccessSpeedLimit.class);



    private JedisPool jedisPool;

    public AccessSpeedLimit(){
    }

    public AccessSpeedLimit(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public JedisPool getJedisPool() {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 针对资源key,每seconds秒最多访问maxCount次,超过maxCount次返回false
     *
     * @param key
     * @param seconds
     * @param limitCount
     * @return
     */
    public boolean tryAccess(String key,int seconds,int limitCount){
        LimitRule limitRule=new LimitRule();
        limitRule.setLimitCount(limitCount);
        limitRule.setSeconds(seconds);
        return tryAccess(key,limitRule);
    }

    /**
     * 针对资源key,每limitRule.seconds秒最多访问limitRule.limitCount,超过limitCount次返回false
     * 超过lockCount 锁定lockTime
     * @param key
     * @param limitRule
     * @return
     */
    public boolean tryAccess(String key,LimitRule limitRule){
        String newKey="Limit:"+key;
        Jedis jedis = null;
        boolean broken = false;
        long count=-1;
        try {
            jedis = jedisPool.getResource();
            List<String> keys = new ArrayList<String>();
            keys.add(newKey);
            List<String> args = new ArrayList<String>();
            args.add(Math.max(limitRule.getLimitCount(), limitRule.getLockCount())+"");
            args.add(limitRule.getSeconds()+"");
            args.add(limitRule.getLockCount()+"");
            args.add(limitRule.getLockTime()+"");
            count=Long.parseLong(jedis.eval(buildLuaScript(limitRule),keys,args)+"");
            return count<=limitRule.getLimitCount();
        } finally {
            if(jedis!=null)jedis.close();
        }
    }


    private String buildLuaScript(LimitRule limitRule){
        StringBuilder lua=new StringBuilder();
        lua.append("\nlocal c");
        lua.append("\nc = redis.call('get',KEYS[1])");
        lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then");
        lua.append("\nreturn c;");
        lua.append("\nend");
        lua.append("\nc = redis.call('incr',KEYS[1])");
        lua.append("\nif tonumber(c) == 1 then");
        lua.append("\nredis.call('expire',KEYS[1],ARGV[2])");
        lua.append("\nend");
        if(limitRule.enableLimitLock()){
            lua.append("\nif tonumber(c) > tonumber(ARGV[3]) then");
            lua.append("\nredis.call('expire',KEYS[1],ARGV[4])");
            lua.append("\nend");
        }
        lua.append("\nreturn c;");
        return lua.toString();
    }
}

测试分布式锁

public class AccessSpeedLimitTest {
    @Test
    public void test1() throws InterruptedException {
        JedisPool jp=new JedisPool("localhost",6379);
        AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp);
        SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss");
        while(true){
            //10.0.0.1这个ip每1秒钟最多访问5次if块内代码.
            if(accessSpeedLimit.tryAccess("10.0.0.1", 1,5)){
                System.out.println("yes"+sdf.format(new Date()));
            }else{
                System.out.println("no"+sdf.format(new Date()));
            }
            Thread.sleep(100);
        }
    }

    @Test
    public void test2() throws InterruptedException {
        JedisPool jp=new JedisPool("127.0.0.1",6379);
        final RedisDistributedLockTemplate template=new RedisDistributedLockTemplate(jp);
        LimitRule limitRule=new LimitRule();
        limitRule.setSeconds(1);
        limitRule.setLimitCount(5);
        limitRule.setLockCount(7);
        limitRule.setLockTime(2);
        AccessSpeedLimit accessSpeedLimit=new AccessSpeedLimit(jp);
        SimpleDateFormat sdf=new SimpleDateFormat(" mm:ss");
        while(true){
            //10.0.0.1这个ip每1秒钟最多访问5次if块内代码.1秒超过10次后,锁定2秒,2秒内无法访问.
            if(accessSpeedLimit.tryAccess("10.0.0.1",limitRule)){
                System.out.println("yes"+sdf.format(new Date()));
            }else{
                System.out.println("no"+sdf.format(new Date()));
            }
            Thread.sleep(100);
        }
    }
}


在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值