Service Control practice of Golang

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

The control of programs and services is essentially the right start-up and can be controlled to stop or exit. In the go language, is actually the program security exits, the service control two aspects. The core is System signal acquisition, Go Concurrency Patterns, and Basic code encapsulation.

Program Safe exit

Execute code non-secure notation

After the code has been deployed, we may need to stop or restart the service because of changes in service configuration or for various other reasons. Typically a for loop is blocked, the code is run, and then forced to exit by Control+c or kill. The code is as follows:

//file svc1.gopackage mainimport (    "fmt"    "time")//当接收到Control+c,kill -1,kill -2,kill -9 均无法正常执行defer函数func main() {    fmt.Println("application is begin.")    //以下代码不会执行    defer fmt.Println("application is end.")    for {        time.Sleep(time.Second)        fmt.Println("application is running.")    }}

This is a simple and rough way, and often enough. However, in this case, the program will not execute defer code, so the end operation will not be processed correctly, some critical log records, message notifications, and very insecure. At this point, you need to introduce a simple framework to perform the exit.

Basic code execution: signal interception

Because the Go language keyword Go is very useful, through the standard library, we can be very graceful to achieve the interception of exit signals:

//file Svc2.go PackageMainImport("FMT"    "Time"    "Os/signal"    "OS")defer function can be executed when CONTROL+C,KILL-1,KILL-2 is received//Kill-9 still does not exit normally. funcMain () {FMT. Println ("Application is begin.")//When the program accepts the exit signal, it will execute    deferFmt. Println ("Application is end.")//The anonymous function initiated by the coprocessor, simulating the business code    Go func(){ for{time. Sleep (time. Second) fmt. Println ("Application is running.")        }    }()//Capture program exit signalmsgchan:= Make(ChanOs. Signal, 1) signal. Notify (Msgchan,os. Interrupt,os. Kill) <-msgchan}

At this point, we implement the program exit signal interception, to complement the business code on it. But the actual business logic involves at least the initialization, the business processing, the exit three chunks, the code quantity is more, will appear to be more chaotic, this needs to standardize the code the structure.

Code Execution improvements: signal blocker wrapper

In view of the above, we define the normal program as:

    • Init: System initialization, such as identification of operating system, initialization of service Discovery Consul, Zookeper agent, database connection pool, etc.
    • Start: The main business logic of the program, including but not limited to data loading, service registration, and specific business response.
    • Stop: The business when the program exits, mainly including memory data storage, service logoff.

Based on this definition, the previous svc2.go only preserves the business code, which can be rewritten as follows:

//file Svc3.go PackageMainImport("FMT"    "Time"    "Study1/svc")typeProgramstruct{}func(P *program) Start () error {FMT. Println ("Application is begin.")//must be non-blocking, so it is encapsulated through a co-process.     Go func(){ for{time. Sleep (time. Second) fmt. Println ("Application is running.")        }    }()return Nil}func(P *program) Init () error{//just Demon,do Nothing    return Nil}func(P *program) Stop () error {FMT. Println ("Application is end.")return Nil}The defer function can be executed when the Control+c,kill-1,kill-2 is received//Kill-9 still does not exit normally. funcMain () {p:=&program{} svc. Run (P)}

The Init, Start, and stop of program in the appeal code actually implements the relevant interface definition, which is used by the Run method in the SVC package. The code is as follows:

//file Svc.go PackageSvcImport("OS"    "Os/signal")//Standard program execution and exit execution interface, run the program to implement the interface definition methodtypeServiceInterface{Init () errorThe code that needs to be executed when the program starts running. Do not block. Start () Error//The code that needs to be executed when the program exits. Do not block. Stop () Error}varMsgchan = Make(ChanOs. Signal,1)//The program runs, exits the wrapper container, the main program is called directly. funcRun (Service service) error {ifERR: = Service. Init (); Err! =Nil{returnERR}ifERR: = Service. Start (); Err! =Nil{returnERR} signal. Notify (Msgchan, OS. Interrupt, OS. Kill) <-msgchanreturnService. Stop ()}//usually does not need to be called, in special cases, in other modules within the program, it is necessary to notify the program to exit. funcInterrupt () {Msgchan<-os. Interrupt}

In this code, the run of the SVG package is only called by the unique main. To support other exit modes, such as the exit of a user-typed character command, a "backdoor"--interrupt method is added. There will be specific use cases in the rear. Because a process will only have one SVG. Instances of the service, which are usually sufficient to use.

Application startup and exit framework for single-machine multi-service

In Web applications, there may be more complex situations that we need to consider:

    • Program Startup
    • Multi-service startup, parallel run, and exit if the program does not exit
    • The program exits and cleans up the running service

