github.com/moleculer-go/moleculer@v0.3.3/registry/node.go (about) 1 package registry 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "net" 9 "time" 10 11 "github.com/moleculer-go/moleculer" 12 "github.com/moleculer-go/moleculer/util" 13 "github.com/moleculer-go/moleculer/version" 14 log "github.com/sirupsen/logrus" 15 ) 16 17 type Node struct { 18 id string 19 sequence int64 20 ipList []string 21 hostname string 22 client map[string]interface{} 23 services []map[string]interface{} 24 isAvailable bool 25 cpu int64 26 cpuSequence int64 27 lastHeartBeatTime int64 28 offlineSince int64 29 isLocal bool 30 logger *log.Entry 31 } 32 33 func discoverIpList() []string { 34 var result []string 35 addrs, err := net.InterfaceAddrs() 36 if err != nil { 37 return make([]string, 0) 38 } 39 for _, address := range addrs { 40 // check the address type and if it is not a loopback the display it 41 if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 42 if ipnet.IP.To4() != nil { 43 result = append(result, ipnet.IP.String()) 44 } 45 } 46 } 47 if result == nil { 48 result = []string{"0.0.0.0"} 49 } 50 return result 51 } 52 53 func discoverHostname() string { 54 hostname, error := os.Hostname() 55 if error != nil { 56 return "unknown" 57 } 58 return hostname 59 } 60 61 func CreateNode(id string, local bool, logger *log.Entry) moleculer.Node { 62 ipList := discoverIpList() 63 hostname := discoverHostname() 64 services := make([]map[string]interface{}, 0) 65 node := Node{ 66 id: id, 67 client: map[string]interface{}{ 68 "type": "moleculer-go", 69 "version": version.Moleculer(), 70 "langVersion": version.Go(), 71 }, 72 ipList: ipList, 73 hostname: hostname, 74 services: services, 75 logger: logger, 76 isLocal: local, 77 sequence: 1, 78 } 79 var result moleculer.Node = &node 80 return result 81 } 82 83 func (node *Node) Update(id string, info map[string]interface{}) (bool, []map[string]interface{}) { 84 if id != node.id { 85 panic(fmt.Errorf("Node.Update() - the id received : %s does not match this node.id : %s", id, node.id)) 86 } 87 node.logger.Debug("node.Update() - info:") 88 node.logger.Debug(util.PrettyPrintMap(info)) 89 90 reconnected := !node.isAvailable 91 92 node.isAvailable = true 93 node.lastHeartBeatTime = time.Now().Unix() 94 node.offlineSince = 0 95 96 node.ipList = interfaceToString(info["ipList"].([]interface{})) 97 node.hostname = info["hostname"].(string) 98 node.client = info["client"].(map[string]interface{}) 99 100 services, removedServices := FilterServices(node.services, info) 101 node.logger.Debug("removedServices:", util.PrettyPrintMap(removedServices)) 102 103 node.services = services 104 node.sequence = int64Field(info, "seq", 0) 105 node.cpu = int64Field(info, "cpu", 0) 106 node.cpuSequence = int64Field(info, "cpuSeq", 0) 107 108 return reconnected, removedServices 109 } 110 111 // FilterServices return all services excluding local services (example: $node) and return all removed services, services that existed in the currentServices list but 112 // no longer exist in th new list coming from the info package 113 func FilterServices(currentServices []map[string]interface{}, info map[string]interface{}) ([]map[string]interface{}, []map[string]interface{}) { 114 item, ok := info["services"] 115 var incomingServices []interface{} 116 if ok { 117 incomingServices = item.([]interface{}) 118 } 119 //get current services 120 services := []map[string]interface{}{} 121 for _, item := range incomingServices { 122 incomingService := item.(map[string]interface{}) 123 incomingName := incomingService["name"].(string) 124 if strings.Index(incomingName, "$") == 0 { 125 //ignore services prefixed with $ (e.g. $node) 126 continue 127 } 128 services = append(services, incomingService) 129 } 130 //check for removed services 131 removedServices := []map[string]interface{}{} 132 for _, currentService := range currentServices { 133 currentName := currentService["name"].(string) 134 135 removed := true 136 for _, service := range services { 137 if currentName == service["name"].(string) { 138 removed = false 139 break 140 } 141 } 142 if removed { 143 removedServices = append(removedServices, currentService) 144 } 145 } 146 return services, removedServices 147 } 148 149 func int64Field(values map[string]interface{}, field string, def int64) int64 { 150 raw, exists := values[field] 151 if !exists { 152 return def 153 } 154 fv, valid := raw.(float64) 155 if !valid { 156 return def 157 } 158 return int64(fv) 159 } 160 161 func interfaceToString(list []interface{}) []string { 162 result := make([]string, len(list)) 163 for index, item := range list { 164 result[index] = item.(string) 165 } 166 return result 167 } 168 169 // ExportAsMap export the node info as a map 170 // this map is used to publish the node info to other nodes. 171 func (node *Node) ExportAsMap() map[string]interface{} { 172 resultMap := make(map[string]interface{}) 173 resultMap["id"] = node.id 174 resultMap["services"] = node.services // node.removeInternalServices(node.services) 175 resultMap["ipList"] = node.ipList 176 resultMap["hostname"] = node.hostname 177 resultMap["client"] = node.client 178 resultMap["seq"] = node.sequence 179 resultMap["cpu"] = node.cpu 180 resultMap["cpuSeq"] = node.cpuSequence 181 resultMap["available"] = node.IsAvailable() 182 resultMap["metadata"] = make(map[string]interface{}) 183 184 return resultMap 185 } 186 187 func (node *Node) GetID() string { 188 return node.id 189 } 190 func (node *Node) IsExpired(timeout time.Duration) bool { 191 if node.IsLocal() || !node.IsAvailable() { 192 return false 193 } 194 diff := time.Now().Unix() - node.lastHeartBeatTime 195 return diff > int64(timeout.Seconds()) 196 } 197 198 func (node *Node) HeartBeat(heartbeat map[string]interface{}) { 199 if !node.isAvailable { 200 node.isAvailable = true 201 node.offlineSince = 0 202 } 203 node.cpu = int64Field(heartbeat, "cpu", 0) 204 node.cpuSequence = int64Field(heartbeat, "cpuSeq", 0) 205 node.lastHeartBeatTime = time.Now().Unix() 206 } 207 208 func (node *Node) Publish(service map[string]interface{}) { 209 node.services = append(node.services, service) 210 } 211 212 func (node *Node) IsAvailable() bool { 213 return node.isLocal || node.isAvailable 214 } 215 216 // Unavailable mark the node as unavailable 217 func (node *Node) Unavailable() { 218 node.isAvailable = false 219 } 220 221 // Unavailable mark the node as available 222 func (node *Node) Available() { 223 node.isAvailable = true 224 } 225 226 func (node *Node) IsLocal() bool { 227 return node.isLocal 228 } 229 230 func (node *Node) IncreaseSequence() { 231 node.sequence++ 232 }