Implementation of distributed lock mechanism in distributed environment by Redis

Source: Internet
Author: User
Tags aop commit constructor hash redis throwable uuid xmlns

Redis redis Command Reference

Redis is a key-value storage system. Similar to memcached, it supports storing more value types, including string (string), list, set (set), Zset (sorted set-ordered collection), and hash (hash type). All support Push/pop, Add/remove, and intersection sets and differences and richer operations, and these operations are atomic in nature. Based on this, Redis supports sorting in a variety of different ways. As with memcached, data is cached in memory to ensure efficiency. The difference is that Redis periodically writes the updated data to disk or writes the modified operation to the appended record file, and Master-slave (Master-Slave) synchronization is implemented on this basis. Redis is a high-performance Key-value database. The emergence of Redis, to a large extent, compensates for the lack of memcached such key/value storage, in some cases can be a good complement to the relational database. It provides the python,ruby,erlang,php client, which is very convenient to use. Redis supports master-slave synchronization. Data can be synchronized from the primary server to any number of slave servers, from the server to the primary server that is associated with other slave servers. This enables Redis to perform single-layer tree replication. The data can be written intentionally or unintentionally from the disk. Because of the full implementation of the publish/subscribe mechanism, you can subscribe to a channel and receive a complete message release record from the master server when the tree is synchronized anywhere from the database. Synchronization is helpful for the scalability and data redundancy of read operations.


Distributed Systems

Distributed Systems (Distributed system) are software systems built on top of the network. Because of the nature of the software, distributed systems are highly cohesive and transparent. Therefore, the difference between a network and a distributed system is more about high-level software (especially the operating system) than the hardware. Cohesion refers to the high degree of autonomy of each database distribution node, and the local database management system. Transparency means that each database distribution node is transparent to the user's application and does not see whether it is local or remote. In a distributed database system, the user does not feel that the data is distributed, that is, the user does not need to know whether the relationship is split, there are no replicas, which site the data is stored, and at which site the transaction executes.


Question due to the characteristics of distributed systems, when one of our web projects runs in a distributed environment. Implementation is similar to when a client user sends a request to the server and is then assigned to a server to execute and return the result. such as this kind of query operation, such as the client sends the request in the distributed environment can get a quick response to the whole system also has no effect. But there are scenarios where we need to do something similar to timed tasks. Because the entire system's services may be configured on multiple servers, it may cause multiple servers to run a timed task together, which is not allowed. So you need to set a lock to manage.


Workaround

Using Redis distributed locks to manage-the scenario allows only one action when working on a scheduled task. Lock control can be added to the operation (in this operation, I tried to encapsulate a process using spring AOP, implementing a distributed lock in a program that only needs to be handled in the form of annotations)


the redis XML configuration file

<?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "Http://www.springframework.org/schema/beans" Xmlns:xs I= "Http://www.w3.org/2001/XMLSchema-instance" xmlns:util= "Http://www.springframework.org/schema/util" XSI:SC hemalocation= "Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/ Spring-beans.xsd Http://www.springframework.org/schema/util http://www.springframework.org/schema/util/ Spring-util.xsd "> <!--Implement Import properties Profile--<util:properties id=" app "location=" Classpath:ap P.properties "/> <!--jedispoolconfig Connection thread configuration-<bean id=" Jedispoolconfig "class=" Redis.clients.jedis. Jedispoolconfig "> <property name=" maxtotal "value=" 5 "/> <property name=" Maxidle "value=" 2 "/&gt
        ; <property name= "Maxwaitmillis" value= "10000"/> <property name= "Testonborrow" value= "true"/> </b
   ean> <!--jedispool Connection processing class-- <bean id= "Jedispool" class= "Redis.clients.jedis.JedisPool" > <constructor-arg index= "0" ref= "Jedispoolcon Fig "/> <constructor-arg index=" 1 "value=" 127.0.0.1 "/> <constructor-arg index=" 2 "value=" 6379 " type= "int"/> </bean> <!--jedistemplate is Jedis implementation class--<bean id= "jedistemplate" class= "COM.R Abbit.util.RedisTemplate "> <constructor-arg index=" 0 "ref=" jedispool "/> </bean> </beans>


implementation class for encapsulated Jedis

Package com.rabbit.util;
Import com.rabbit.ActionInterface.RedisAction;
Import com.rabbit.ActionInterface.RedisNoResultAction;
Import Org.slf4j.Logger;
Import Org.slf4j.LoggerFactory;
Import Redis.clients.jedis.Jedis;
Import redis.clients.jedis.exceptions.JedisConnectionException;
Import redis.clients.jedis.exceptions.JedisException;

