github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/manager.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/errors" 23 "github.com/aacfactory/fns/commons/bytex" 24 "github.com/aacfactory/fns/commons/futures" 25 "github.com/aacfactory/fns/commons/signatures" 26 "github.com/aacfactory/fns/commons/versions" 27 "github.com/aacfactory/fns/context" 28 fLog "github.com/aacfactory/fns/logs" 29 "github.com/aacfactory/fns/runtime" 30 "github.com/aacfactory/fns/services" 31 "github.com/aacfactory/fns/services/tracings" 32 "github.com/aacfactory/fns/transports" 33 "github.com/aacfactory/logs" 34 "github.com/aacfactory/workers" 35 "reflect" 36 "sort" 37 "sync" 38 "time" 39 ) 40 41 func NewManager(id string, version versions.Version, address string, cluster Cluster, local services.EndpointsManager, worker workers.Workers, log logs.Logger, dialer transports.Dialer, signature signatures.Signature) ClusterEndpointsManager { 42 v := &Manager{ 43 id: id, 44 version: version, 45 address: address, 46 log: log.With("cluster", "endpoints"), 47 cluster: cluster, 48 local: local, 49 worker: worker, 50 dialer: dialer, 51 signature: signature, 52 registration: &Registration{ 53 values: sync.Map{}, 54 }, 55 } 56 return v 57 } 58 59 type ClusterEndpointsManager interface { 60 services.EndpointsManager 61 FnAddress(ctx context.Context, endpoint []byte, fnName []byte, options ...services.EndpointGetOption) (address string, internal bool, has bool) 62 } 63 64 type Manager struct { 65 id string 66 version versions.Version 67 address string 68 log logs.Logger 69 cluster Cluster 70 local services.EndpointsManager 71 worker workers.Workers 72 dialer transports.Dialer 73 signature signatures.Signature 74 registration *Registration 75 } 76 77 func (manager *Manager) Add(service services.Service) (err error) { 78 err = manager.local.Add(service) 79 if err != nil { 80 return 81 } 82 functions := make(services.FnInfos, 0, len(service.Functions())) 83 for _, fn := range service.Functions() { 84 functions = append(functions, services.FnInfo{ 85 Name: fn.Name(), 86 Readonly: fn.Readonly(), 87 Internal: service.Internal() || fn.Internal(), 88 }) 89 } 90 sort.Sort(functions) 91 info, infoErr := NewService(service.Name(), service.Internal(), functions, service.Document()) 92 if infoErr != nil { 93 err = errors.Warning("fns: create cluster service info failed").WithCause(infoErr).WithMeta("endpoint", service.Name()) 94 return 95 } 96 manager.cluster.AddService(info) 97 return 98 } 99 100 func (manager *Manager) Info() (infos services.EndpointInfos) { 101 infos = manager.registration.Infos() 102 if infos == nil { 103 infos = make(services.EndpointInfos, 0) 104 } 105 local := manager.local.Info() 106 infos = append(infos, local...) 107 sort.Sort(infos) 108 return 109 } 110 111 func (manager *Manager) FnAddress(ctx context.Context, endpoint []byte, fnName []byte, options ...services.EndpointGetOption) (address string, internal bool, has bool) { 112 local, inLocal := manager.local.Get(ctx, endpoint, options...) 113 if inLocal { 114 fnNameString := bytex.ToString(fnName) 115 for _, fn := range local.Functions() { 116 if fn.Name() == fnNameString { 117 address = manager.address 118 internal = fn.Internal() 119 has = true 120 return 121 } 122 } 123 } 124 125 if len(options) == 0 { 126 matched := manager.registration.MaxOne(endpoint) 127 if matched == nil || reflect.ValueOf(matched).IsNil() { 128 return 129 } 130 if fn, hasFn := matched.Functions().Find(fnName); hasFn { 131 address = matched.Address() 132 internal = fn.Internal() 133 has = true 134 } 135 return 136 } 137 138 opt := services.EndpointGetOptions{} 139 for _, option := range options { 140 option(&opt) 141 } 142 143 interval, hasVersion := opt.Versions().Get(endpoint) 144 if hasVersion { 145 matched := manager.registration.Range(endpoint, interval) 146 if matched == nil || reflect.ValueOf(matched).IsNil() { 147 return 148 } 149 if fn, hasFn := matched.Functions().Find(fnName); hasFn { 150 address = matched.Address() 151 internal = fn.Internal() 152 has = true 153 } 154 return 155 } 156 if endpointId := opt.Id(); len(endpointId) > 0 { 157 matched := manager.registration.Get(endpoint, endpointId) 158 if matched == nil || reflect.ValueOf(matched).IsNil() { 159 return 160 } 161 if fn, hasFn := matched.Functions().Find(fnName); hasFn { 162 address = matched.Address() 163 internal = fn.Internal() 164 has = true 165 } 166 return 167 } 168 return 169 } 170 171 func (manager *Manager) Get(ctx context.Context, name []byte, options ...services.EndpointGetOption) (endpoint services.Endpoint, has bool) { 172 // local 173 local, inLocal := manager.local.Get(ctx, name, options...) 174 if inLocal { 175 endpoint = local 176 has = true 177 return 178 } 179 // max one 180 if len(options) == 0 { 181 endpoint = manager.registration.MaxOne(name) 182 has = !reflect.ValueOf(endpoint).IsNil() 183 return 184 } 185 186 opt := services.EndpointGetOptions{} 187 for _, option := range options { 188 option(&opt) 189 } 190 // get by id 191 if eid := opt.Id(); len(eid) > 0 { 192 endpoint = manager.registration.Get(name, eid) 193 has = !reflect.ValueOf(endpoint).IsNil() 194 return 195 } 196 // get by intervals 197 if intervals := opt.Versions(); len(intervals) > 0 { 198 interval, matched := intervals.Get(name) 199 if !matched { 200 return 201 } 202 endpoint = manager.registration.Range(name, interval) 203 has = !reflect.ValueOf(endpoint).IsNil() 204 return 205 } 206 return 207 } 208 209 func (manager *Manager) RequestAsync(ctx context.Context, name []byte, fn []byte, param any, options ...services.RequestOption) (future futures.Future, err error) { 210 // valid params 211 if len(name) == 0 { 212 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil")) 213 return 214 } 215 if len(fn) == 0 { 216 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil")) 217 return 218 } 219 // request 220 req := services.NewRequest(ctx, name, fn, param, options...) 221 // get endpoint 222 var endpointGetOptions []services.EndpointGetOption 223 if endpointId := req.Header().EndpointId(); len(endpointId) > 0 { 224 endpointGetOptions = make([]services.EndpointGetOption, 0, 1) 225 endpointGetOptions = append(endpointGetOptions, services.EndpointId(endpointId)) 226 } 227 if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 { 228 if endpointGetOptions == nil { 229 endpointGetOptions = make([]services.EndpointGetOption, 0, 1) 230 } 231 endpointGetOptions = append(endpointGetOptions, services.EndpointVersions(acceptedVersions)) 232 } 233 endpoint, found := manager.Get(req, name, endpointGetOptions...) 234 if !found { 235 err = errors.NotFound("fns: endpoint was not found"). 236 WithMeta("endpoint", bytex.ToString(name)). 237 WithMeta("fn", bytex.ToString(fn)) 238 return 239 } 240 // get fn 241 function, hasFunction := endpoint.Functions().Find(fn) 242 if !hasFunction { 243 err = errors.NotFound("fns: endpoint was not found"). 244 WithMeta("endpoint", bytex.ToString(name)). 245 WithMeta("fn", bytex.ToString(fn)) 246 return 247 } 248 // log 249 fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn))) 250 // components 251 service, ok := endpoint.(services.Service) 252 if ok { 253 components := service.Components() 254 if len(components) > 0 { 255 services.WithComponents(req, name, components) 256 } 257 } 258 // tracing 259 trace, hasTrace := tracings.Load(req) 260 if hasTrace { 261 trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local") 262 } 263 // promise 264 var promise futures.Promise 265 promise, future = futures.New() 266 // dispatch 267 dispatched := manager.worker.Dispatch(req, services.FnTask{ 268 Fn: function, 269 Promise: promise, 270 }) 271 if !dispatched { 272 // release promise 273 futures.ReleaseUnused(promise) 274 future = nil 275 // tracing 276 if hasTrace { 277 trace.Finish("succeed", "false", "cause", "***TOO MANY REQUEST***") 278 } 279 err = errors.TooMayRequest("fns: too may request, try again later."). 280 WithMeta("endpoint", bytex.ToString(name)). 281 WithMeta("fn", bytex.ToString(fn)) 282 } 283 return 284 } 285 286 func (manager *Manager) Request(ctx context.Context, name []byte, fn []byte, param interface{}, options ...services.RequestOption) (response services.Response, err error) { 287 // valid params 288 if len(name) == 0 { 289 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil")) 290 return 291 } 292 if len(fn) == 0 { 293 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil")) 294 return 295 } 296 297 // request 298 req := services.AcquireRequest(ctx, name, fn, param, options...) 299 defer services.ReleaseRequest(req) 300 // get endpoint 301 var endpointGetOptions []services.EndpointGetOption 302 if endpointId := req.Header().EndpointId(); len(endpointId) > 0 { 303 endpointGetOptions = make([]services.EndpointGetOption, 0, 1) 304 endpointGetOptions = append(endpointGetOptions, services.EndpointId(endpointId)) 305 } 306 if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 { 307 if endpointGetOptions == nil { 308 endpointGetOptions = make([]services.EndpointGetOption, 0, 1) 309 } 310 endpointGetOptions = append(endpointGetOptions, services.EndpointVersions(acceptedVersions)) 311 } 312 endpoint, found := manager.Get(req, name, endpointGetOptions...) 313 if !found { 314 err = errors.NotFound("fns: endpoint was not found"). 315 WithMeta("endpoint", bytex.ToString(name)). 316 WithMeta("fn", bytex.ToString(fn)) 317 return 318 } 319 // get fn 320 function, hasFunction := endpoint.Functions().Find(fn) 321 if !hasFunction { 322 err = errors.NotFound("fns: endpoint was not found"). 323 WithMeta("endpoint", bytex.ToString(name)). 324 WithMeta("fn", bytex.ToString(fn)) 325 return 326 } 327 // log 328 fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn))) 329 // components 330 service, ok := endpoint.(services.Service) 331 if ok { 332 components := service.Components() 333 if len(components) > 0 { 334 services.WithComponents(req, name, components) 335 } 336 } 337 // tracing 338 trace, hasTrace := tracings.Load(req) 339 if hasTrace { 340 trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local") 341 trace.Waited() 342 } 343 // handle 344 result, handleErr := function.Handle(req) 345 if handleErr != nil { 346 codeErr := errors.Wrap(handleErr).WithMeta("endpoint", bytex.ToString(name)).WithMeta("fn", bytex.ToString(fn)) 347 if hasTrace { 348 trace.Finish("succeed", "false", "cause", codeErr.Name()) 349 } 350 err = codeErr 351 return 352 } 353 if hasTrace { 354 trace.Finish("succeed", "true") 355 } 356 response = services.NewResponse(result) 357 return 358 } 359 360 func (manager *Manager) Listen(ctx context.Context) (err error) { 361 // watching 362 manager.watching() 363 // cluster.join 364 err = manager.cluster.Join(ctx) 365 if err != nil { 366 if manager.log.WarnEnabled() { 367 manager.log.Warn().With("cluster", "join").Cause(err).Message("fns: cluster join failed") 368 } 369 return 370 } 371 // local.listen 372 err = manager.local.Listen(ctx) 373 if err != nil { 374 _ = manager.cluster.Leave(ctx) 375 return 376 } 377 return 378 } 379 380 func (manager *Manager) Shutdown(ctx context.Context) { 381 leaveErr := manager.cluster.Leave(ctx) 382 if leaveErr != nil { 383 if manager.log.WarnEnabled() { 384 manager.log.Warn().With("cluster", "leave").Cause(leaveErr).Message("fns: cluster leave failed") 385 } 386 } 387 manager.local.Shutdown(ctx) 388 return 389 } 390 391 func (manager *Manager) watching() { 392 go func(eps *Manager) { 393 for { 394 event, ok := <-eps.cluster.NodeEvents() 395 if !ok { 396 break 397 } 398 if eps.log.DebugEnabled() { 399 eps.log.Debug(). 400 With("event", event.Kind.String()). 401 Message(fmt.Sprintf( 402 "fns: get node(id:%s addr:%s ver:%s, services:%d) event(%s)", 403 event.Node.Id, event.Node.Address, event.Node.Version.String(), len(event.Node.Services), event.Kind.String())) 404 } 405 switch event.Kind { 406 case Add: 407 endpoints := make([]*Endpoint, 0, 1) 408 client, clientErr := eps.dialer.Dial(bytex.FromString(event.Node.Address)) 409 if eps.log.DebugEnabled() { 410 succeed := "succeed" 411 var cause error 412 if clientErr != nil { 413 succeed = "failed" 414 cause = errors.Warning(fmt.Sprintf("fns: dial %s failed", event.Node.Address)).WithMeta("address", event.Node.Address).WithCause(clientErr) 415 } 416 eps.log.Debug(). 417 With("cluster", "registrations"). 418 Cause(cause). 419 Message(fmt.Sprintf("fns: dial %s %s", event.Node.Address, succeed)) 420 } 421 if clientErr != nil { 422 if eps.log.WarnEnabled() { 423 eps.log.Warn(). 424 With("cluster", "registrations"). 425 Cause(errors.Warning(fmt.Sprintf("fns: dial %s failed", event.Node.Address)).WithMeta("address", event.Node.Address).WithCause(clientErr)). 426 Message(fmt.Sprintf("fns: dial %s failed", event.Node.Address)) 427 } 428 break 429 } 430 // check health 431 active := false 432 for i := 0; i < 10; i++ { 433 ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second) 434 if runtime.CheckHealth(ctx, client) { 435 active = true 436 cancel() 437 break 438 } 439 cancel() 440 if eps.log.DebugEnabled() { 441 eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: %s is not health", event.Node.Address)) 442 } 443 time.Sleep(1 * time.Second) 444 } 445 446 if eps.log.DebugEnabled() { 447 eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: health of %s is %v", event.Node.Address, active)) 448 } 449 if !active { 450 break 451 } 452 // get document 453 for _, endpoint := range event.Node.Services { 454 document, documentErr := endpoint.Document() 455 if documentErr != nil { 456 if eps.log.WarnEnabled() { 457 eps.log.Warn(). 458 With("cluster", "registrations"). 459 Cause(errors.Warning("fns: get endpoint document failed").WithMeta("address", event.Node.Address).WithCause(documentErr)). 460 Message(fmt.Sprintf("fns: dial %s failed", event.Node.Address)) 461 } 462 continue 463 } 464 ep := NewEndpoint(manager.log, event.Node.Address, event.Node.Id, event.Node.Version, endpoint.Name, endpoint.Internal, document, client, eps.signature) 465 for _, fnInfo := range endpoint.Functions { 466 ep.AddFn(fnInfo.Name, fnInfo.Internal, fnInfo.Readonly) 467 } 468 endpoints = append(endpoints, ep) 469 } 470 for _, endpoint := range endpoints { 471 eps.registration.Add(endpoint) 472 } 473 if eps.log.DebugEnabled() { 474 eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: %s added", event.Node.Address)) 475 } 476 break 477 case Remove: 478 for _, endpoint := range event.Node.Services { 479 eps.registration.Remove(endpoint.Name, event.Node.Id) 480 } 481 break 482 default: 483 break 484 } 485 } 486 }(manager) 487 return 488 }