Two kinds of Distributed lock implementation Scheme (I.)

Source: Internet
Author: User
Tags mutex zookeeper

One. Why use distributed locks?
When the number of application servers exceeds 1, access to the same data can cause access violations (especially write conflicts). Applications that use relational databases such as MySQL can use transactions to implement locks, or to implement optimistic locking using version numbers, the biggest drawback is the reduced availability (poor performance). For Gleasy, an application that satisfies large-scale concurrent access requests, using database transactions to implement a database is somewhat stretched. In addition, for some applications that do not rely on databases, such as distributed file systems, in order to ensure the correctness of the same file in a large number of read and write operations, a distributed lock must be introduced to constrain concurrent operation of the same file.

Two. Requirements for distributed locks
1. High performance (distributed locks cannot be a performance bottleneck for the system)
2. Avoid deadlocks (the node that gets locked will not cause other nodes to continue)
3. Support Lock re-entry

Three. Scenario 1, zookeeper-based distributed lock

/** * Distributedlockutil.java * Distributed lock Factory class, all distributed requests are owned by the factory class */public class Distributedlockutil {private static objec
    T schemelock = new Object ();
    private static Object Mutexlock = new Object ();
    private static map<string, object> Mutexlockmap = new Concurrenthashmap ();
    Private String schema; Private map<string, distributedreentrantlock> cache = new Concurrenthashmap<string, Distributedreentrantlock

    > ();

    private static map<string, distributedlockutil> instances = new Concurrenthashmap ();
        public static Distributedlockutil getinstance (String schema) {distributedlockutil u = instances.get (schema);
                if (U = = null) {synchronized (schemelock) {u = instances.get (schema);
                    if (U = = null) {u = new Distributedlockutil (schema);
                Instances.put (schema, u);
    }}} return u; } Private DistributedlocKutil (String schema) {This.schema = schema;
        } private Object Getmutex (String key) {Object mx = mutexlockmap.get (key);
                if (mx = = null) {synchronized (mutexlock) {mx = mutexlockmap.get (key);
                    if (mx = = null) {mx = new Object ();
                Mutexlockmap.put (key, MX);
    }}} return MX;
        } private Distributedreentrantlock Getlock (String key) {Distributedreentrantlock lock = cache.get (key);
                if (lock = = null) {synchronized (Getmutex (key)) {lock = Cache.get (key);
                    if (lock = = null) {lock = new Distributedreentrantlock (key, schema);
                Cache.put (key, lock);
    }}} return lock;
        } public void Reset () {for (String S:cache.keyset ()) {Getlock (s). Unlock ();
    }/** * Try to lock * If the current thread already owns the lock, return false directly, indicating that no more locks are needed, and that the unlock should not be unlocked at this time * * @param key * @return * @throws interruptedexception * @throws keeperexception */Public Lockstat Lock (String key) throws in
        Terruptedexception, Keeperexception {if (Getlock (key). Isowner ()) {return lockstat.noneed;
        } getlock (key). Lock ();
    return lockstat.success; } public void Clearlock (String key) throws Interruptedexception, keeperexception {synchronized (Getmutex (key
            ) {Distributedreentrantlock L = cache.get (key);
            L.clear ();
        Cache.remove (key); }} public void unlock (String key, Lockstat stat) throws Interruptedexception, keeperexception {unlock (k
    EY, stat, false);
        } public void Unlock (String key, Lockstat Stat, Boolean keepalive) throws Interruptedexception, Keeperexception {
        if (stat = = null) return; if (LOCKSTAT.SUCCEss.equals (stat)) {Distributedreentrantlock lock = Getlock (key);
            Boolean haswaiter = Lock.unlock ();
                    if (!haswaiter &&!keepalive) {synchronized (Getmutex (key)) {lock.clear ();
                Cache.remove (key); }}}} public static enum Lockstat {noneed, SUCCESS}}

/** * Distributedreentrantlock.java * lock contention between local threads, first using the virtual machine internal lock mechanism to reduce communication overhead between nodes */public class Distributedreentrantlock {PR
    Ivate static final Logger Logger = Logger.getlogger (Distributedreentrantlock.class);

    Private Reentrantlock Reentrantlock = new Reentrantlock ();
    Private Writelock Writelock;

    Private Long timeout = 3 * 60 * 1000;
    Private Final Object Mutex = new Object ();
    Private String dir;

    Private String schema;
            Private final Exitlistener Exitlistener = new Exitlistener () {@Override public void execute () {
        Initwritelock ();

    }
    };
        Private synchronized void Initwritelock () {logger.debug ("Initialize Writelock");
                Writelock = new Writelock (dir, new Locklistener () {@Override public void lockacquired () {
                Synchronized (mutex) {mutex.notify ();
         }} @Override public void lockreleased () {   }}, schema);
        if (Writelock! = NULL && WRITELOCK.ZK! = null) {WriteLock.zk.addExitListener (Exitlistener);
        } synchronized (Mutex) {mutex.notify ();
        }} public Distributedreentrantlock (String dir, string schema) {this.dir = dir;
        This.schema = schema;
    Initwritelock (); public void Lock (long timeout) throws Interruptedexception, keeperexception {reentrantlock.lock ();//multi-threaded competition
            , get first-level lock try {Boolean res = Writelock.trylock ();
                if (!res) {synchronized (mutex) {mutex.wait (timeout); } if (Writelock = = NULL | |!writelock.isowner ()) {throw new Interruptedexception ("Lock timeout
                ");
            }}} catch (Interruptedexception e) {reentrantlock.unlock ();
        Throw e;
         } catch (Keeperexception e) {   Reentrantlock.unlock ();
        Throw e;
    }} public void Lock () throws Interruptedexception, Keeperexception {lock (timeout);
    } public void Destroy () throws Keeperexception {Writelock.unlock ();
        public Boolean unlock () {if (!isowner ()) return false;
            try {writelock.unlock (); Reentrantlock.unlock ();//multi-threaded competition, release the outermost lock} catch (RuntimeException e) {reentrantlock.unlock ();//multi-threaded competition,
        Release the outermost lock throw e;
    } return Reentrantlock.hasqueuedthreads ();
    } public boolean Isowner () {return reentrantlock.isheldbycurrentthread () && writelock.isowner ();
    } public void Clear () {writelock.clear (); }

}

/** * Writelock.java * based on ZK's lock implementation * One of the simplest scenarios is as follows: * 1. Node A request lock, register yourself under a specific path (session self-increment node), get an ID number 1 * 2. Node B requests lock, registers itself under a specific path (session self-increment node), Get an ID number 2 * 3. Node A gets all the node IDs, determines that it is the minimum node number, and acquires the lock * 4. Node B gets all the node IDs and determines that it is not the minimum node, so the listener is less than its own maximum node (Node A) Change event * 5. Node A gets locked, handles business, finishes,
 Release Lock (Delete yourself) * 6. Node B receives the Node A change event and determines that it is the smallest node number, and then gets the lock.

    */public class Writelock extends Zkprimative {private static final Logger LOG = Logger.getlogger (Writelock.class);
    Private Final String dir;
    Private String ID;
    Private Locknode Idname;
    Private String ownerID;
    Private String Lastchildid;
    Private byte[] data = {0x12, 0x34};

    Private Locklistener callback;
        Public Writelock (String dir, string schema) {Super (schema, true);
    This.dir = dir;
        } public Writelock (String dir, Locklistener callback, string schema) {This (dir, schema);
    <a href = "http://www.nbso.ca/" > NBSO online casino reviews</a > This.callback = callback; } public Locklistener Getlocklistener() {return this.callback;
    } public void Setlocklistener (Locklistener callback) {this.callback = callback;
            } public synchronized void Unlock () throws RuntimeException {if (ZK = = NULL | | zk.isclosed ()) {
        Return
            } if (id! = NULL) {try {zk.delete (ID,-1);
                } catch (Interruptedexception e) {Log.warn ("Caught:" E, E);
                Set that we have been interrupted.
            Thread.CurrentThread (). interrupt ();
                } catch (Keeperexception.nonodeexception e) {//Do nothing} catch (Keeperexception e) {
                Log.warn ("Caught:" E, E);
                        Throw (runtimeexception) New RuntimeException (E.getmessage ()).
            Initcause (e);
                } finally {if (callback! = null) {callback.lockreleased ();
       } id = null;     }}} Private class LockWatcher implements watcher {public void process (Watchedevent event) {Log.debug ("Watcher Fired on Path:" Event.getpath () "State:" Event.getstate () "Type" ev
            Ent.gettype ());
            try {trylock ();
            } catch (Exception e) {Log.warn ("Failed to acquire Lock:" E, E); }}} private void Findprefixinchildren (string prefix, ZooKeeper ZooKeeper, string dir) throw
        S keeperexception, interruptedexception {list<string> names = Zookeeper.getchildren (dir, false);
                for (String name:names) {if (Name.startswith (prefix)) {id = dir "/" name;
                if (log.isdebugenabled ()) {Log.debug ("Found ID created last time:" id);
            } break; }} if (id = = NULL) {id = zookeeper.create (dir "/" PRefix, data, ACL, ephemeral_sequential);
            if (log.isdebugenabled ()) {Log.debug ("Created ID:" id);
        }}} public void Clear () {if (ZK = = NULL | | zk.isclosed ()) {return;
        } try {zk.delete (dir,-1);
        } catch (Exception e) {log.error ("Clear error:" E, E); }} public synchronized Boolean Trylock () throws Keeperexception, interruptedexception {if (ZK = = null)
            {Log.info ("ZK is empty");
        return false;
            } if (zk.isclosed ()) {Log.info ("ZK has been closed");
        return false;

        } ensurepathexists (dir);
        Log.debug ("ID:" id);
                Do {if (id = = NULL) {Long sessionId = Zk.getsessionid ();
                String prefix = "x" SessionId "-";
                Idname = new Locknode (ID);
         Log.debug ("Idname:" Idname);   } if (id! = null) {list<string> names = Zk.getchildren (dir, false);
                            if (Names.isempty ()) {Log.warn ("No Children in:" dir "when we ' ve just" "Created one!
                    Lets recreate it ... ");
                id = null;
                    } else {sortedset<locknode> sortednames = new treeset<locknode> ();
                    for (String name:names) {sortednames.add (New Locknode (dir "/" name) ");
                    } ownerid = Sortednames.first (). GetName ();
                    Log.debug ("All:" Sortednames);
                    sortedset<locknode> Lessthanme = Sortednames.headset (idname);
                    Log.debug ("Less Than Me:" Lessthanme);
                        if (!lessthanme.isempty ()) {Locknode lastchildname = Lessthanme.last (); Lastchildid = Lastchildname.getname ();
                        if (log.isdebugenabled ()) {Log.debug ("Watching Less Than me node:" L
                        Astchildid);
                        Stat stat = zk.exists (Lastchildid, New LockWatcher ());
                        if (stat! = null) {return boolean.false; } else {Log.warn ("Could not find the" "stats for less th
                        An me: "Lastchildname.getname ()); }} else {if (Isowner ()) {if (callback! = NULL)
                            {callback.lockacquired ();
                        } return boolean.true;
        }}}}} while (id = = NULL);
    return boolean.false; } public String Getdir () {return dir;
    } public boolean Isowner () {return id! = NULL && ownerID! = null && id.equals (ownerid);
    } public String GetId () {return this.id;
 }
}


Using this scheme to implement the distributed lock, can solve the problem of lock re-entry, and the use of session nodes to avoid deadlocks; performance, according to the author self-test results, lock unlock each time is an operation, the implementation of the scheme of distributed locks, TPS is about 2000-3000, the performance is relatively general;

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.