Distributed - using Redis achieve Distributed Lock

A, Redis realize the concept of a distributed lock

1.1, related to the commonly used commands

(1)SETNX

Role 1: SETNX key val: if and only if the key does not exist, set a key to a string of val, returns 1 (if successful return into key 1); if the key already exists, do nothing, returns 0 .

command and command set setnx difference:

set return OK, you can coverage value;

SETNX, if the key does not exist covering, the result is 0

(2)Expire

expire key timeout

As the key to set a timeout, the unit is second, more than this time it will automatically release the lock, to avoid deadlock.

(3)Delete

delete key

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

1.2 The principle

Multiple clients (JVM), the use setnx command mode, while creating a key on the same redis, because redis key can not be allowed to repeat only, as long as someone can create a key success, who will be able to acquire a lock, not created key success will be waiting.

The core idea of ​​realization: multiple servers can only be done to ensure the operation of a JVM

(1) acquiring the lock when: Using setnx lock and use the expire command to add a timeout to lock. This time is exceeded will automatically release the lock, the lock is a random value generated by the UUID, the lock is released when the determination by this value;

(2) to obtain the lock time: also set a time-out to acquire the lock, if you give up over this time to acquire the lock;

(3) When releasing locks: by UUID judgment is not the lock, if the lock, you lock the delete were released.

 

1.3, Redis and Zookeeper distributed lock to achieve realization Distributed Lock difference

The same point: In a clustered environment, ensure that allows only one JVM execution procedures

(1) technical point of view different: Redis is nosql main feature is cached, ZK is distributed coordination tool, mainly for distributed solutions

(2) implement different ideas: the core is to acquire the lock, release the lock, deadlock, but they achieve different ideas

Acquire a lock : ZK aspect, multiple clients (also called multiple JVM) creates a temporary node on the same ZK, ZK node named because the only path to maintain, as long as someone who will be able to create a successful acquire the lock; Redis aspects, and more clients will create a key using the same command in setnx redis because the only guarantee redis the key, as long as who can create success will come to acquire a lock.

Release the lock : ZK aspect, directly off the temporary session connection node session, because the life cycle of the temporary node session is to bind a session, the session if the session connection is closed, the temporary node will be deleted , this time to other clients using event listeners If the nodes are removed temporarily re-entered into the step of acquiring the lock;

Redis aspect, when the lock is released in order to ensure consistency lock, delete key redis the same time need to determine a lock id, you can remove (not delete wrong).

Deadlock (deadlock is the same idea) : ZK uses the session valid way to solve the deadlock, Redis also set the validity period to resolve the deadlock.

(3) different properties : as is redis nosql database, it is relatively better than the performance redis point.

(4) Different Reliability : not valid because redis control, then set the lock to get valid after, may cause a delay period; ZK congenital own valid function, session close lock is released, ZK use of temporary node will be more reliable manner .

 

 

The biggest difference is the difference between the release of the lock, ZK release the lock can be notified through an event.

 

1.4, some common doubts

How to release the lock?

When performing the operation, and delete the corresponding key, each key has its own corresponding period.

Valid purpose?

In order to prevent deadlock

How to prevent a deadlock?

Set session is valid

scenes to be used?

Distributed systems, such as buying a resource, this time using a distributed lock can be well controlled resources.

redis use of distributed lock encounter?

(1) two timeout:

      In the timeout before acquiring the lock: when trying to acquire the lock, if you do not get the lock within the specified time, you can abandon;

      After acquiring the lock timeout: after acquiring the lock, the corresponding key has a corresponding expiration date, the corresponding key for the failure within a predetermined time.

 

Second, the realization of the core code

Here is a simple implementation, and did not think too much about other issues, the main idea is:

2.1, Maven dependency information

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

2.2, LockRedis

public class LockRedis {
    //redis线程池
	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);//timeOut 一般以秒为单位,所以除1000
			// 5.定义在没有获取锁之前,锁的超时时间
			Long endTime = System.currentTimeMillis() + acquireTimeout;
            //这个where是核心,通过循环方式重试获取锁
			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;
	}
}

LockService

When the lock is released in two ways:

End (1) key is automatically valid

(2) completed the entire program to the next case, delete the corresponding key-- in this way does not work, such as locks on a can only delete a b can not delete the ( only delete their own, others can not be deleted when you delete )

public class LockService {
	private static JedisPool pool = null;

    //下面的就是redis连接代码(当然不是Springcloud方式的)
	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);
	}
    //创建一个lock锁
	LockRedis lockRedis = new LockRedis(pool);

	public void seckill() {
		String identifier = lockRedis.lockWithTimeout("xxx", 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("xxx", identifier);
		if (releaseLock) {
			System.out.println(Thread.currentThread().getName() + "释放锁成功,锁id identifier:" + identifier);
		}
	}
}

Release the lock may also like:

key in the lock id take as long as equivalent to the passed value is the same lock, then you can delete the same lock

ThreadRedis

class ThreadRedis extends Thread {
	private LockService lockService;

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

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

	}

}

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();
		}
	}

}

 

Published 52 original articles · won praise 116 · views 50000 +

Guess you like

Origin blog.csdn.net/RuiKe1400360107/article/details/103846733