Run Go program in the future process mode

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. Since 1999 I have been writing services for Windows, starting with C + + and later in C #. Now I'm writing server-side software on Linux with Go. But I can't do it. What's even more frustrating is that the operating system I used to write the software was not the one I was going to deploy. Of course, that's something. I want to run the code in a background process (daemon) on my Mac. And my problem is, I can't do it. I was fortunate to find an open source project called [Service] (HTTPS://BITBUCKET.ORG/KARDIANOS/SERVICE/SRC) written by Daniel Theophanes on BitBucket. Through its code I learned how to create, install, start, and stop daemons on Mac OS. Of course, this project also supports Linux and Windows. # # Background process on Mac OS There are two types of background processes on Mac OS. Daemon (daemons) and agent Processes (Agents). This is explained as follows: * * Daemon runs in the background as part of the entire system (that is, it is not associated with a particular user). The daemon does not have a graphical interface and does not even allow the connection of the Windows Server (window servers, not Microsoft windows). The WEB server is a typical example. * * The agent process is different, it runs in the background as a specific user. It can do a lot of things that the daemon can't do, such as reliably accessing the user's home directory or connecting to a window server. For more information, visit: [http://developer.apple.com/library/mac/#documentation/macosx/conceptual/bpsystemstartup/chapters/ Introduction.html] (http://developer.apple.com/library/mac/#documentation/macosx/conceptual/bpsystemstartup/ chapters/introduction.html) Let's see how to configure a daemon on Mac OS. [Mac-os-daemons] (Https://raw.githubusercontent.com/studygolang/gctt-images/master/background-process/Screen-shot-2013-07-28-at-9.51.35-am.png) Open Finder You can find the following directory. The Launchdaemons directory under the Library is used to store launchd. plist files for us. /system There is also a library/launchdaemons directory, which is used to service the daemon of the operating system. On Mac OS, the LAUNCHD program is a service management framework that is used to manage (including start and stop) daemons, applications, processes, and scripts. Once the kernel launches the LAUNCHD program, it will start scanning a series of directories on the system, including scripts under/etc,/library and launchagents,launchdaemons directories under/system/library. The program found under Launchdaemons will run as root. This is a sample of the Launchd. plist file, which contains basic configuration information: "' Xml<?xml version= ' 1.0 ' encoding= ' UTF-8 '? ><! DOCTYPE plist public \ "-//apple computer//dtd plist 1.0//en\" \ "http://www.apple.com/dtds/propertylist-1.0.dtd\" > <plist version= ' 1.0 ' ><dict> <key>label</key><string>my service</string> < Key>programarguments</key> <array> <string>/Users/bill/MyService/MyService</string> </array> <key>WorkingDirectory</key><string>/Users/bill/MyService</string> <key >standardoutpath</key><string>/users/bill/myservice/my.log</string> <key>KeepAlive</key><true/> <key>Disabled</key><false/></dict> </plist> "Here you can find a variety of different configuration items in the. plist file: [https://developer.apple.com/library/mac/documentation/Darwin/ Reference/manpages/man5/launchd.plist.5.html] (https://developer.apple.com/library/mac/documentation/Darwin/ reference/manpages/man5/launchd.plist.5.html) programarguments field is very important: "' Xml<key>programarguments</key ><array><string>/Users/bill/MyService/MyService</string></array> ' Through this field, You can specify the program to run and other parameters that are required to pass to the program's main method. The two fields of WorkingDirectory and Standardoutpath are also useful: "' xml<key>workingdirectory</key><string>/users/ Bill/myservice</string><key>standardoutpath</key><string>/users/bill/myservice/my.log </string> "with the launchd. plist file, we can use a program called Launchctl to get our program started in a daemon way. The "Launchctl load/library/launchdaemons/myservice.plist" LAUNCHCTL Program provides service control and reporting capabilities. The load command is used to start a daemon based on launchd. plist. To verify that the program has beenSuccessfully launched you can use the List command: "' Launchctl listpid Status label948-0x7ff4a9503410.anonymous.launchctl946-my service910-0x7ff4a94 2ce00.anonymous.bash ' can see that My Service is running with a PID of 946. Now to stop the program you can use the Unload command: "' Launchctl unload/library/launchdaemons/myservice.plistlaunchctl listpid Status Label948- The 0x7ff4a9503410.anonymous.launchctl910-0x7ff4a942ce00.anonymous.bash ' program was terminated. We also need to process the start and stop requests that the operating system sends when the program starts and terminates. # # OS related go file you can write a go file that is specific to the platform only. [Go-specific-platform] (https://raw.githubusercontent.com/studygolang/gctt-images/master/background-process/ Screen-shot-2013-07-28-at-9.52.55-am.png) in my liteide project you can see 5 Go source files. There are 3 file names that contain the name of the target platform (Darwin (MAC), Linux, and Windows). Because I am now building under Mac OS, the ' service_linux.go ' and ' Service_windows.go ' two files are ignored by the compiler. This naming convention is recognized by the compiler by default. This is a cool feature because you always have to deal with different things on different platforms and use a few different packages. For example, in the ' service_windows.go ' file, the following are quoted: "" "Code.google.com/p/winsvc/eventlog" "Code.google.com/p/winsvc/mgr" " Code.google.com/p/winsvc/svc "" I don't currently have these packages installed because I'm not going to run it on Windows. But this does not affect the build because ' SerVice_windows.go ' was neglected. There is another benefit. Because only one of these files will be compiled, I can reuse the types and method names in these files. In other words, any code that uses the package does not need to be modified after the platform has been changed. It's really cool. # # Service Interface each service must implement three interfaces in order to provide command and control functions. ' Gotype Service interface {Installercontrollerrunner}type Installer interface {Install (config *config) errorremove () Error}type Controller Interface {Start () Errorstop () error} type Runner interface {Run (config *config) error} ' Installer The interface provides the logic to install and uninstall background processes on a specific operating system. The Controller interface provides the logic to start and stop programs from the command line. The last Runner interface is used to implement all application logic that executes as a service when requested. # # The implementation under Darwin since this article is for Mac OS, I'm focused on explaining the implementation of the Service_darwin.go source file. The Installer interface needs to implement two methods, Install and Remove. As stated above, we need to create a launchd. plist file for the service. The best way to complete this step is to use the Text/template package. The _installscript function uses a multiline string (string value) to create a template for the launchd. plist file. "' Gofunc _installscript (script string) {return ' <?xml version= ' 1.0 ' encoding= ' UTF-8 '? ><! DOCTYPE plist public \ "-//apple computer//dtd plist 1.0//en\" \ "[Http://www.apple.com/DTDs/PropertyList-1.0.dtd] (.. /.. /broken-link.html) \ "><plist version= ' 1.0 ' ><dict> <key> label</key><string><b>{{. Displayname}}</b></string> <key>ProgramArguments</key> <array><string><b >{{. workingdirectory}}</b>/<b>{{. Executablename}}</b></string> </array> <key>WorkingDirectory</key><string> <b>{{. Workingdirectory}}</b></string> <key>standardoutpath</key><string><b>{{. loglocation}}</b>/<b>{{. Name}}</b>.log</string> <key>KeepAlive</key><true/> <key>disabled</key ><false/></dict></plist> '} ' multi-line string The cool thing is that the carriage return, line breaks and spaces are not ignored. Because this is just a template, we need to allow the variables to be replaced by real data. {. Variable_name}} Expressions are used to define these variables. The following is the implementation of the Install method: "Gofunc (Service *_darwinlaunchdservice) Install (config *config) error {confpath: = Service._ Getservicefilepath () _, Err: = OS. Stat (confpath) If Err = = Nil {return FMT. Errorf ("Init already exists:%s", Confpath)}file, err: = OS. Create (Confpath) if err! = Nil {retUrn Err}defer file. Close () Parameters: = struct {executablename stringworkingdirectory stringname stringdisplayname stringlongdescription Stringloglocation String}{service._config.executablename,service._config.workingdirectory,service._config.name, Service._config.displayname,service._config.longdescription,service._config.loglocation,}template: = template. Must (template. New ("Launchdconfig"). Parse (_installscript ())) return template. The Execute (file, &parameters)} ' _getservicefilepath () ' function is used to obtain the path to the configuration file under different platforms. Darwin is like this: "Gofunc (Service *_darwinlaunchdservice) _getservicefilepath () string {return FMT. Sprintf ("/library/launchdaemons/%s.plist", Service._config.name)} "then the code checks whether the file already exists and does not exist to create an empty file." Then we create a struct and populate the template. The parameters required for the Execute function call. Note the name of the field must match the name of the ' {{. variable_name}} ' variable in the template. The Execute function processes the template and writes it to the disk file. A Controller interface requires two methods, Start and Stop. The code implementation in Darwin is simple: "Gofunc (Service *_darwinlaunchdservice) Start () error {Confpath: = Service._getservicefilepath () CMD: = Exec.command ("LaunchCTL "," Load ", Confpath) return cmd. Run ()}func (service *_darwinlaunchdservice) Stop () error {Confpath: = Service._getservicefilepath () cmd: = exec. Command ("Launchctl", "unload", Confpath) return cmd. Run ()} ", as we said earlier, each method invokes the LAUNCHCTL program. This is an easy way to start and stop the daemon process. The last Runner interface to be implemented is only a method called Run. Gofunc (service *_darwinlaunchdservice) Run (config *config) error {defer func () {if r: = Recover (); r! = nil {fmt. Println ("******> SERVICE PANIC:", R)}} () Fmt. Print ("******> initing service\n") if config. Init! = Nil {If err: = config. Init (); Err! = Nil {return err}}fmt. Print ("******> starting service\n") if config. Start! = Nil {If err: = config. Start (); Err! = Nil {return err}}fmt. Print ("******> Service started\n")//Create a channel to talk with the Osvar Sigchan = Make (Chan os. Signal, 1) Signal. Notify (Sigchan, OS. Interrupt)//Wait for an eventwhatsig: = <-sigchanfmt.print ("******> Service shutting down\n") if config. Stop! = Nil {If err: = config. Stop (); Err! = Nil {return err}}fmt. Print("******> Service down\n") return nil} ' when the program starts to run as a daemon, the Run method is called. It first invokes the user's OnInit and OnStart methods. The user does some initialization work, executes their program, and then returns. Then create a channel to interact with the operating system. Signal. Notify binds the various events that the channel uses to receive the operating system. The code then executes an infinite loop until the operating system has an event notification program. The code looks for the Shutdown event. Once a shutdown event is accepted, the user's OnStop method is called, and the Run method returns and terminates the program. # # Service Manager Service Manager provides all boilerplate code, so any program can easily implement the service. The following code shows the Run method for config: "gofunc (config *config) run () {var err errorconfig. Service, err = newservice (config) if err! = Nil {fmt. Printf ("%s Unable to start:%s", config. DisplayName, err) return}//Perform a command and then returnif len (OS. Args) > 1 {verb: = os. Args[1]switch verb {case "install": If err: = Service. Install (config); Err! = Nil {fmt. Println ("Failed to install:", err) return}fmt. Printf ("Service \"%s\ "installed.\n", config. DisplayName) returncase "Remove": If err: = Service. Remove (); Err! = Nil {fmt. Println ("Failed to remove:", err) return}fmt. Printf ("Service \"%s\ "removed.\n", config. DisplayName) returncase "debug": Config. Start (config) fmt. Println ("Starting upIn Debug Mode ") Reader: = Bufio. Newreader (OS. Stdin) reader. ReadString (' \ n ') fmt. PRINTLN ("shutting down") config. Stop (config) returncase "start": If Err: = Service. Start (); Err! = Nil {fmt. Println ("Failed to start:", err) return}fmt. Printf ("Service \"%s\ "started.\n", config. DisplayName) returncase "Stop": If err: = Service. Stop (); Err! = Nil {fmt. Println ("Failed to stop:", err) return}FMT. Printf ("Service \"%s\ "stopped.\n", config. DisplayName) returndefault:fmt. Printf ("Options for \"%s\ ": (Install | remove | debug | start | stop) \ n", Os. Args[0]) return}}//Run the Serviceservice. Run (config)} "the Run method starts by creating a service object from the configuration provided. It then queries the incoming command-line arguments. If the argument is a command, the corresponding command executes and the program terminates. If the command is debug, the program starts with a similar service unless it is not hooked into the operating system. Click the ' <enter> ' button to end the program. If no command-line arguments are passed in, the code is invoked by invoking the service. The Run method attempts to start as a daemon. # # Service Implementation The following code is an example of using the Service Framework: ' ' Gopackage mainimport ("FMT" "Path/filepath" "Github.com/goinggo/service/v1") func Main () {//Capture the working directoryworkingdirectory, _: = FilePath. Abs ("")//Create a Config object toStart the ServiceConfig: = service. Config{executablename: "MyService", Workingdirectory:workingdirectory,name: "MyService", DisplayName: "My Service", LongDescription: "My Service provides support for ...", LogLocation: _straps.strap ("Basefilepath"), Init:initservice, start:startservice,stop:stopservice,}//Run any command line options or start the serviceconfig. Run ()}func Initservice () {fmt. Println ("Service inited")}func StartService () {fmt. Println ("Service Started")}func StopService () {fmt. Println ("Service Stopped")} ' Init,start and Stop method must return config immediately after execution. Run method. My code has been tested on Mac OS. The code under Linux is the same in addition to the scripts that are created and installed. Of course the Start and Stop methods are implemented using different programs. Soon I will test some of the code under Linux. As for Windows, you need to refactor the code, and I'm not building it anymore. If you're planning to use Windows, you can start writing yourself from Daniel's code. Once the code is built, you can open the terminal and execute different commands. "./myservice debug./myservice install./myservice start./myservice Stop" and as always, I hope this code will help you create and run your own services.

Via:https://www.ardanlabs.com/blog/2013/06/running-go-programs-as-background.html

Author: William Kennedy Translator: Alfred-zhong proofreading: polaris1119

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

1265 reads ∙1 likes

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.