github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/edge/edge.go (about) 1 package edge 2 3 import ( 4 "bufio" 5 "fmt" 6 "net/http" 7 "os" 8 "os/signal" 9 "runtime/debug" 10 "time" 11 12 "go.opentelemetry.io/otel/codes" 13 14 "github.com/ronaksoft/rony" 15 "github.com/ronaksoft/rony/errors" 16 "github.com/ronaksoft/rony/internal/metrics" 17 "github.com/ronaksoft/rony/internal/msg" 18 "github.com/ronaksoft/rony/log" 19 "github.com/ronaksoft/rony/pools" 20 "github.com/ronaksoft/rony/registry" 21 "github.com/ronaksoft/rony/tools" 22 "go.opentelemetry.io/otel/propagation" 23 semconv "go.opentelemetry.io/otel/semconv/v1.7.0" 24 "go.opentelemetry.io/otel/trace" 25 "go.uber.org/zap" 26 ) 27 28 /* 29 Creation Time: 2020 - Feb - 21 30 Created by: (ehsan) 31 Maintainers: 32 1. Ehsan N. Moosa (E2) 33 Auditor: Ehsan N. Moosa (E2) 34 Copyright Ronak Software Group 2020 35 */ 36 37 type MessageKind byte 38 39 const ( 40 _ MessageKind = iota 41 GatewayMessage 42 TunnelMessage 43 ) 44 45 var ( 46 messageKindNames = map[MessageKind]string{ 47 GatewayMessage: "GatewayMessage", 48 TunnelMessage: "TunnelMessage", 49 } 50 ) 51 52 func (c MessageKind) String() string { 53 return messageKindNames[c] 54 } 55 56 // Server represents Edge serve. Edge server sticks all other parts of the system together. 57 type Server struct { 58 // General 59 name string 60 serverID []byte 61 logger log.Logger 62 63 // Handlers 64 preHandlers []Handler 65 handlers map[uint64]*HandlerOption 66 postHandlers []Handler 67 68 // Edge components 69 router rony.Router 70 cluster rony.Cluster 71 tunnel rony.Tunnel 72 gateway rony.Gateway 73 dispatcher Dispatcher 74 restMux *restMux 75 76 // Tracing 77 tracer trace.Tracer 78 propagator propagation.TraceContext 79 } 80 81 func NewServer(serverID string, opts ...Option) *Server { 82 // Initialize metrics 83 metrics.Init(map[string]string{ 84 "ServerID": serverID, 85 }) 86 87 edgeServer := &Server{ 88 handlers: make(map[uint64]*HandlerOption), 89 serverID: []byte(serverID), 90 dispatcher: &DefaultDispatcher{}, 91 logger: log.With("EDGE"), 92 } 93 94 for _, opt := range opts { 95 opt(edgeServer) 96 } 97 98 // register builtin rony handlers 99 builtin := newBuiltin(edgeServer) 100 edgeServer.SetHandler(NewHandlerOptions().SetConstructor(rony.C_GetNodes).Append(builtin.getNodes)) 101 edgeServer.SetHandler(NewHandlerOptions().SetConstructor(rony.C_GetAllNodes).Append(builtin.getAllNodes)) 102 edgeServer.SetHandler(NewHandlerOptions().SetConstructor(rony.C_Ping).Append(builtin.ping)) 103 104 return edgeServer 105 } 106 107 // GetServerID return this server id, which MUST be unique in the cluster otherwise 108 // the behaviour is unknown. 109 func (edge *Server) GetServerID() string { 110 return string(edge.serverID) 111 } 112 113 // SetGlobalPreHandlers set the handler which will be called before executing any request. 114 // These pre handlers are like middlewares which will be automatically triggered for each request. 115 // If you want to set pre handler for specific request then you must use SetHandlers, 116 // PrependHandlers or AppendHandlers 117 func (edge *Server) SetGlobalPreHandlers(handlers ...Handler) { 118 edge.preHandlers = handlers 119 } 120 121 // SetGlobalPostHandlers set the handler which will be called after executing any request. 122 // These pre handlers are like middlewares which will be automatically triggered for each request. 123 // If you want to set post handler for specific request then you must use SetHandlers, 124 // PrependHandlers or AppendHandlers 125 func (edge *Server) SetGlobalPostHandlers(handlers ...Handler) { 126 edge.postHandlers = handlers 127 } 128 129 // SetHandler set the handlers for the constructor. 130 func (edge *Server) SetHandler(ho *HandlerOption) { 131 for c := range ho.constructors { 132 edge.handlers[c] = ho 133 } 134 } 135 136 // GetHandler returns the handlers of the constructor 137 func (edge *Server) GetHandler(constructor uint64) *HandlerOption { 138 return edge.handlers[constructor] 139 } 140 141 // SetRestProxy set a REST wrapper to expose RPCs in REST (Representational State Transfer) format 142 func (edge *Server) SetRestProxy(method string, path string, p RestProxy) { 143 if edge.restMux == nil { 144 edge.restMux = &restMux{ 145 routes: map[string]*trie{}, 146 } 147 } 148 edge.restMux.Set(method, path, p) 149 } 150 151 // SetCustomDispatcher replace the default dispatcher with your custom dispatcher. Usually you don't need to 152 // use custom dispatcher, but in some rare cases you can implement your own custom dispatcher 153 func (edge *Server) SetCustomDispatcher(d Dispatcher) { 154 edge.dispatcher = d 155 } 156 157 // Cluster returns a reference to the underlying cluster of the Edge server 158 func (edge *Server) Cluster() rony.Cluster { 159 return edge.cluster 160 } 161 162 // Gateway returns a reference to the underlying gateway of the Edge server 163 func (edge *Server) Gateway() rony.Gateway { 164 return edge.gateway 165 } 166 167 // Tunnel returns a reference to the underlying tunnel of the Edge server 168 func (edge *Server) Tunnel() rony.Tunnel { 169 return edge.tunnel 170 } 171 172 func (edge *Server) execute(dispatchCtx *DispatchCtx) (err error) { 173 waitGroup := pools.AcquireWaitGroup() 174 switch dispatchCtx.req.GetConstructor() { 175 case rony.C_MessageContainer: 176 x := &rony.MessageContainer{} 177 err = x.Unmarshal(dispatchCtx.req.Message) 178 if err != nil { 179 return 180 } 181 xLen := len(x.Envelopes) 182 for i := 0; i < xLen; i++ { 183 ctx := acquireRequestCtx(dispatchCtx, x.SyncRun) 184 waitGroup.Add(1) 185 go func(ctx *RequestCtx, idx int) { 186 edge.executeFunc(ctx, x.Envelopes[idx]) 187 if x.SyncRun { 188 select { 189 case ctx.nextChan <- struct{}{}: 190 default: 191 } 192 } 193 waitGroup.Done() 194 releaseRequestCtx(ctx) 195 }(ctx, i) 196 197 if x.SyncRun { 198 // wait until we allowed to go to next 199 <-ctx.nextChan 200 } 201 } 202 default: 203 ctx := acquireRequestCtx(dispatchCtx, false) 204 edge.executeFunc(ctx, dispatchCtx.req) 205 releaseRequestCtx(ctx) 206 } 207 waitGroup.Wait() 208 pools.ReleaseWaitGroup(waitGroup) 209 210 return nil 211 } 212 func (edge *Server) executeFunc(requestCtx *RequestCtx, in *rony.MessageEnvelope) { 213 defer edge.recoverPanic(requestCtx, in) 214 215 startTime := tools.CPUTicks() 216 217 // Set the context request 218 requestCtx.reqID = in.RequestID 219 220 ho, ok := edge.handlers[in.GetConstructor()] 221 if !ok { 222 requestCtx.PushError(errors.ErrInvalidHandler) 223 224 return 225 } 226 227 if edge.tracer != nil { 228 switch in.Constructor { 229 case rony.C_Ping, rony.C_GetAllNodes, rony.C_GetNodes: 230 default: 231 requestCtx.ctx = edge.propagator.Extract(requestCtx.ctx, in.Carrier()) 232 var span trace.Span 233 requestCtx.ctx, span = edge.tracer. 234 Start( 235 requestCtx.ctx, 236 fmt.Sprintf("rpc.%s/%s", ho.serviceName, ho.methodName), 237 trace.WithAttributes( 238 semconv.RPCServiceKey.String(ho.serviceName), 239 semconv.RPCMethodKey.String(ho.methodName), 240 ), 241 trace.WithSpanKind(trace.SpanKindServer), 242 ) 243 defer span.End() 244 } 245 } 246 247 // Run the handler 248 if !ho.builtin { 249 for idx := range edge.preHandlers { 250 edge.preHandlers[idx](requestCtx, in) 251 if requestCtx.stop { 252 break 253 } 254 } 255 } 256 if !requestCtx.stop { 257 for idx := range ho.handlers { 258 ho.handlers[idx](requestCtx, in) 259 if requestCtx.stop { 260 break 261 } 262 } 263 } 264 if !ho.builtin { 265 for idx := range edge.postHandlers { 266 edge.postHandlers[idx](requestCtx, in) 267 } 268 } 269 if !requestCtx.stop { 270 requestCtx.StopExecution() 271 } 272 273 if ce := edge.logger.Check(log.DebugLevel, "Request Executed"); ce != nil { 274 ce.Write( 275 zap.String("ServerID", edge.GetServerID()), 276 zap.String("Kind", requestCtx.Kind().String()), 277 zap.Uint64("ReqID", in.GetRequestID()), 278 zap.String("C", registry.C(in.GetConstructor())), 279 zap.Duration("T", time.Duration(tools.CPUTicks()-startTime)), 280 ) 281 } 282 283 metrics.AddCounterVec(metrics.CntRPC, 1, registry.C(in.Constructor)) 284 285 if requestCtx.err == nil { 286 requestCtx.Span().SetStatus(codes.Ok, "") 287 } else { 288 requestCtx.Span().SetStatus(codes.Error, requestCtx.err.Error()) 289 } 290 291 switch requestCtx.Kind() { 292 case GatewayMessage: 293 metrics.ObserveHistogram( 294 metrics.HistGatewayRequestTime, 295 float64(time.Duration(tools.CPUTicks()-startTime)/time.Millisecond), 296 ) 297 case TunnelMessage: 298 metrics.ObserveHistogram( 299 metrics.HistTunnelRequestTime, 300 float64(time.Duration(tools.CPUTicks()-startTime)/time.Millisecond), 301 ) 302 } 303 } 304 func (edge *Server) recoverPanic(ctx *RequestCtx, in *rony.MessageEnvelope) { 305 if r := recover(); r != nil { 306 edge.logger.Error("Panic Recovered", 307 zap.String("C", registry.C(in.Constructor)), 308 zap.Uint64("ReqID", in.RequestID), 309 zap.Uint64("ConnID", ctx.ConnID()), 310 zap.Any("Error", r), 311 ) 312 edge.logger.Error("Panic Stack Trace", zap.ByteString("Stack", debug.Stack())) 313 ctx.PushError(errors.ErrInternalServer) 314 } 315 } 316 317 func (edge *Server) onGatewayMessage(conn rony.Conn, streamID int64, data []byte) { 318 // Fill dispatch context with data. We use the GatewayDispatcher or consume data directly based on the 319 // byPassDispatcher argument 320 dispatchCtx := acquireDispatchCtx(edge, conn, streamID, edge.serverID, GatewayMessage) 321 defer releaseDispatchCtx(dispatchCtx) 322 323 // if it is REST API then we take a different approach. 324 if !conn.Persistent() && edge.restMux != nil { 325 if rc, ok := conn.(rony.RestConn); ok { 326 path, p := edge.restMux.Search(rc) 327 if p != nil { 328 if edge.tracer != nil { 329 var span trace.Span 330 dispatchCtx.ctx = edge.propagator.Extract(dispatchCtx.ctx, &carrier{c: rc}) 331 dispatchCtx.ctx, span = edge.tracer. 332 Start( 333 dispatchCtx.ctx, 334 fmt.Sprintf("%s %s", rc.Method(), path), 335 trace.WithAttributes( 336 semconv.HTTPMethodKey.String(rc.Method()), 337 ), 338 trace.WithSpanKind(trace.SpanKindServer), 339 ) 340 defer span.End() 341 } 342 343 edge.onGatewayRest(dispatchCtx, rc, p) 344 345 return 346 } 347 } 348 } 349 350 err := edge.dispatcher.Decode(data, dispatchCtx.req) 351 if err != nil { 352 return 353 } 354 355 err = edge.execute(dispatchCtx) 356 if err != nil { 357 edge.onError(dispatchCtx, errors.ErrInternalServer) 358 } else { 359 edge.dispatcher.Done(dispatchCtx) 360 } 361 } 362 func (edge *Server) onGatewayRest(ctx *DispatchCtx, conn rony.RestConn, proxy RestProxy) { 363 // apply the transformation on the client message before execute it 364 err := proxy.ClientMessage(conn, ctx) 365 if err != nil { 366 switch err := err.(type) { 367 case *rony.Error: 368 conn.WriteStatus(errors.Code(err.Code).HttpStatus()) 369 b, _ := err.MarshalJSON() 370 _ = conn.WriteBinary(0, b) 371 default: 372 conn.WriteStatus(http.StatusInternalServerError) 373 b, _ := errors.New(errors.Internal, err.Error()).MarshalJSON() 374 _ = conn.WriteBinary(0, b) 375 } 376 377 return 378 } 379 380 err = edge.execute(ctx) 381 if err != nil { 382 switch err := err.(type) { 383 case *rony.Error: 384 conn.WriteStatus(errors.Code(err.Code).HttpStatus()) 385 b, _ := err.MarshalJSON() 386 _ = conn.WriteBinary(0, b) 387 default: 388 conn.WriteStatus(http.StatusInternalServerError) 389 b, _ := errors.New(errors.Internal, err.Error()).MarshalJSON() 390 _ = conn.WriteBinary(0, b) 391 } 392 393 return 394 } 395 396 // apply the transformation on the server message before sending it to the client 397 err = proxy.ServerMessage(conn, ctx) 398 if err != nil { 399 switch err := err.(type) { 400 case *rony.Error: 401 conn.WriteStatus(errors.Code(err.Code).HttpStatus()) 402 b, _ := err.MarshalJSON() 403 _ = conn.WriteBinary(0, b) 404 default: 405 conn.WriteStatus(http.StatusInternalServerError) 406 b, _ := errors.New(errors.Internal, err.Error()).MarshalJSON() 407 _ = conn.WriteBinary(0, b) 408 } 409 } 410 } 411 func (edge *Server) onGatewayConnect(conn rony.Conn, kvs ...*rony.KeyValue) { 412 edge.dispatcher.OnOpen(conn, kvs...) 413 } 414 func (edge *Server) onGatewayClose(conn rony.Conn) { 415 edge.dispatcher.OnClose(conn) 416 } 417 func (edge *Server) onTunnelMessage(conn rony.Conn, tm *msg.TunnelMessage) { 418 // Fill the dispatch context envelope from the received tunnel message 419 dispatchCtx := acquireDispatchCtx(edge, conn, 0, tm.SenderID, TunnelMessage) 420 tm.Envelope.DeepCopy(dispatchCtx.req) 421 422 if err := edge.execute(dispatchCtx); err != nil { 423 edge.onError(dispatchCtx, errors.ErrInternalServer) 424 } else { 425 edge.onTunnelDone(dispatchCtx) 426 } 427 428 // Release the dispatch context 429 releaseDispatchCtx(dispatchCtx) 430 } 431 func (edge *Server) onTunnelDone(ctx *DispatchCtx) { 432 tm := msg.PoolTunnelMessage.Get() 433 defer msg.PoolTunnelMessage.Put(tm) 434 switch ctx.BufferSize() { 435 case 0: 436 return 437 case 1: 438 tm.SenderReplicaSet = edge.cluster.ReplicaSet() 439 tm.SenderID = edge.serverID 440 ctx.BufferPop(func(envelope *rony.MessageEnvelope) { 441 envelope.DeepCopy(tm.Envelope) 442 buf := pools.Buffer.FromProto(tm) 443 _ = ctx.Conn().WriteBinary(ctx.streamID, *buf.Bytes()) 444 pools.Buffer.Put(buf) 445 }) 446 default: 447 // TODO:: implement it 448 panic("not implemented, handle multiple tunnel message") 449 } 450 } 451 func (edge *Server) onError(ctx *DispatchCtx, err *rony.Error) { 452 envelope := rony.PoolMessageEnvelope.Get() 453 err.ToEnvelope(envelope) 454 switch ctx.kind { 455 case GatewayMessage: 456 _ = edge.dispatcher.Encode(ctx.conn, ctx.streamID, envelope) 457 rony.PoolMessageEnvelope.Put(envelope) 458 case TunnelMessage: 459 ctx.BufferPush(envelope) 460 } 461 } 462 463 // StartCluster is non-blocking function which runs the cluster component of the Edge server. 464 func (edge *Server) StartCluster() (err error) { 465 if edge.cluster == nil { 466 edge.logger.Info("Cluster is NOT set", 467 zap.ByteString("ServerID", edge.serverID), 468 ) 469 470 return errors.ErrClusterNotSet 471 } 472 473 err = edge.cluster.Start() 474 if err != nil { 475 return 476 } 477 478 edge.logger.Info("Cluster Started", 479 zap.ByteString("ServerID", edge.serverID), 480 zap.String("Cluster", edge.cluster.Addr()), 481 zap.Uint64("ReplicaSet", edge.cluster.ReplicaSet()), 482 ) 483 484 return 485 } 486 487 // StartGateway is non-blocking function runs the gateway in background, so we can accept clients requests 488 func (edge *Server) StartGateway() error { 489 if edge.gateway == nil { 490 edge.logger.Info("Gateway is NOT set", 491 zap.ByteString("ServerID", edge.serverID), 492 ) 493 494 return errors.ErrGatewayNotSet 495 } 496 edge.gateway.Start() 497 498 edge.logger.Info("Gateway Started", 499 zap.ByteString("ServerID", edge.serverID), 500 zap.String("Protocol", edge.gateway.Protocol().String()), 501 zap.Strings("Addr", edge.gateway.Addr()), 502 ) 503 504 if edge.cluster != nil { 505 return errors.WrapText("cluster:")(edge.cluster.SetGatewayAddrs(edge.gateway.Addr())) 506 } 507 508 return nil 509 } 510 511 // StartTunnel is non-blocking function runs the gateway in background, so we can accept other servers requests 512 func (edge *Server) StartTunnel() error { 513 if edge.tunnel == nil { 514 edge.logger.Info("Tunnel is NOT set", 515 zap.ByteString("ServerID", edge.serverID), 516 ) 517 518 return errors.ErrTunnelNotSet 519 } 520 edge.tunnel.Start() 521 522 edge.logger.Info("Tunnel Started", 523 zap.ByteString("ServerID", edge.serverID), 524 zap.Strings("Addr", edge.tunnel.Addr()), 525 ) 526 527 if edge.cluster != nil { 528 return edge.cluster.SetTunnelAddrs(edge.tunnel.Addr()) 529 } 530 531 return nil 532 } 533 534 // Start is a helper function which tries to start all three parts of the edge server 535 // This function does not return any error, if you need to make sure all the parts are started with 536 // no error, then you must start each section separately. i.e. use StartGateway, StartCluster and StartTunnel 537 // functions. 538 func (edge *Server) Start() { 539 if err := edge.StartCluster(); err != nil && err != errors.ErrClusterNotSet { 540 panic(err) 541 } 542 if err := edge.StartGateway(); err != nil && err != errors.ErrGatewayNotSet { 543 panic(err) 544 } 545 if err := edge.StartTunnel(); err != nil && err != errors.ErrTunnelNotSet { 546 panic(err) 547 } 548 } 549 550 // Shutdown gracefully shutdown the services 551 func (edge *Server) Shutdown() { 552 // First shutdown gateway to not accept any more request 553 if edge.gateway != nil { 554 edge.gateway.Shutdown() 555 } 556 557 // Shutdown the cluster 558 if edge.cluster != nil { 559 edge.cluster.Shutdown() 560 } 561 562 // Shutdown the tunnel 563 if edge.tunnel != nil { 564 edge.tunnel.Shutdown() 565 } 566 567 edge.logger.Info("Server Shutdown!", zap.ByteString("ID", edge.serverID)) 568 } 569 570 // ShutdownWithSignal blocks until any of the signals has been called 571 func (edge *Server) ShutdownWithSignal(signals ...os.Signal) error { 572 edge.WaitForSignal(signals...) 573 if edge.cluster != nil { 574 if err := edge.cluster.Leave(); err != nil { 575 return err 576 } 577 } 578 edge.Shutdown() 579 580 return nil 581 } 582 583 func (edge *Server) WaitForSignal(signals ...os.Signal) { 584 ch := make(chan os.Signal, 1) 585 signal.Notify(ch, signals...) 586 587 // Wait for signal 588 <-ch 589 } 590 591 // GetGatewayConn return the gateway connection identified by connID or returns nil if not found. 592 func (edge *Server) GetGatewayConn(connID uint64) rony.Conn { 593 if edge.gateway == nil { 594 return nil 595 } 596 597 conn := edge.gateway.GetConn(connID) 598 if conn == nil { 599 return nil 600 } 601 602 return conn 603 } 604 605 // TunnelRequest sends and receives a request through the Tunnel interface of the receiver Edge node. 606 func (edge *Server) TunnelRequest(replicaSet uint64, req, res *rony.MessageEnvelope) error { 607 return edge.TryTunnelRequest(1, 0, replicaSet, req, res) 608 } 609 610 func (edge *Server) TryTunnelRequest( 611 attempts int, retryWait time.Duration, replicaSet uint64, 612 req, res *rony.MessageEnvelope, 613 ) error { 614 if edge.tunnel == nil { 615 return errors.ErrTunnelNotSet 616 } 617 startTime := tools.CPUTicks() 618 err := tools.Try(attempts, retryWait, func() error { 619 target := edge.getReplicaMember(replicaSet) 620 if target == nil { 621 return errors.ErrMemberNotFound 622 } 623 624 return edge.sendRemoteCommand(target, req, res) 625 }) 626 metrics.ObserveHistogram( 627 metrics.HistTunnelRoundTripTime, 628 float64(time.Duration(tools.CPUTicks()-startTime)/time.Millisecond), 629 ) 630 631 return err 632 } 633 func (edge *Server) getReplicaMember(replicaSet uint64) (target rony.ClusterMember) { 634 members := edge.cluster.MembersByReplicaSet(replicaSet) 635 if len(members) == 0 { 636 return nil 637 } 638 for idx := range members { 639 target = members[idx] 640 641 break 642 } 643 644 return 645 } 646 func (edge *Server) sendRemoteCommand(target rony.ClusterMember, req, res *rony.MessageEnvelope) error { 647 conn, err := target.Dial() 648 if err != nil { 649 return err 650 } 651 652 // Get a rony.TunnelMessage from pool and put it back into the pool when we are done 653 tmOut := msg.PoolTunnelMessage.Get() 654 defer msg.PoolTunnelMessage.Put(tmOut) 655 tmIn := msg.PoolTunnelMessage.Get() 656 defer msg.PoolTunnelMessage.Put(tmIn) 657 tmOut.Fill(edge.serverID, edge.cluster.ReplicaSet(), req) 658 659 // Marshal and send over the wire 660 buf := pools.Buffer.FromProto(tmOut) 661 _, err = conn.Write(*buf.Bytes()) 662 pools.Buffer.Put(buf) 663 if err != nil { 664 return err 665 } 666 667 // Wait for response and unmarshal it 668 buf = pools.Buffer.GetLen(4096) 669 _ = conn.SetReadDeadline(time.Now().Add(time.Second * 3)) 670 n, err := bufio.NewReader(conn).Read(*buf.Bytes()) 671 if err != nil || n == 0 { 672 return err 673 } 674 err = tmIn.Unmarshal((*buf.Bytes())[:n]) 675 if err != nil { 676 return err 677 } 678 pools.Buffer.Put(buf) 679 680 // deep copy 681 tmIn.Envelope.DeepCopy(res) 682 683 return nil 684 } 685 686 type carrier struct { 687 c rony.RestConn 688 } 689 690 func (c *carrier) Get(key string) string { 691 v, _ := c.c.Get(tools.ToCamel(key)).(string) 692 693 return v 694 } 695 696 func (c *carrier) Set(key string, value string) { 697 c.c.Set(key, value) 698 } 699 700 func (c *carrier) Keys() []string { 701 var keys []string 702 c.c.Walk(func(k string, v interface{}) bool { 703 keys = append(keys, k) 704 705 return true 706 }) 707 708 return keys 709 }