Use Golang to implement a distributed lock using ECTD

Source: Internet
Author: User
Tags sprintf etcd
This is a creation in Article, where the information may have evolved or changed.

http://blog.codeg.cn/post/blog/2016-02-24-distrubute-lock-over-etcd/

by Zieckey February 24, 2016 · 1205 Words ~3min reading Time | Edit this page | Tags:golang ETCD Distributed

This article http://blog.codeg.cn/post/blog/2016-02-24-distrubute-lock-over-etcd/is the author Zieckey in research and study related content when the note, welcome the broad masses of friends to correct and exchange! Copyright, Welcome to reprint and share, but please keep this paragraph statement.

etcdis growing with CoreOS the project, with projects such as Golang and CoreOS becoming increasingly hot in the open source community, etcd a highly available, highly consistent distributed key-value storage system is being watched and used by a growing number of developers.

This article provides a comprehensive overview of the ETCD application scenario, which is summarized here as follows:

    • Services Discovery (Service Discovery)
    • Message Publishing and subscription
    • Load Balancing
    • Distributed notification and coordination
    • Distributed locks
    • Distributed queues
    • Cluster monitoring and leader campaign
    • Why use ETCD instead of zookeeper

This article focuses on how to ectd implement a distributed lock. Lock concept Everyone is familiar with when we want an event at the same point in time only one thread (goroutine) is doing, or a resource at the same point in time only one service can access, this time we need to use the lock. For example, to implement a distributed ID generator, coordination between multiple servers is cumbersome. Distributed locks are just handy.

Its basic implementation principle is:

    1. Create a key in the ECTD system
    2. If creation fails, key exists, then listen for the key change event until the key is deleted, back to 1
    3. If the creation succeeds, you think I got the lock.

The specific code is as follows:

Package EtcdsyncImport ("FMT""IO""OS""Sync""Time""Github.com/coreos/etcd/client""Github.com/coreos/etcd/godeps/_workspace/src/golang.org/x/net/context")Const (DEFAULTTTL =60defaultTry =3deleteAction ="Delete" expireaction ="Expire")A Mutex is a mutual exclusion lock which is distributed across a cluster.Type Mutexstruct {keyStringidStringThe identity of the Callerclient client. CLIENTKAPI client. Keysapictx context. Contextttl time. Durationmutex *sync. Mutexlogger io. Writer}New creates a Mutex with the given key which must be the sameAcross the cluster nodes.Machines is the ECTD cluster addressesFunc New (keyString, TTLint, machines []String) *mutex {cfg: = client. Config{endpoints:machines,transport:client. Defaulttransport,headertimeoutperrequest:time. Second,}c, Err: = client. New (CFG)If Err! =Nil {ReturnNil}hostname, err: = OS. Hostname ()If Err! =Nil {ReturnNilIfLen (key) = =0 | |Len (machines) = =0 {ReturnNilIf key[0]! ='/' {key ="/" + key}If TTL <1 {ttl = DefaultTTL}Return &AMP;MUTEX{KEY:KEY,ID:FMT. Sprintf ("%v-%v-%v", hostname, OS. Getpid (), time. Now (). Format ("20060102-15:04:05.999999999")), Client:c,kapi:client. Newkeysapi (c), Ctx:context. TODO (), Ttl:time. Second * time. Duration (TTL), Mutex:New (sync. Mutex),}}Lock locks M.If the lock is already on use, the calling GoroutineBlocks until the mutex is available.Func (M *mutex) Lock () (err error) {M.mutex.lock ()For try: =1; Try <= defaulttry; try++ {If m.lock () = =Nil {ReturnNil}m.debug ("Lock node%v ERROR%v", M.key, Err)If try < Defaulttry {M.debug ("Try to lock node%v again", M.key, Err)}}Return err}Func (M *mutex) lock () (err error) {M.debug ("Trying to create a node:key=%v", M.key) SetOptions: = &client. Setoptions{prevexist:client. PREVNOEXIST,TTL:M.TTL,}RESP, err: = M.kapi.set (M.ctx, M.key, M.id, SetOptions)If Err = =Nil {M.debug ("Create node%v OK [%q]", M.key, RESP)ReturnNil}m.debug ("Create node%v failed [%v]", M.key, Err) e, OK: = Err. (Client. Error)If!ok {Return err}If E.code! = client. errorcodenodeexist {Return err}Get the already node ' s value.resp, err = M.kapi.get (M.ctx, M.key,NilIf Err! =Nil {Return Err}m.debug ("Get node%v OK", M.key) Watcheroptions: = &client. Watcheroptions{afterindex:resp. Index,recursive:False,}watcher: = M.kapi.watcher (M.key, watcheroptions)for {M.debug ("Watching%v ...", M.key) resp, err = Watcher. Next (M.CTX)If Err! =Nil {Return Err}m.debug ("Received an event:%q", resp)If resp. Action = = Deleteaction | | Resp. Action = = Expireaction {ReturnNil}}}Unlock unlocks M.It is a run-time error if m are not locked on entry to Unlock.//A locked Mutex is not associated with a particular goroutine.It is allowed for one goroutine to lock a mutexes and thenArrange for another goroutine to unlock it.Func (M *mutex) Unlock () (err error) {Defer M.mutex.unlock ()For I: =1; I <= defaulttry; i++ {Var resp *client. RESPONSERESP, err = M.kapi.delete (M.ctx, M.key,NilIf Err = =Nil {M.debug ("Delete%v OK", M.key)ReturnNil}m.debug ( "Delete%v falied:%q", M.key, resp) e, OK: = Err. (Client. Error) if ok && e.code = = client. Errorcodekeynotfound {return nil}} return Err} func (M *mutex) debug (format string, v ... interface{}" {if m.logger! = nil { M.logger.write ([]byte (m.id)) m.logger.write ([]byte ( Span class= "hljs-string")) m.logger.write ([]byte (FMT. Sprintf (format, v ...))) M.logger.write ([]byte (func (M *mutex) setdebuglogger (w io. Writer) {M.logger = w}              

In fact, there are many similar implementations, but they are now obsolete and are used by officially marked deprecated projects. And most interfaces are less straightforward than the above code. On the use, with the Golang official Sync packet Mutex interface very similar, first New() , then call, Lock() use after the call Unlock() , on the three interface, it is so simple. The sample code is as follows:

Package MainImport ("Github.com/zieckey/etcdsync""Log")Func Main () {Etcdsync. Setdebug (True) log. SetFlags (log. Ldate|log. Ltime|log. Lshortfile) M: = Etcdsync. New ( "/etcdsync",  "123", []string{< Span class= "hljs-string" > "http://127.0.0.1:2379"}) if m = =  Nil {log. Printf ( "Etcdsync". Newmutex failed ")}err: = M.lock () if err! = nil {log. Printf ( "Etcdsync". Lock failed ")} else {log. Printf ( "Etcdsync". Lock OK ")}log. Printf (" Get the lock. Do something here. ") Err = M.unlock () if err! = nil {log. Printf ( "Etcdsync". Unlock failed ")} else {log. Printf ( "Etcdsync". Unlock OK}}               

Reference

    1. Etcdsync Project Address
    2. ECTD Project Official Address
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.