Can do a simple demon program, to achieve the above three points, wherein the program exit can be entered through the keyboard command, can also be control+d. Based on golang1.7, we can use the following knowledge points:

    • Using Cancelcontext to control the exit of a service
    • Using previously implemented SVC to implement safe exit of the program
    • Use the OS. StdIn to get the keyboard input command to simulate the service loading with the exit message driver. May actually be a network RPC or HTTP data trigger

golang1.7 's context Pack

We know that when the channel Chan is close, any <-chan will be executed immediately. If not clear, you can consult the relevant information or write a test code, it is best to study Golang official information: Go Concurrency patterns:pipelines and cancellation.
With this feature, we can implement global or individual service control by injecting the new context package from the golang1.7 standard library.
The context interface is defined in the context, and we use several different methods to obtain different implementations. Including:

    • Withdeadline\withtimeout, gets the time-dependent exit handle to control the service exit.
    • Withcancel, gets the cancelfunc handle to control the exit of the service.
    • Withvalue, gets the K-v key value pair, implements the business support similar to the session information preservation.
    • The root of the background\todo,conext, usually as the parent of the above three methods.

The context pack is not new, and 2014 has been used in google.org/x/net as an extension repository for many open source projects (GIN, Iris, etc.). The way the CSP is applied is well worth reading.

Capturing keyboard input

Through Os.stdin to get keyboard input, its parsing needs bufilo.reader to assist processing. The usual code format is:

//...//初始化键盘读取reader:=bufilo.NewReader(os.Stdin)//阻塞,直到敲入Enter键input, _, _ := reader.ReadLine()command:=string(input)//...

Sample code

With these two concepts, it is convenient to implement a simple micro-service loading, exit framework. The reference code is as follows:

//file Svc4.go PackageMainImport("Bufio"    "Context"    "Errors"    "FMT"    "OS"    "Strings"    "Study1/svc"    "Sync"    "Time")typeProgramstruct{CTX context. Context Exitfunc context. Cancelfunc CancelfuncMap[string]context. CANCELFUNC WG Waitgroupwrapper}funcMain () {p: = &program{cancelfunc: Make(Map[string]context. Cancelfunc),} p.ctx, P.exitfunc = context. Withcancel (context. Background ()) Svc. Run (P)}func(P *program) Init () Error {//just Demon,do Nothing    return Nil}func(P *program) Start () error {FMT. Println ("This program will start or stop the service according to the input. "Reader: = Bufio. Newreader (OS. Stdin)Go func() { for{FMT. Println ("program exit command: Exit; service start command: <start| | s>-[name]; Service Stop command: <cancel| | C>-[name]. Please note the case! ") Input, _, _: = Reader. ReadLine () Command: =string(input)Switchcommand { Case "Exit":GotoOutloopdefault: command, name, err: = Splitinput (Input)ifErr! =Nil{FMT. PRINTLN (ERR)Continue}Switchcommand { Case "Start","S": Newctx, Cancelfunc: = context. Withcancel (p.ctx) p.cancelfunc[name] = Cancelfunc P.wg.wrap (func() {Func (NEWCTX, Name)}) Case "Cancel","C": Cancelfunc, founded: = P.cancelfunc[name]iffounded {Cancelfunc ()}}}} Outloop://Because the program exits being blocked by the run's os.notify, call the following method to notify exit code execution. Svc. Interrupt ()} ()return Nil}func(P *program) Stop () error {P.exitfunc () p.wg.wait () fmt. Println ("All services terminated, program exited!")return Nil}//used to convert input string to input commandfuncSplitinput (input []byte) (Command, namestring, err Error) {line: =string(input) STRs: = Strings. Split (Line,"-")ifSTRs = =Nil||Len(STRs)! =2{err = errors. New ("The input does not conform to the rule. ")returnCommand = STRs[0] name = STRs[1]return}//A simple loop method that simulates the loaded, freed MicroServicesfuncFunc (CTX context. Context, Namestring) { for{Select{ Case<-ctx. Done ():GotoOutloop Case<-time. Tick (time. Second *2): FMT. Printf ("%s is running.\n", name)}}outloop:fmt. Printf ("%s is end.\n", name)}//waitgroup Package StructuretypeWaitgroupwrapperstruct{Sync. Waitgroup}func(W *waitgroupwrapper) Wrap (ffunc()) {W.add(1)Go func() {f () W.done ()} ()}

When the code is running, you can:

    • Start a service by entering "s" or "start-" + Service Name
    • Use "C" or "cancel-" + Service name to exit the specified service
    • You can use "exit" or control+c, kill to exit the program (except Kill-9).

On this basis, the context package can also be used to implement service timeout exit, using the for range to limit the number of services, using HTTP to implement micro-service restful information driven. Since the expansion of code after the increase, appear redundant, here no longer repeat.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.