github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/endpoints.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package clusters 19 20 import ( 21 "fmt" 22 "github.com/aacfactory/fns/commons/bytex" 23 "github.com/aacfactory/fns/commons/signatures" 24 "github.com/aacfactory/fns/commons/versions" 25 "github.com/aacfactory/fns/commons/window" 26 "github.com/aacfactory/fns/context" 27 "github.com/aacfactory/fns/services" 28 "github.com/aacfactory/fns/services/documents" 29 "github.com/aacfactory/fns/transports" 30 "github.com/aacfactory/logs" 31 "math/rand" 32 "sort" 33 "sync" 34 "sync/atomic" 35 "time" 36 ) 37 38 func NewEndpoint(log logs.Logger, address string, id string, version versions.Version, name string, internal bool, document documents.Endpoint, client transports.Client, signature signatures.Signature) (endpoint *Endpoint) { 39 endpoint = &Endpoint{ 40 log: log.With("endpoint", name), 41 info: services.EndpointInfo{ 42 Id: id, 43 Version: version, 44 Address: address, 45 Name: name, 46 Internal: internal, 47 Functions: nil, 48 Document: document, 49 }, 50 running: atomic.Bool{}, 51 functions: make(services.Fns, 0, 1), 52 client: client, 53 signature: signature, 54 errs: window.NewTimes(10 * time.Second), 55 } 56 endpoint.running.Store(true) 57 return 58 } 59 60 type Endpoint struct { 61 log logs.Logger 62 info services.EndpointInfo 63 running atomic.Bool 64 functions services.Fns 65 client transports.Client 66 signature signatures.Signature 67 errs *window.Times 68 } 69 70 func (endpoint *Endpoint) Running() bool { 71 return endpoint.running.Load() 72 } 73 74 func (endpoint *Endpoint) Id() string { 75 return endpoint.info.Id 76 } 77 78 func (endpoint *Endpoint) Address() string { 79 return endpoint.info.Address 80 } 81 82 func (endpoint *Endpoint) Name() string { 83 return endpoint.info.Name 84 } 85 86 func (endpoint *Endpoint) Internal() bool { 87 return endpoint.info.Internal 88 } 89 90 func (endpoint *Endpoint) Document() documents.Endpoint { 91 return endpoint.info.Document 92 } 93 94 func (endpoint *Endpoint) Functions() services.Fns { 95 return endpoint.functions 96 } 97 98 func (endpoint *Endpoint) Shutdown(_ context.Context) { 99 endpoint.running.Store(false) 100 endpoint.client.Close() 101 } 102 103 func (endpoint *Endpoint) IsHealth() bool { 104 return endpoint.errs.Value() < 5 105 } 106 107 func (endpoint *Endpoint) AddFn(name string, internal bool, readonly bool) { 108 fn := &Fn{ 109 log: endpoint.log.With("fn", name), 110 endpointName: endpoint.info.Name, 111 address: endpoint.info.Address, 112 name: name, 113 internal: internal, 114 readonly: readonly, 115 path: bytex.FromString(fmt.Sprintf("/%s/%s", endpoint.info.Name, name)), 116 signature: endpoint.signature, 117 errs: endpoint.errs, 118 health: atomic.Bool{}, 119 client: endpoint.client, 120 } 121 fn.health.Store(true) 122 endpoint.functions = endpoint.functions.Add(fn) 123 endpoint.info.Functions = append(endpoint.info.Functions, services.FnInfo{ 124 Name: name, 125 Readonly: readonly, 126 Internal: internal, 127 }) 128 sort.Sort(endpoint.info.Functions) 129 } 130 131 func (endpoint *Endpoint) Info() services.EndpointInfo { 132 return endpoint.info 133 } 134 135 type VersionEndpoints struct { 136 version versions.Version 137 values []*Endpoint 138 length uint64 139 pos uint64 140 } 141 142 func (endpoints *VersionEndpoints) Add(ep *Endpoint) { 143 endpoints.values = append(endpoints.values, ep) 144 endpoints.length++ 145 return 146 } 147 148 func (endpoints *VersionEndpoints) Remove(id []byte) (ok bool) { 149 if endpoints.length == 0 { 150 return 151 } 152 idStr := bytex.ToString(id) 153 pos := -1 154 for i, value := range endpoints.values { 155 if value.Id() == idStr { 156 pos = i 157 break 158 } 159 } 160 if pos == -1 { 161 return 162 } 163 endpoints.values = append(endpoints.values[:pos], endpoints.values[pos+1:]...) 164 endpoints.length-- 165 ok = true 166 return 167 } 168 169 func (endpoints *VersionEndpoints) Get(id []byte) (ep *Endpoint) { 170 if endpoints.length == 0 { 171 return 172 } 173 idStr := bytex.ToString(id) 174 for _, value := range endpoints.values { 175 if value.Id() == idStr { 176 ep = value 177 return 178 } 179 } 180 return 181 } 182 183 func (endpoints *VersionEndpoints) Next() (ep *Endpoint) { 184 if endpoints.length == 0 { 185 return 186 } 187 for i := uint64(0); i < endpoints.length; i++ { 188 pos := atomic.AddUint64(&endpoints.pos, 1) % endpoints.length 189 target := endpoints.values[pos] 190 if target.Running() && target.IsHealth() { 191 ep = target 192 return 193 } 194 } 195 return 196 } 197 198 type SortedVersionEndpoints []*VersionEndpoints 199 200 func (list SortedVersionEndpoints) Len() int { 201 return len(list) 202 } 203 204 func (list SortedVersionEndpoints) Less(i, j int) bool { 205 return list[i].version.LessThan(list[j].version) 206 } 207 208 func (list SortedVersionEndpoints) Swap(i, j int) { 209 list[i], list[j] = list[j], list[i] 210 return 211 } 212 213 func (list SortedVersionEndpoints) Get(version versions.Version) *VersionEndpoints { 214 for _, vps := range list { 215 if vps.version.Equals(version) { 216 return vps 217 } 218 } 219 return nil 220 } 221 222 func (list SortedVersionEndpoints) Add(ep *Endpoint) SortedVersionEndpoints { 223 vps := list.Get(ep.info.Version) 224 if vps == nil { 225 vps = &VersionEndpoints{ 226 version: ep.info.Version, 227 values: make([]*Endpoint, 0), 228 length: 0, 229 pos: 0, 230 } 231 vps.Add(ep) 232 newList := append(list, vps) 233 sort.Sort(newList) 234 return newList 235 } else { 236 vps.Add(ep) 237 return list 238 } 239 } 240 241 type Endpoints struct { 242 values SortedVersionEndpoints 243 length uint64 244 lock sync.RWMutex 245 } 246 247 func (endpoints *Endpoints) Add(ep *Endpoint) { 248 endpoints.lock.Lock() 249 defer endpoints.lock.Unlock() 250 endpoints.values = endpoints.values.Add(ep) 251 endpoints.length++ 252 return 253 } 254 255 func (endpoints *Endpoints) Remove(id []byte) { 256 endpoints.lock.Lock() 257 defer endpoints.lock.Unlock() 258 evict := -1 259 for i, value := range endpoints.values { 260 if value.Remove(id) { 261 if value.length == 0 { 262 evict = i 263 } 264 break 265 } 266 } 267 if evict == -1 { 268 return 269 } 270 endpoints.values = append(endpoints.values[:evict], endpoints.values[evict+1:]...) 271 endpoints.length-- 272 } 273 274 func (endpoints *Endpoints) MaxOne() (ep *Endpoint) { 275 endpoints.lock.RLock() 276 defer endpoints.lock.RUnlock() 277 if endpoints.length == 0 { 278 return 279 } 280 ep = endpoints.values[endpoints.length-1].Next() 281 return 282 } 283 284 func (endpoints *Endpoints) Get(id []byte) *Endpoint { 285 endpoints.lock.RLock() 286 defer endpoints.lock.RUnlock() 287 if endpoints.length == 0 { 288 return nil 289 } 290 for _, value := range endpoints.values { 291 target := value.Get(id) 292 if target == nil { 293 continue 294 } 295 if target.Running() && target.IsHealth() { 296 return target 297 } 298 return nil 299 } 300 return nil 301 } 302 303 func (endpoints *Endpoints) Range(interval versions.Interval) *Endpoint { 304 endpoints.lock.RLock() 305 defer endpoints.lock.RUnlock() 306 if endpoints.length == 0 { 307 return nil 308 } 309 targets := make([]*Endpoint, 0, 1) 310 for _, value := range endpoints.values { 311 if interval.Accept(value.version) { 312 for _, endpoint := range value.values { 313 if endpoint.Running() && endpoint.IsHealth() { 314 targets = append(targets, endpoint) 315 } 316 } 317 } 318 } 319 pos := rand.Intn(len(targets)) 320 return targets[pos] 321 } 322 323 func (endpoints *Endpoints) Infos() (v services.EndpointInfos) { 324 endpoints.lock.RLock() 325 defer endpoints.lock.RUnlock() 326 if endpoints.length == 0 { 327 return 328 } 329 for _, value := range endpoints.values { 330 for _, ep := range value.values { 331 v = append(v, ep.info) 332 } 333 } 334 return 335 }