1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        「GoCN酷Go推薦」golang 跨平臺部署利器

        共 13525字,需瀏覽 28分鐘

         ·

        2021-09-13 19:36

        1

        研究背景


        go程序部署時,直接將編譯好的文件在服務器上運行即可,一般無需安裝所依賴的第三方庫。

        Linux下部署分為以下幾種方式:

        • 使用nohup 命令
        • 使用 Supervisord管理
        • 使用systemd管理

        windows下部署方式:

        • 注冊成windows服務,方便進行管理

        其實golang自己也可以實現(xiàn)以服務的形式常駐后臺。

        現(xiàn)在介紹一個包 github.com/kardianos/service ,它可以讓 go 程序支持在不同系統(tǒng)上作為服務:注冊,卸載,啟動,停止。

        2

        安裝部署


        go get github.com/kardianos/service

        3

        使用方法


        通過兩個例子,我們來了解其用法。

        3.1 示例程序: simple

        package main

        import (
         "log"

         "github.com/kardianos/service"
        )

        var logger service.Logger //service的日志對象

        type program struct{} //定義結構體,并實現(xiàn)service.Interface的start和stop接口

        func (p *program) Start(s service.Service) error {
         // Start should not block. Do the actual work async.
         go p.run()
         return nil
        }
        func (p *program) run() {
         // Do work here
        }
        func (p *program) Stop(s service.Service) error {
         // Stop should not block. Return with a few seconds.
         return nil
        }

        func main() {
         svcConfig := &service.Config{
          Name:        "GoServiceExampleSimple",
          DisplayName: "Go Service Example",
          Description: "This is an example Go service.",
         }

         prg := &program{}
         s, err := service.New(prg, svcConfig)
         if err != nil {
          log.Fatal(err)
         }
         logger, err = s.Logger(nil)
         if err != nil {
          log.Fatal(err)
         }
         err = s.Run()
         if err != nil {
          logger.Error(err)
         }
        }

        套路如下:

        一、定義結構體program,并實現(xiàn)service的接口函數(shù)Start和Stop

        需要實現(xiàn)的service接口Interface如下:

        type Interface interface {
           // Start provides a place to initiate the service. The service doesn't
           // signal a completed start until after this function returns, so the
           // Start function must not take more then a few seconds at most.
           Start(s Service) error

           // Stop provides a place to clean up program execution before it is terminated.
           // It should not take more then a few seconds to execute.
           // Stop should not call os.Exit directly in the function.
           Stop(s Service) error
        }

        二、初始化service.Config結構體,并調用service.New創(chuàng)建service對象

        三、為service對象設置日志Logger

        四、調用service的Run方法來運行程序

        3.2 示例程序2

        package main

        import (
         "encoding/json"
         "flag"
         "fmt"
         "log"
         "os"
         "os/exec"
         "path/filepath"

         "github.com/kardianos/service"
        )

        // Config is the runner app config structure.
        type Config struct {
         Name, DisplayName, Description string

         Dir  string
         Exec string
         Args []string
         Env  []string

         Stderr, Stdout string
        }

        var logger service.Logger

        type program struct {
         exit    chan struct{}
         service service.Service

         *Config

         cmd *exec.Cmd
        }

        func (p *program) Start(s service.Service) error {
         // Look for exec.
         // Verify home directory.
         fullExec, err := exec.LookPath(p.Exec)
         if err != nil {
          return fmt.Errorf("Failed to find executable %q: %v", p.Exec, err)
         }

         p.cmd = exec.Command(fullExec, p.Args...)
         p.cmd.Dir = p.Dir
         p.cmd.Env = append(os.Environ(), p.Env...)

         go p.run()
         return nil
        }
        func (p *program) run() {
         logger.Info("Starting ", p.DisplayName)
         defer func() {
          if service.Interactive() {
           p.Stop(p.service)
          } else {
           p.service.Stop()
          }
         }()

         if p.Stderr != "" {
          f, err := os.OpenFile(p.Stderr, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777)
          if err != nil {
           logger.Warningf("Failed to open std err %q: %v", p.Stderr, err)
           return
          }
          defer f.Close()
          p.cmd.Stderr = f
         }
         if p.Stdout != "" {
          f, err := os.OpenFile(p.Stdout, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777)
          if err != nil {
           logger.Warningf("Failed to open std out %q: %v", p.Stdout, err)
           return
          }
          defer f.Close()
          p.cmd.Stdout = f
         }

         err := p.cmd.Run()
         if err != nil {
          logger.Warningf("Error running: %v", err)
         }

         return
        }
        func (p *program) Stop(s service.Service) error {
         close(p.exit)
         logger.Info("Stopping ", p.DisplayName)
         if p.cmd.Process != nil {
          p.cmd.Process.Kill()
         }
         if service.Interactive() {
          os.Exit(0)
         }
         return nil
        }

        func getConfigPath() (string, error) {
         fullexecpath, err := os.Executable()
         if err != nil {
          return "", err
         }

         dir, execname := filepath.Split(fullexecpath)
         ext := filepath.Ext(execname)
         name := execname[:len(execname)-len(ext)]

         return filepath.Join(dir, name+".json"), nil
        }

        func getConfig(path string) (*Config, error) {
         f, err := os.Open(path)
         if err != nil {
          return nil, err
         }
         defer f.Close()

         conf := &Config{}

         r := json.NewDecoder(f)
         err = r.Decode(&conf) //將json字符串轉為Config對象
         if err != nil {
          return nil, err
         }
         return conf, nil
        }

        func main() {
         svcFlag := flag.String("service""""Control the system service.")
         flag.Parse()

         configPath, err := getConfigPath() //獲取配置文件路徑
         if err != nil {
          log.Fatal(err)
         }
         config, err := getConfig(configPath) //獲取配置
         if err != nil {
          log.Fatal(err)
         }

         //初始化service配置屬性
         svcConfig := &service.Config{
          Name:        config.Name,
          DisplayName: config.DisplayName,
          Description: config.Description,
         }

         prg := &program{
          exit: make(chan struct{}),

          Config: config,
         }
         s, err := service.New(prg, svcConfig)
         if err != nil {
          log.Fatal(err)
         }
         prg.service = s

         errs := make(chan error, 5)
         logger, err = s.Logger(errs)
         if err != nil {
          log.Fatal(err)
         }

         go func() {
          for {
           err := <-errs
           if err != nil {
            log.Print(err)
           }
          }
         }()

         if len(*svcFlag) != 0 {
             //通過命令行參數(shù)來控制服務
          err := service.Control(s, *svcFlag)
          if err != nil {
           log.Printf("Valid actions: %q\n", service.ControlAction)
           log.Fatal(err)
          }
          return
         }
         err = s.Run()
         if err != nil {
          logger.Error(err)
         }
        }

        其配置文件runner.json如下

        {
         "Name""builder",
         "DisplayName""Go Builder",
         "Description""Run the Go Builder",
         
         "Dir""C:\\dev\\go\\src",
         "Exec""C:\\windows\\system32\\cmd.exe",
         "Args": ["/C","C:\\dev\\go\\src\\all.bat"],
         "Env": [
          "PATH=C:\\TDM-GCC-64\\bin;C:\\Program Files (x86)\\Git\\cmd",
          "GOROOT_BOOTSTRAP=C:\\dev\\go_ready",
          "HOMEDRIVE=C:",
          "HOMEPATH=\\Documents and Settings\\Administrator"
         ],
         
         "Stderr""C:\\builder_err.log",
         "Stdout""C:\\builder_out.log"
        }

        可將service相關的配置信息存儲到配置文件中。通過讀取配置文件中的配置信息來生成service。

        4

        接口定義(源碼剖析)


        這個庫有兩個接口

        4.1 Service接口

        其中Service接口有Run、Start、Stop、Restart、Install、Uninstall等核心方法可實現(xiàn),以對應服務的運行、啟動、停止、重啟、安裝、卸載操作。

        type Service interface {
         // Run should be called shortly after the program entry point.
         // After Interface.Stop has finished running, Run will stop blocking.
         // After Run stops blocking, the program must exit shortly after.
         Run() error

         // Start signals to the OS service manager the given service should start.
         Start() error

         // Stop signals to the OS service manager the given service should stop.
         Stop() error

         // Restart signals to the OS service manager the given service should stop then start.
         Restart() error

         // Install setups up the given service in the OS service manager. This may require
         // greater rights. Will return an error if it is already installed.
         Install() error

         // Uninstall removes the given service from the OS service manager. This may require
         // greater rights. Will return an error if the service is not present.
         Uninstall() error

         // Opens and returns a system logger. If the user program is running
         // interactively rather then as a service, the returned logger will write to
         // os.Stderr. If errs is non-nil errors will be sent on errs as well as
         // returned from Logger's functions.
         Logger(errs chan<- error) (Logger, error)

         // SystemLogger opens and returns a system logger. If errs is non-nil errors
         // will be sent on errs as well as returned from Logger'
        functions.
         SystemLogger(errs chan<- error) (Logger, error)

         // String displays the name of the service. The display name if present,
         // otherwise the name.
         String() string
        }
        image-20210910150157094

        其中aixService、freebsdService、solarisService、systemd、sysv、upstart、windowsService分別對應不同的操作系統(tǒng)aix,freebsd,solaris,linux,windows操作系統(tǒng)上的服務。

        4.2 Interface接口

        Interface接口中有Start和Stop

        type Interface interface {
         // Start provides a place to initiate the service. The service doesn't not
         // signal a completed start until after this function returns, so the
         // Start function must not take more then a few seconds at most.
         Start(s Service) error

         // Stop provides a place to clean up program execution before it is terminated.
         // It should not take more then a few seconds to execute.
         // Stop should not call os.Exit directly in the function.
         Stop(s Service) error
        }


        《酷Go推薦》招募:


        各位Gopher同學,最近我們社區(qū)打算推出一個類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個庫或者好的項目,然后寫一點這個庫使用方法或者優(yōu)點之類的,這樣可以真正的幫助到大家能夠學習到

        新的庫,并且知道怎么用。


        大概規(guī)則和每日新聞類似,如果報名人多的話每個人一個月輪到一次,歡迎大家報名!戳「閱讀原文」,即可報名


        掃碼也可以加入 GoCN 的大家族喲~




        瀏覽 82
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲日韩一区二区在线观看 | 免费看干逼 | 玖玖资源精品站 | 黄色成人网站视频在线观看 | 香蕉漫画羞羞动漫入口 | 国产成人久久婷婷精品流白浆 | 国产精品永久久久 | 国产性猛交XXXX免费看久久 | a性毛片| 巨型怪物h蹂躏3d美女 |