In a recent business upgrade, we encountered such a problem. We designed a new account system, which requires users to manually synchronize the data of the original account after the application upgrade, that is, users need to trigger the synchronization button for synchronization, because some data is stored locally by users. So there is a problem in this process, what if the user clicks the button repeatedly because of the network? Even if we do some processing on the client side, we can’t click again during synchronization, but after my recent crawler practice, it’s still not safe if someone catches our interface.
Based on such a business scenario, I use the Redis locking method, limit the user in the request, can not initiate a second request.
After we entered the request first attempts to acquire lock object, then the lock object is in fact the key user id, if successful, when we judge the user has been synchronized data, if have synchronization, you can direct return, prompt the user has been synchronized, if not then execute the business logic of the synchronous data directly, finally lock release, If the lock fails to be acquired after the method is entered, then it is possible that the first request is not completed, and then the lock is not acquired, and the data will not be synchronized several times.
Gorgeous dividing line
With this requirement in mind, let’s implement the following code using Redis. The first thing we need to know is that we are going to introduce a method of Redis.
If we want to use Redis as the user’s unique lock object, then it should be unique in Redis and should not be overridden. This method returns true upon success, or false if the element already exists in a Redis instance
setIfAbsent(key,value)Copy the code
However, there is another problem in this process. If our service is suspended after obtaining the lock object, then other requests cannot obtain the lock at this time. In this case, we should add an expiration time to this element to prevent the deadlock problem after our service is suspended.
/** * Add element ** @param key * @param value */ public voidset(Object key, Object value) {
if (key == null || value == null) {
return; } redisTemplate.opsForValue().set(key, value.toString()); } /** * Returns if it already existsfalseOtherwise returntrue
*
* @param key
* @param value
* @return
*/
public Boolean setNx(Object key, Object value, Long expireTime, TimeUnit mimeUnit) {
if (key == null || value == null) {
return false;
}
returnredisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit); } /** * Get data ** @param key * @return
*/
public Object get(Object key) {
if (key == null) {
return null;
}
returnredisTemplate.opsForValue().get(key); } /** * Delete ** @param key * @return
*/
public Boolean remove(Object key) {
if (key == null) {
return false;
}
returnredisTemplate.delete(key); } /** * lock ** @param key ** @paramwaitTime Wait Time * @param expireTime Expiration Time */ Public Boolean Lock (String key, LongwaitTime, Long expireTime) {
String value = UUID.randomUUID().toString().replaceAll("-"."").toLowerCase();
Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS); // Attempts to acquire the lock are successfulif (flag) {
return flag;
} elseLong newTime = system.currentTimemillis (); Long loseTime = newTime +waitTime; // Repeated attempts to acquire the lock return successwhile (System.currentTimeMillis() < loseTime) {
Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
if (testFlag) {
return testFlag; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }}}return false; } /** * release the lock ** @param key ** @return
*/
public Boolean unLock(Object key) {
return remove(key);
}Copy the code
The lock() method takes three arguments: lock(); lock(); lock(); Key, waitTime is how long the user can wait if the lock cannot be obtained, after which the user will no longer wait, and the last parameter is how long the lock will expire to prevent a deadlock if the service hangs.
When entering the lock () after the first lock operation, if the lock is successful, then returns true, then execute the business logic behind the us, if failed to get the lock, will get the current time and set the expiration time, compare with the current time, if still in the waiting time, then try again to obtain locks, until after waiting time.
Note: At the time of setting values, we set up in order to prevent deadlock an expiration time, everybody must pay attention to, don’t go to don’t wait to set up a successful element to set the expiration time, because this is not an atomic operation, wait for you just set up after successful, haven’t set the expiration time success, such as the service directly, so when this happens the deadlock problem, So you want to make sure that you store elements and set expiration times atomic.
Finally, let’s write a test class to test it out
@Test
public void test01() {
String key = "uid:12011";
Boolean flag = redisUtil.lock(key, 10L, 1000L * 60);
if(! Flag) {// Failed to obtain the lock system.err. Println ("Failed to acquire lock");
} else{// Obtain the lock successfully system.out.println ("Lock acquired successfully"); } // Release the lock. Redisutil.unlock (key); }Copy the code