Import Redis.clients.util.Pool; /** * Implements the encapsulation of the Jedis object, implements the operation */public class Redistemplate {private static Logger Logger = Loggerfactory.getlogger (Redis

    Template.class);

    Encapsulates a pool for the management of Jedis objects private pool<jedis> jedispool;
    Public redistemplate (pool<jedis> jedispool) {this.jedispool = Jedispool; }/** * Implement callback processing of events via observer pattern (using PARADIGM) * * @param jedisaction Required Method interface * @param <T> execution return type issued * @return T * @throws jedisexception * Returns the executed object information */public <T> T Execute (redisactio
        N<t> jedisaction) {T result = null;
  Jedis Jedis = null;      Boolean broken = false;
            try {Jedis = Jedispool.getresource ();
        result = Jedisaction.action (Jedis);
            } catch (Jedisconnectionexception e) {logger.error ("Redis connection lost.", e);
        Broken = true;
            } finally {//Release Jedis resource Closeresource (Jedis, broken);
        return result; }}/** * Implement callback handling of events via Observer mode */public void execute (redisnoresultaction jedisaction) {Jedis J
        Edis = null;
        Boolean broken = false;
            try {Jedis = Jedispool.getresource ();
        Jedisaction.actionnoresult (Jedis);
            } catch (Jedisconnectionexception e) {logger.error ("Redis connection lost.", e);
        Broken = true;
        } finally {Closeresource (Jedis, broken); }}/** * restore Jedis resource to Pool * * @param jedis Jedis resource * @param connectionbroken connection State (depending on the state, using different resources to interpret the*/protected void Closeresource (Jedis Jedis, Boolean ConnectionBroken) {if (Jedis! = null) {
            if (ConnectionBroken) {//Jedispool.returnbrokenresource (Jedis) is the way to fail the connected resource;
            } else {//Jedispool.returnresource (Jedis) When a successful connection is made to the resource;
     }}}/** * Gets the Jedispool object.
    */Public pool<jedis> Getjedispool () {return jedispool;
 }
}


encapsulating Redis distributed lock Objects

Package com.rabbit.util;

Import Lombok. Getter;
Import Lombok. Setter;

/**
 * Created by zero on 14-4-3.
 *

/public class redislockmodel{
    //Lock ID
    @Setter
    @Getter
    private String lockid;

    Lock name
    @Getter
    private String lockname;

    Public Redislockmodel (String lockname) {
        this.lockname = lockname;
    }
}


Redislock annotations (implemented with spring AOP)

Package com.rabbit.annotation;

Import Com.rabbit.util.RedisKeys;

Import java.lang.annotation.*;

/**
 * Design for Redis distributed lock operation using annotations
 * Created by zero on 14-4-8.
 */
@Target (Elementtype.method)
@Retention (retentionpolicy.runtime)
@Documented public
@ Interface Redislock {
    //redis Key object
    Rediskeys Rediskeys ();

    Maximum wait time
    int maxwait ();

    The expiration time of the key
    int expiredtime ();
}


The implementation class of Redislock's AOP

Package com.rabbit.annotation;
Import Com.rabbit.util.RedisLockExecute;
Import Com.rabbit.util.RedisLockModel;
Import Com.rabbit.util.RedisTemplate;
Import Org.aspectj.lang.ProceedingJoinPoint;
Import Org.aspectj.lang.annotation.Around;
Import Org.aspectj.lang.annotation.Aspect;
Import Org.slf4j.Logger;
Import Org.slf4j.LoggerFactory;
Import org.springframework.beans.factory.annotation.Autowired;

Import org.springframework.stereotype.Component; /** * Implementation of concurrency management for distributed environments via Redis (concurrent processing via REDIS Lock) * * * @Aspect @Component public class Redislockoperation {Priv

    Ate static final Logger Logger = Loggerfactory.getlogger (Redislockoperation.class);

    @Autowired private Redistemplate redistemplate; /** * Create a redis lock * @param proceed actual Processing object * @param redislock redislock Annotation Object */@Around ("Execut Ion (* * (..)) && @annotation (Redislock) ") public void Acquirelock (final proceedingjoinpoint proceed, final Red Islock redislock) {RedislockModel Redislockmodel = Redislockexecute.acquirelock (Redistemplate, Redislock.rediskeys (), redislock.maxwait (),

        Redislock.expiredtime ());
            try{if (Redislockexecute.acquire_result (Redislockmodel)) {proceed.proceed ();
            }else{Logger.debug ("Acquire lock is failed!");
            }} catch (Throwable throwable) {logger.error ("proceed={} is failed!", proceed.tostring ());
        Throwable.printstacktrace ();
        }finally {redislockexecute.releaselock (redistemplate, Redislockmodel); }
    }
}

Note that you need to include <!--in the spring configuration file to provide support for spring AOP--
<aop:aspectj-autoproxy/>


Rabbitlock Processing Class

Package com.rabbit.util;
Import com.google.common.base.Objects;
Import com.google.common.base.Strings;
Import com.rabbit.ActionInterface.RedisAction;
Import Org.slf4j.Logger;
Import Org.slf4j.LoggerFactory;
Import Redis.clients.jedis.Jedis;

