├── Godeps ├── Godeps.json └── Readme ├── example └── main.go ├── README.md └── discovery ├── worker.go └── master.go /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/daizuozhuo/etcd-service-discovery", 3 | "GoVersion": "go1.7", 4 | "GodepVersion": "v74", 5 | "Deps": [] 6 | } 7 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "github.com/daizuozhuo/etcd-service-discovery/discovery" 8 | ) 9 | 10 | func main() { 11 | var role = flag.String("role", "", "master | worker") 12 | flag.Parse() 13 | endpoints := []string{"http://127.0.0.1:2379"} 14 | if *role == "master" { 15 | master := discovery.NewMaster(endpoints) 16 | master.WatchWorkers() 17 | } else if *role == "worker" { 18 | worker := discovery.NewWorker("localhost", "127.0.0.1", endpoints) 19 | worker.HeartBeat() 20 | } else { 21 | fmt.Println("example -h for usage") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # etcd-service-discovery 2 | daizuozhuo.github.io/etcd-service-discovery/ 3 | 4 | ### Build 5 | 1. Install Golang: sudo yum install golang 6 | 2. mkdir $HOME/Go 7 | 3. add environment variable to ~/.bashrc `export GOPATH=$HOME/Go` `export PATH=$HOME/Go/bin:$PATH`, 8 | reload it `source ~/.bashrc` 9 | 4. put the project inside $HOME/Go/src/github.com/daizuozhuo/ 10 | 5. `go get github.com/tools/godep` 11 | 6. `godep restore` 12 | 7. `cd etcd-service-discovery/exmaple" 13 | 8. `go build` 14 | 15 | ### Run 16 | 1. run etcd server on the localhost. 17 | 2. `./example -role master` 18 | 3. `./example -role worker` 19 | -------------------------------------------------------------------------------- /discovery/worker.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/coreos/etcd/client" 10 | "golang.org/x/net/context" 11 | ) 12 | 13 | type Worker struct { 14 | Name string 15 | IP string 16 | KeysAPI client.KeysAPI 17 | } 18 | 19 | // workerInfo is the service register information to etcd 20 | type WorkerInfo struct { 21 | Name string 22 | IP string 23 | CPU int 24 | } 25 | 26 | func NewWorker(name, IP string, endpoints []string) *Worker { 27 | cfg := client.Config{ 28 | Endpoints: endpoints, 29 | Transport: client.DefaultTransport, 30 | HeaderTimeoutPerRequest: time.Second, 31 | } 32 | 33 | etcdClient, err := client.New(cfg) 34 | if err != nil { 35 | log.Fatal("Error: cannot connec to etcd:", err) 36 | } 37 | 38 | w := &Worker{ 39 | Name: name, 40 | IP: IP, 41 | KeysAPI: client.NewKeysAPI(etcdClient), 42 | } 43 | return w 44 | } 45 | 46 | func (w *Worker) HeartBeat() { 47 | api := w.KeysAPI 48 | 49 | for { 50 | info := &WorkerInfo{ 51 | Name: w.Name, 52 | IP: w.IP, 53 | CPU: runtime.NumCPU(), 54 | } 55 | 56 | key := "workers/" + w.Name 57 | value, _ := json.Marshal(info) 58 | 59 | _, err := api.Set(context.Background(), key, string(value), &client.SetOptions{ 60 | TTL: time.Second * 10, 61 | }) 62 | if err != nil { 63 | log.Println("Error update workerInfo:", err) 64 | } 65 | time.Sleep(time.Second * 3) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /discovery/master.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "time" 7 | 8 | "github.com/coreos/etcd/client" 9 | "golang.org/x/net/context" 10 | ) 11 | 12 | type Master struct { 13 | members map[string]*Member 14 | KeysAPI client.KeysAPI 15 | } 16 | 17 | // Member is a client machine 18 | type Member struct { 19 | InGroup bool 20 | IP string 21 | Name string 22 | CPU int 23 | } 24 | 25 | func NewMaster(endpoints []string) *Master { 26 | cfg := client.Config{ 27 | Endpoints: endpoints, 28 | Transport: client.DefaultTransport, 29 | HeaderTimeoutPerRequest: time.Second, 30 | } 31 | 32 | etcdClient, err := client.New(cfg) 33 | if err != nil { 34 | log.Fatal("Error: cannot connec to etcd:", err) 35 | } 36 | 37 | master := &Master{ 38 | members: make(map[string]*Member), 39 | KeysAPI: client.NewKeysAPI(etcdClient), 40 | } 41 | go master.WatchWorkers() 42 | return master 43 | } 44 | 45 | func (m *Master) AddWorker(info *WorkerInfo) { 46 | member := &Member{ 47 | InGroup: true, 48 | IP: info.IP, 49 | Name: info.Name, 50 | CPU: info.CPU, 51 | } 52 | m.members[member.Name] = member 53 | } 54 | 55 | func (m *Master) UpdateWorker(info *WorkerInfo) { 56 | member := m.members[info.Name] 57 | member.InGroup = true 58 | } 59 | 60 | func NodeToWorkerInfo(node *client.Node) *WorkerInfo { 61 | log.Println(node.Value) 62 | info := &WorkerInfo{} 63 | err := json.Unmarshal([]byte(node.Value), info) 64 | if err != nil { 65 | log.Print(err) 66 | } 67 | return info 68 | } 69 | 70 | func (m *Master) WatchWorkers() { 71 | api := m.KeysAPI 72 | watcher := api.Watcher("workers/", &client.WatcherOptions{ 73 | Recursive: true, 74 | }) 75 | for { 76 | res, err := watcher.Next(context.Background()) 77 | if err != nil { 78 | log.Println("Error watch workers:", err) 79 | break 80 | } 81 | if res.Action == "expire" { 82 | info := NodeToWorkerInfo(res.PrevNode) 83 | log.Println("Expire worker ", info.Name) 84 | member, ok := m.members[info.Name] 85 | if ok { 86 | member.InGroup = false 87 | } 88 | } else if res.Action == "set" { 89 | info := NodeToWorkerInfo(res.Node) 90 | if _, ok := m.members[info.Name]; ok { 91 | log.Println("Update worker ", info.Name) 92 | m.UpdateWorker(info) 93 | } else { 94 | log.Println("Add worker ", info.Name) 95 | m.AddWorker(info) 96 | } 97 | } else if res.Action == "delete" { 98 | info := NodeToWorkerInfo(res.Node) 99 | log.Println("Delete worker ", info.Name) 100 | delete(m.members, info.Name) 101 | } 102 | } 103 | } 104 | --------------------------------------------------------------------------------