[Distributed Distributed Lock]

I. Overview

1.1 The core idea of ​​distributed solution

In multiple servers, one server can only guarantee the operation of jvm

1.2 Distributed Lock There are three general ways to achieve

  1. Database optimistic locking;
  2. Redis-based distributed lock setnx can be stored in the key, if stored in the key success returns 1 if the deposit key already exists, 0 is returned.
    • Multiple clients (jvm), use the command setnx way, while creating a key on the same redis, because rediskey not be allowed to repeat, as long as someone can create a key success, will be able to acquire the lock, did not create a key success, We will be waiting. Release the lock: Perform operation is complete when the delete key, key solution is to set the validity period, corresponding to each key has its own expiration date.
  3. ZooKeeper based distributed lock, is the general idea: each client to lock a function, in the directory specified node corresponding to the function on the zookeeper, instantaneous and orderly generate a unique node. Determining whether to acquire the lock is very simple, requires only a minimum determination number of ordered nodes. When the lock is released, simply delete this node can be instantaneous. At the same time, it avoids service downtime caused by lock can not be released, and the deadlock created.
    • Multiple clients (jvm), at the same time create the same node in a temporary zk, because temporary node path is guaranteed to be unique, as long as someone can create a node is successful, will be able to acquire the lock, did not create a successful node will wait when the lock is released when using event notification to the client to re-acquire the lock resource.

Second, based on the Distributed Lock Redis

2.1 Use common commands


#当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。
setnx key val

#为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
expire key timeout

# 删除key    
delete key

When using Redis implementation of distributed locks, will be used primarily to these three commands.

2.2 realization of ideas

1. Obtain a lock when using setnx lock, and the lock using the expire command to add a timeout period, this time is exceeded will automatically release the lock, the lock is a random value generated by the UUID, the lock is released in this time by judgment.
2. Get a lock when it set a timeout acquired over this time if you give up to acquire the lock.
3. Release the lock when the judgment is not by UUID of the lock, if the lock, you lock the delete were released.

2.3 core code

Maven dependency information

<dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
</dependency>

LockRedis

 public class LockRedis {

    private JedisPool jedisPool;

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

    /**
     * redis 上锁方法 
     * 
     * @param lockKey
     *            锁的key<br>
     * @param acquireTimeout
     *            在没有上锁之前,获取锁的超时时间<br>
     * @param timeOut
     *            上锁成功后,锁的超时时间<br>
     * @return
     */
    public String lockWithTimeout(String lockKey, Long acquireTimeout, Long timeOut) {
        Jedis conn = null;
        String retIdentifierValue = null;
        try {
            // 1.建立redis连接
            conn = jedisPool.getResource();
            // 2.随机生成一个value
            String identifierValue = UUID.randomUUID().toString();
            // 3.定义锁的名称
            String lockName = "redis_lock" + lockKey;
            // 4.定义上锁成功之后,锁的超时时间
            int expireLock = (int) (timeOut / 1000);
            // 5.定义在没有获取锁之前,锁的超时时间
            Long endTime = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < endTime) {
                // 6.使用setnx方法设置锁值
                if (conn.setnx(lockName, identifierValue) == 1) {
                    // 7.判断返回结果如果为1,则可以成功获取锁,并且设置锁的超时时间
                    conn.expire(lockName, expireLock);
                    retIdentifierValue = identifierValue;
                    return retIdentifierValue;
                }
                // 8.否则情况下继续循环等待
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retIdentifierValue;
    }

    /**
     * 释放锁
     * 
     * @return
     */
    public boolean releaseLock(String lockKey, String identifier) {

        Jedis conn = null;
        boolean flag = false;
        try {

            // 1.建立redis连接
            conn = jedisPool.getResource();
            // 2.定义锁的名称
            String lockName = "redis_lock" + lockKey;
            // 3.如果value与redis中一直直接删除,否则等待超时
            if (identifier.equals(conn.get(lockName))) {
                conn.del(lockName);
                System.out.println(identifier + "解锁成功......");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return flag;
    }
}

Service

public class LockService {
    private static JedisPool pool = null;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(200);
        // 设置最大空闲数
        config.setMaxIdle(8);
        // 设置最大等待时间
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
        config.setTestOnBorrow(true);
        pool = new JedisPool(config, "39.107.69.43", 6379, 3000);
    }

    LockRedis lockRedis = new LockRedis(pool);

    public void seckill() {
        String identifier = lockRedis.lockWithTimeout("itmayiedu", 5000l, 5000l);
        if (StringUtils.isEmpty(identifier)) {
            // 获取锁失败
            System.out.println(Thread.currentThread().getName() + ",获取锁失败,原因时间超时!!!");
            return;
        }
        System.out.println(Thread.currentThread().getName() + "获取锁成功,锁id identifier:" + identifier + ",执行业务逻辑");
        try {
            Thread.sleep(30);
        } catch (Exception e) {

        }
        // 释放锁
        boolean releaseLock = lockRedis.releaseLock("itmayiedu", identifier);
        if (releaseLock) {
            System.out.println(Thread.currentThread().getName() + "释放锁成功,锁id identifier:" + identifier);
        }
    }
    }

    class ThreadRedis extends Thread {
    private LockService lockService;

    public ThreadRedis(LockService lockService) {
        this.lockService = lockService;
    }

    @Override
    public void run() {
        lockService.seckill();

    }

    }

Test code

public class Test001 {

    public static void main(String[] args) {
        LockService lockService = new LockService();
        for (int i = 0; i < 50; i++) {
            ThreadRedis threadRedis = new ThreadRedis(lockService);
            threadRedis.start();
        }
    }

    }

  • In a distributed environment, of resources locked sometimes very important, such as buying a resource, this time using a distributed lock can be well controlled resources. Of course, the specific use, also need to consider many factors, such as time-out selection, acquisition has a significant impact on the time selected lock concurrency, distributed lock to achieve the above is only a simple implementation, the main It is an idea.

Third, within Distributed contrast

  • Above in several ways, which way can not be perfect. Like, like CAP, can not meet at the same time in terms of complexity, reliability, performance, etc. So, choose the most suitable for their own is king according to different application scenarios.
从理解的难易程度角度(从低到高)
数据库 > 缓存 > Zookeeper
    
从实现的复杂性角度(从低到高)
Zookeeper >= 缓存 > 数据库
    
从性能角度(从高到低)
缓存 > Zookeeper >= 数据库
    
从可靠性角度(从高到低)
Zookeeper > 缓存 > 数据库

Redis achieve Zookeeper distributed lock and achieve Distributed Lock difference

Redis achieved using a distributed lock

  • The command set nx redis, when the key does not exist, add the key to success in the redis, the property may be implemented using a distributed lock and key for redis have expiration time can be controlled when a client successfully locked hang up, causing congestion problems.

Zookeeper achieved using a distributed lock

  • Multiple clients to create a node on the same temporary Zookeeper, because the temporary node can only allow a client to create success, as long as any one client node to create a successful, success will come to acquire a lock, when the lock is released, other Similarly in Zookeeper client node.

Guess you like

Origin www.cnblogs.com/haoworld/p/distributed-fen-bu-shi-suo.html