Import redis.clients.jedis.Transaction;
Import java.util.List;
Import Java.util.UUID;

Import Java.util.concurrent.TimeUnit; /** * Implementation of concurrency management for distributed environments via Redis (concurrent processing via REDIS Lock) */public class Redislockexecute {private static final Logger Logger

    = Loggerfactory.getlogger (Redislockexecute.class); /** * Create a redis lock * @param redistemplate Redis Processing Object * @param rediskeys the Lock object to be added * @param max Wait the longest time to add a lock (in ms) * @param expiredtime Set the expiration time of the lock (in s) * @return Redislockmodel * return A locked object after a package */public static Redislockmodel Acquirelock (final redistemplate redistemplate, final Rediskeys Redis Keys, Final integer maxwait, final integer expiredtime) {//immediately gets a UUID, as a keyThe same name as the lock final String uuid = Uuid.randomuuid (). toString (); Boolean result = Redistemplate.execute (new redisaction<boolean> () {@Override public Boolean
                Action (Jedis Jedis) {//Gets the last time period of the wait time long expireat = System.currenttimemillis () +maxwait; The loop determines whether the lock persists while (System.currenttimemillis () < expireat) {LOGGER.
                    Debug ("Try Lock key={}", rediskeys.tostring ()); if (Jedis.setnx (rediskeys.tostring (), uuid) = = 1) {Jedis.expire (rediskeys.tostring (), ExpiredTime
                        );
                        Logger.debug ("Get Lock, key={}, expire in seconds={}", Rediskeys.tostring (), expiredtime);
                    return true; } else {//There is a lock (which will be executed (within the maximum wait time range) waiting for the lock to be released) String desc = jedis.get (rediskeys
                        . toString ()); Logger.debug ("key={} locked BY another business={} ", rediskeys.tostring (), DESC);
                    } try{TimeUnit.MILLISECONDS.sleep (1);
                    }catch (interruptedexception e) {thread.currentthread (). interrupt ();
            }}//did not acquire a lock return false within the maximum wait time range;

        }
        });
        Redislockmodel Redislockmodel = new Redislockmodel (rediskeys.tostring ());
        Redislockmodel.setlockid (result uuid:null);
    return Redislockmodel;
     /** * Determine if the lock was added successfully by the lock number obtained * @param redislockmodel redis Lock Object * @return Boolean * Returns whether the Add succeeded */public static Boolean Acquire_result (Redislockmodel redislockmodel) {return!
    Strings.isnullorempty (Redislockmodel.getlockid ()); }//Release lock public static Boolean ReleaseLock (final redistemplate redistemplate, final Redislockmodel Redislockmodel ) {return redistemplate.Execute (new redisaction<boolean> () {@Override Public Boolean action (Jedis Jedis) {

                Prevent multiple servers from processing the same key requires watch operation (equivalent to prohibit other client processing this key) Jedis.watch (Redislockmodel.getlockname ()); If the lock does not expire, then the value of the lock must not change if (Objects.equal (Redislockmodel.getlockid (), Jedis.get (Redislockmodel).
                    Getlockname ()))) {//Remove lock Jedis.del (Redislockmodel.getlockname ());
                return true;
                }//Lock has expired, release watch Operation Jedis.unwatch ();
            return false;
    }
        }); }//Bulk release lock public static Integer Releasebatchlock (final redistemplate redistemplate, final List<redislockmodel
            > Redislockmodels) {return Redistemplate.execute (new redisaction<integer> () {@Override

   Public Integer Action (Jedis Jedis) {Transaction Transaction = Jedis.multi ();             Logger.debug ("Release batch Redis lock start"); for (Redislockmodel redislockmodel:redislockmodels) {if (Objects.equal (Redislockmodel.getlockid (), je Dis.get (Redislockmodel.getlockname ()))) {//delete an unexpired lock Transaction.del (redislo
                    Ckmodel.getlockname ());
                }}//Transaction commit operation (returns the number of transactions executed) Integer length = Transaction.exec (). Size ();

                Logger.debug ("Release batch Redis lock end, release num={}", length);
            return length;
    }
        }); }/** * destroys all created lock * * @param redistemplate redis operand * @return Integer * Returns the total destroyed creation bar Number */public static Integer destructionlocks (final redistemplate redistemplate) {return redistemplate.exec
               Ute (new redisaction<integer> () {@Override Public Integer action (Jedis Jedis) { Transaction Transaction = Jedis.multi ();
                Logger.debug ("Destruction Redis lock Start");
                For (Rediskeys redisKeys:RedisKeys.values ()) {Transaction.del (rediskeys.tostring ());
                }//Transaction commit operation (returns the number of transactions executed) Integer length = Transaction.exec (). Size ();

                Logger.debug ("Destruction Redis lock end, release num={}", length);
            return length;
    }
        }); }
}

The test class is no longer written here, and this is part of my previous blog about rabbit processing. Interested students can go and see
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.