This is a creation in Article, where the information may have evolved or changed.
agent.NodeStructures have 4 a framework for understanding channel the program by understanding their role swarmd :
// Node implements the primary node functionality for a member of a swarm// cluster. Node handles workloads and may also run as a manager.type Node struct { ...... started chan struct{} stopped chan struct{} ready chan struct{} // closed when agent has completed registration and manager(if enabled) is ready to receive control requests ...... closed chan struct{} ......}
swarmdThe framework of the program (which executor engine-addr , by getting, represents the final running task entity, is actually one Docker engineapi.APIClient . Other parameters are obtained directly from the command line. ):
...//Create a context for we grpc call CTX, Cancel: = context. Withcancel (context. Background ()) defer cancel () ... n, err: = Agent. NewNode (&agent. nodeconfig{Hostname:hostname, Forcenewcluster:forcenewcluster, Listencontrola Pi:unix, Listenremoteapi:addr, Joinaddr:manageraddr, statedir:stated IR, Jointoken:jointoken, ExternalCAs:externalCAOpt.Value (), Executor: Executor, HEARTBEATTICK:HB, Electiontick:election,}) if err! = Nil { Return err} If err: = N.start (CTX); Err! = Nil {return err} c: = Make (chan os. Signal, 1) Signal. Notify (c, OS. Interrupt) go func () {<-c n.stop (CTX)} () go func () {select { Case <-n.reAdy (): Case <-ctx. Done ():} if ctx. ERR () = = Nil {Logrus. Info ("node is Ready")}} () return N.err (CTX)
(1)
if err := n.Start(ctx); err != nil { return err}
Look at Node.Start() the implementation of the function:
// Start starts a node instance.func (n *Node) Start(ctx context.Context) error { select { case <-n.started: select { case <-n.closed: return n.err case <-n.stopped: return errAgentStopped case <-ctx.Done(): return ctx.Err() default: return errAgentStarted } case <-ctx.Done(): return ctx.Err() default: } close(n.started) go n.run(ctx) return nil}
If Node.Start() no exception occurs during execution, it is Node.started channel switched off () and the close(n.started) node initialization process is initiated: go n.run(ctx) .
(2)
c := make(chan os.Signal, 1)signal.Notify(c, os.Interrupt)go func() { <-c n.Stop(ctx)}()
The meaning of this code is that the user Ctrl+C can interrupt the program by pressing. The Node.Stop() function is implemented as follows:
// Stop stops node executionfunc (n *Node) Stop(ctx context.Context) error { select { case <-n.started: select { case <-n.closed: return n.err case <-n.stopped: select { case <-n.closed: return n.err case <-ctx.Done(): return ctx.Err() } case <-ctx.Done(): return ctx.Err() default: close(n.stopped) // recurse and wait for closure return n.Stop(ctx) } case <-ctx.Done(): return ctx.Err() default: return errAgentNotStarted }}
Because this is turned off at this point Node.started , the channel first branch will be executed forever select case : case <-n.started . It then decides which branch to execute according to the situation.
(3)
go func() { select { case <-n.Ready(): case <-ctx.Done(): } if ctx.Err() == nil { logrus.Info("node is ready") }}()
Node.Ready()The function will return Node.ready this channel :
// Ready returns a channel that is closed after node's initialization has// completes for the first time.func (n *Node) Ready() <-chan struct{} { return n.ready}
When Node the initialization is complete, Node.ready this channel will be turned off. So if everything goes well, you'll see " node is ready " log .
(4)
return n.Err(ctx)
Node.Err()The implementation of the function:
// Err returns the error that caused the node to shutdown or nil. Err blocks// until the node has fully shut down.func (n *Node) Err(ctx context.Context) error { select { case <-n.closed: return n.err case <-ctx.Done(): return ctx.Err() }}
Node.Err()The function blocks here, waiting to Node close.