This is a creation in Article, where the information may have evolved or changed.
Exiting a running program can be rough kill-9 $PID, but this will disrupt the integrity of the business, it is possible that one is executing in the middle of the logic, resulting in abnormal garbage data.
This article summarizes in go language, how can gracefully exit the network application, involves the knowledge including: Signal,channel,waitgroup and so on.
From here: Https://gobyexample.com/channel-synchronization can simply learn how to use the channel in go to achieve goroutines synchronization.
In NSQ, the same mechanism is used, but the encapsulation is more complicated. We use the implementation of Nsqadmin as an example for a simple analysis.
Code Snippet 1 (from: https://github.com/bitly/nsq/blob/master/nsqadmin/main.go):
Exitchan: = make (chan int)
Signalchan: = Make (chan os. Signal, 1)
Go func () {
<-signalchan
Exitchan <-1
}()
Signal. Notify (Signalchan, Syscall. SIGINT, Syscall. SIGTERM)
//....
Nsqadmin. Main ()
<-exitchan
Nsqadmin. Exit ()
When the above code executes normally, it snaps to the <-exitchan of the penultimate sentence until there is data entry in the Exitchan.
When we execute from the command line: Kill-s SIGINT $PID, Signalchan receives a message that goroutines in the fourth line of code stops blocking, continues to execute down, and adds a piece of data to Exitchann. When there is data in the Exitchann, the penultimate sentence stops blocking and executes the nsqadmin. Exit () for graceful exit.
Of course, the above code can also be simplified:
Signalchan: = Make (chan os. Signal, 1)
Signal. Notify (Signalchan, Syscall. SIGINT, Syscall. SIGTERM)
//....
Nsqadmin. Main ()
<-signalchan
Nsqadmin. Exit ()
As to why NSQ did not do so, it is not clear. Ability is limited, can not realize its profound meaning.
The above example only applies to two goroutines, a processing after the business to Exitchan write data, the main Goroutines card in the Exitchan of the upper-class data. (The primary goroutines must exit after processing the goroutines of the business).
If multiple sub-goroutines are opened under a main thread, the channel is not elegant enough. can use Waitgroup, about Waitgroup Introduction can refer to: http://www.baiyuxiong.com/?p=913
The NSQ also uses the Waitgroup implementation to exit.
Code Snippet 2 (from: Https://github.com/bitly/nsq/blob/master/util/wait_group_wrapper.go)
Type Waitgroupwrapper struct {
Sync. Waitgroup
}
Func (w *waitgroupwrapper) Wrap (CB func ()) {
W.add (1)
Go func () {
CB ()
W.done ()
}()
}
Code snippet 3 (from: <a href= "Https://github.com/bitly/nsq/blob/master/nsqadmin/nsqadmin.go" >https://github.com/bitly/ nsq/blob/master/nsqadmin/nsqadmin.go</a>)
Func (n *nsqadmin) Main () {
HttpListener, err: = Net. Listen ("TCP", N.httpaddr.string ())
If err! = Nil {
N.logf ("Fatal:listen (%s) failed-%s", N.HTTPADDR, Err)
Os. Exit (1)
}
N.httplistener = HttpListener
Httpserver: = Newhttpserver (&context{n})
N.waitgroup.wrap (func () {
Util. Httpserver (N.httplistener, Httpserver, N.opts.logger, "HTTP")
})
N.waitgroup.wrap (func () {n.handleadminactions ()})
}
Func (n *nsqadmin) Exit () {
N.httplistener.close ()
Close (n.notifications)
N.waitgroup.wait ()
}
In code Snippet 2, the Waitgroup is simply encapsulated, the goroutines count plus 1 is turned on, and the count minus 1 is executed.
Code Snippet 3, the Main () method, called two times the Waitgroup.wrap () method, the reference code snippet 2 to know, this will start two sub-goroutines, and make the Waitgroup count add 2. While the Goroutines uses the HTTP packet to listen to the network service, blocking the goroutines, so that the count minus 1 operation cannot be called.
In our Code Snippet 1 we can know that after the command line sent Kill, will execute code snippet 3 of the Exit () method, when the method of N.httplistener.close () is called, the Network Service is interrupted, the block in code Snippet 2 will stop, and then the execution count minus 1, When the count of two sub-goroutines is reduced by 1. N.waitgroup.wait () in the exit () method will continue to execute, the main thread ends, and the program exits.