This is a creation in Article, where the information may have evolved or changed.
This article collates the ideas, writes the example (Golang), in order to deepen the ETCD understanding
Basically, the listener is master, service is
1 when the service starts, register its own information with ETCD, that is, register to the services/directory
2 service may be out of the ordinary, need to maintain a TTL (V3 use lease implementation), similar to the heartbeat, hung off, master can be heard
3 master listens to all services under the services/directory, and handles them according to different action (V3 put/delete)
Service Registration
Provide key (service name), value (serviceinfo) for registered
Start after start, perform keeplive (), maintain heartbeat, hang revoke ()
Listen to stop Chan at the same time, equivalent to unregistered
Package Discoveryimport ("GITHUB.COM/COREOS/ETCD/CLIENTV3" "Context" " Time" "Log" "Encoding/json" "Errors")//The detail of serviceType ServiceInfostruct{IPstring}type Servicestruct{NamestringInfo serviceinfo Stop chan error Leaseid clientv3. Leaseid Client*CLIENTV3. Client}func NewService (Namestring, Info serviceinfo,endpoints []string) (*Service, error) {CLI, err:=CLIENTV3. New (CLIENTV3. config{endpoints:endpoints, Dialtimeout:2*Time . Second,})ifErr! =Nil {log. Fatal (ERR)returnnil, err}return&Service {name:name, Info:info, Stop:make (chan error), client: CLI,},err}func (s*Service) Start () error {ch, err:=s.keepalive ()ifErr! =Nil {log. Fatal (ERR)returnErr} for { Select { CaseERR: = <-S.stop:s.revoke ()returnErr Case<-S.client.ctx (). Done ():returnErrors. New ("Server closed") CaseKa, OK: = <-Ch:if!OK {log. Println ("keep Alive Channel closed") S.revoke ()returnNil}Else{log. Printf ("Recv reply from service:%s, ttl:%d", S.name, Ka. TTL)}}}}func (S*Service) Stop () {s.stop<-Nil}func (s*service) keepAlive () (<-chan *CLIENTV3. Leasekeepaliveresponse, error) {info:= &s.info Key:="services/"+S.name Value, _:=JSON. Marshal (Info)//Minimum lease TTL is 5-secondRESP, err: = S.client.grant (context. TODO (),5) ifErr! =Nil {log. Fatal (ERR)returnNil, err} _, Err= S.client.put (context. TODO (), Key,string(value), CLIENTV3. Withlease (resp.id))ifErr! =Nil {log. Fatal (ERR)returnnil, err} S.leaseid=resp.idreturns.client.keepalive (context. TODO (), resp.id)}func (s*Service) Revoke () error {_, err:=S.client.revoke (context. TODO (), S.leaseid)ifErr! =Nil {log. Fatal (Err)} log. Printf ("servide:%s stop\n", S.name)returnErr}
Listener Master
Provide a listening path, start master, add map when put, remove from map when delete
Package Discoveryimport ("GITHUB.COM/COREOS/ETCD/CLIENTV3" "Context" "Log" " Time" "Encoding/json" "FMT") Type Masterstruct{PathstringNodes map[string] *Node Client*CLIENTV3. Client}//node is a clientType Nodestruct{ stateBOOLKeystringInfo serviceinfo}func newmaster (endpoints []string, Watchpathstring) (*master,error) {CLI, err:=CLIENTV3. New (CLIENTV3. config{endpoints:endpoints, Dialtimeout:time. Second,})ifErr! =Nil {log. Fatal (ERR)returnNil,err} master:= &Master {path:watchpath, nodes:make (map[string]*Node), CLIENT:CLI,} go master. Watchnodes ()returnMaster,err}func (M*master) AddNode (keystring, Info *serviceinfo) {node:= &node{State:true, Key:key, Info:*info,} m.nodes[node. Key]=node}func getserviceinfo (EV*CLIENTV3. Event) *serviceinfo {info:= &serviceinfo{} ERR:= json. Unmarshal ([]byte(EV. Kv.value), info)ifErr! =Nil {log. PRINTLN (ERR)}returnInfo}func (M*Master) Watchnodes () {rch:=M.client.watch (context. Background (), M.path, CLIENTV3. Withprefix ()) forWRESP: =Range RCH { for_, Ev: =Range Wresp. Events {Switchev. Type { CaseCLIENTV3. Eventtypeput:fmt. Printf ("[%s]%q:%q\n", Ev. Type, Ev. Kv.key, Ev. Kv.value) Info:=getserviceinfo (EV) M.addnode (string(EV. Kv.key), info) CaseCLIENTV3. Eventtypedelete:fmt. Printf ("[%s]%q:%q\n", Ev. Type, Ev. Kv.key, Ev. Kv.value) Delete (M.nodes,string(EV. Kv.key)) }}}}
Service Usage example, disconnect after 20s
Package Mainimport ("fmt" Dis "discovery" "Log" "Time") Func main () {serviceName: = "S-test" serviceinfo: = Dis. Serviceinfo{ip: "192.168.1.26"}s, err: = Dis. NewService (ServiceName, serviceinfo,[]string {"http://192.168.1.17:2379", "http://192.168.1.17:2479", "/HTTP/ 192.168.1.17:2579 ",}) if err! = Nil {log. Fatal (Err)}fmt. Printf ("name:%s, ip:%s\n", S.name, S.info.ip) go func () {time. Sleep (time. SECOND*20) S.stop ()} () S.start ()}
MAster Use example
Package Mainimport ("Log" "Time" "FMT" Dis "discovery") Func main () {m, err: = Dis. Newmaster ([]string{"http://192.168.1.17:2379", "http://192.168.1.17:2479", "http://192.168.1.17:2579",}, " Services/") if err! = Nil {log. Fatal (Err)}for {for k, V: = Range M.nodes {fmt. Printf ("node:%s, ip=%s\n", K, V.info.ip)}fmt. PRINTF ("Nodes num =%d\n", Len (m.nodes)) time. Sleep (time. Second * 5)}}
Execution result ( need to build ETCD server in advance , can download to GitHub, provide simple startup script at the end)
Go Run Master
Go Run service
ETCD Server startup script (execution Environment CentOS6.5)
#!/bin/Shwork_path=$ (DirName $0) CD./${work_path} #echo $ (PWD) #echo ' Date ' Case$1 inch1) echo-E"[1]start First server\n"./ETCD--name cd0--initial-advertise-peer-urls http://127.0.0.1:2380 \--listen-peer-urls http://127.0.0.1:2380 \--listen-client-urls http://192.168.1.17:2379,http://127.0.0.1: 2379 \--advertise-client-urls http://192.168.1.17:2379,http://127.0.0.1: 2379 \--initial-cluster-token etcd-cluster-1 \ --initial-cluster cd0=http://127.0.0.1:2380,cd1=http://127.0.0.1: 2480,cd2=http://127.0.0.1: 2580 \--initial-cluster-stateNew ;;2) echo-E"[2]start Second server\n"./ETCD--name CD1--initial-advertise-peer-urls http://127.0.0.1:2480 \--listen-peer-urls http://127.0.0.1:2480 \--listen-client-urls http://192.168.1.17:2479,http://127.0.0.1: 2479 \--advertise-client-urls http://192.168.1.17:2479,http://127.0.0.1: 2479 \--initial-cluster-token etcd-cluster-1 \ --initial-cluster cd0=http://127.0.0.1:2380,cd1=http://127.0.0.1: 2480,cd2=http://127.0.0.1: 2580 \--initial-cluster-stateNew ;;3) echo-E"[3]start Third server\n"./ETCD--name CD2--initial-advertise-peer-urls http://127.0.0.1:2580 \--listen-peer-urls http://127.0.0.1:2580 \--listen-client-urls http://192.168.1.17:2579,http://127.0.0.1: 2579 \--advertise-client-urls http://192.168.1.17:2579,http://127.0.0.1: 2579 \--initial-cluster-token etcd-cluster-1 \ --initial-cluster cd0=http://127.0.0.1:2380,cd1=http://127.0.0.1: 2480,cd2=http://127.0.0.1: 2580 \--initial-cluster-stateNew ;;*) echo"Error Paramater";; Esac View Code
Complete code, please visit git@github.com:moonlong/etcd-discovery.git
Welcome Treatise
Finish!