zoukankan      html  css  js  c++  java
  • 使用kardianos-service 创建golang开机自启动服务

    开机自启动服务在实际的应用中还是比较多的,kardianos-service 是golang 的一个很不错的实现,我们增强我们
    golang 应用的可管理性,以下是一个实践说明

    基本使用

    此代码比较简单

    • 代码
    package main
    import (
        "flag"
        "log"
        "time"
        "github.com/kardianos/service"
    )
    var logger service.Logger
    // Program structures.
    //  Define Start and Stop methods.
    type program struct {
        exit chan struct{}
    }
    func (p *program) Start(s service.Service) error {
        if service.Interactive() {
            logger.Info("Running in terminal.")
        } else {
            logger.Info("Running under service manager.")
        }
        p.exit = make(chan struct{})
        // Start should not block. Do the actual work async.
        go p.run()
        return nil
    }
    func (p *program) run() error {
        logger.Infof("I'm running %v.", service.Platform())
        ticker := time.NewTicker(2 * time.Second)
        for {
            select {
            case tm := <-ticker.C:
                logger.Infof("Still running at %v...", tm)
            case <-p.exit:
                ticker.Stop()
                return nil
            }
        }
    }
    func (p *program) Stop(s service.Service) error {
        // Any work in Stop should be quick, usually a few seconds at most.
        logger.Info("I'm Stopping!")
        close(p.exit)
        return nil
    }
    // Service setup.
    //   Define service config.
    //   Create the service.
    //   Setup the logger.
    //   Handle service controls (optional).
    //   Run the service.
    func main() {
        svcFlag := flag.String("service", "", "Control the system service.")
        flag.Parse()
        options := make(service.KeyValue)
        options["Restart"] = "on-success"
        options["SuccessExitStatus"] = "1 2 8 SIGKILL"
        svcConfig := &service.Config{
            Name:        "GoServiceExampleLogging",
            DisplayName: "Go Service Example for Logging",
            Description: "This is an example Go service that outputs log messages.",
            Dependencies: []string{
                "Requires=network.target",
                "After=network-online.target syslog.target"},
            Option: options,
        }
        prg := &program{}
        s, err := service.New(prg, svcConfig)
        if err != nil {
            log.Fatal(err)
        }
        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 {
            err := service.Control(s, *svcFlag)
            if err != nil {
                log.Printf("Valid actions: %q
    ", service.ControlAction)
                log.Fatal(err)
            }
            return
        }
        err = s.Run()
        if err != nil {
            logger.Error(err)
        }
    }
    • 重点
      实现一个服务的golang 应用,应该实现以下接口
     
    Start // start 方法不行block,可以基于goruntine 解决
    Stop

    一个参考集成cli 的代码

    没有提供完整代码,集成了logger 以及基于urfave 的cli 服务

    package commands
    import (
        "fmt"
        "log"
        "os"
        "time"
        "github.com/kardianos/service"
        "github.com/robfig/cron/v3"
        "github.com/urfave/cli/v2"
        "github.com/rongfengliang/sql-server-exporter/pkg/agent"
        "github.com/rongfengliang/sql-server-exporter/pkg/buildinfo"
        "github.com/rongfengliang/sql-server-exporter/pkg/jobs"
    )
    var (
        logger service.Logger
        errs   chan error
    )
    const (
        AGENTNAME = "sql-data-exporter-agent"
        JOBNAME   = "sql-data-exporter-job"
    )
    // Server server
    type Server struct {
        jobdirconf string
        cron       *cron.Cron
    }
    type ServerWrapper struct {
        *Server
        jobdirconf string
    }
    func NewServer() *Server {
        return &Server{}
    }
    func init() {
        errs = make(chan error, 5)
    }
    func newServerConf(jobdirconf string) *Server {
        return &Server{
            jobdirconf: jobdirconf,
        }
    }
    // NewServerWrapper newServerWrapper
    func NewServerWrapper(jobdirconf string) *ServerWrapper {
        return &ServerWrapper{
            Server:     newServerConf(jobdirconf),
            jobdirconf: jobdirconf,
        }
    }
    // Start start job Server
    func (server *ServerWrapper) Start(s service.Service) error {
        // Start should not block. Do the actual work async.
        if service.Interactive() {
            logger.Info("Running in terminal.")
        } else {
            logger.Info("Running under service manager.")
        }
        go server.run(server.jobdirconf)
        return nil
    }
    // Stop Stop
    func (server *ServerWrapper) Stop(s service.Service) error {
        time.Sleep(2 * time.Second)
        err := server.stop()
        return err
    }
    func (server *Server) run(jobdirconf string) error {
        jobdir := jobdirconf
        logger.Info("job is running")
        if jobdir != "" {
            loadJobs, cronhub, err := jobs.ParseJobs(jobdir)
            if err != nil {
                return err
            }
            server.cron = cronhub
            for _, v := range loadJobs {
                logger.Info("js engine:" + v.EngineName)
            }
            go cronhub.Run()
        }
        return nil
    }
    func (server *Server) stop() error {
        ctx := server.cron.Stop()
        select {
        case <-ctx.Done():
            return ctx.Err()
        }
    }
    // Serve Serve job service
    func (server *Server) Serve() error {
        // TODos
        // load jobs create scheduler info
        app := cli.NewApp()
        app.Usage = "basic sql server data fetch service"
        app.Flags = []cli.Flag{
            &cli.StringFlag{
                Name:  "jobdirconf",
                Usage: "set job dirs",
                Value: ".",
            },
            &cli.StringFlag{
                Name:  "dbconf",
                Usage: "db agent config",
                Value: "",
            },
        }
        app.Commands = []*cli.Command{
            {
                Name:    "version",
                Aliases: []string{"v"},
                Usage:   "print application version",
                Action: func(c *cli.Context) error {
                    fmt.Println(buildinfo.Version)
                    return nil
                },
            },
            {
                Name:  "job",
                Usage: "start job service",
                Action: func(c *cli.Context) error {
                    jobdir := c.String("jobdirconf")
                    if jobdir != "" {
                        serviceConfig := &service.Config{
                            Name:        "sql-data-exporter-job",
                            Description: "sql-data-exporter-job",
                            DisplayName: "sql-data-exporter-job",
                        }
                        server := NewServerWrapper(jobdir)
                        s, err := service.New(server, serviceConfig)
                        if err != nil {
                            return err
                        }
                        logger, err = s.SystemLogger(errs)
                        if err != nil {
                            return err
                        }
                        err = s.Run()
                        if err != nil {
                            return err
                        }
                        return nil
                    }
                    return nil
                },
            },
            {
                Name:  "agent",
                Usage: "start agent service",
                Action: func(c *cli.Context) error {
                    dbconfig := c.String("dbconf")
                    if dbconfig != "" {
                        serviceConfig := &service.Config{
                            Name:        "sql-data-exporter-agent",
                            Description: "sql-data-exporter-agent",
                            DisplayName: "sql-data-exporter-agent",
                        }
                        server := agent.NewAgentWrapper(dbconfig)
                        s, err := service.New(server, serviceConfig)
                        if err != nil {
                            return err
                        }
                        logger, err = s.SystemLogger(errs)
                        if err != nil {
                            log.Fatal(err.Error())
                        }
                        server.Logger = logger
                        s.Run()
                        return nil
                    }
                    return nil
                },
            },
            {
                Name:  "service",
                Usage: "system service operator",
                Subcommands: []*cli.Command{
                    {
                        Name:  "agent",
                        Usage: "agent servie operator",
                        Subcommands: []*cli.Command{
                            {
                                Name:  "install",
                                Usage: "agent servie install",
                                Action: func(c *cli.Context) error {
                                    dbconfig := c.String("dbconf")
                                    if dbconfig == "" {
                                        dbconfig = "agent.hcl"
                                    }
                                    agentname := c.String("agentname")
                                    if agentname == "" {
                                        agentname = AGENTNAME
                                    }
                                    serviceConfig := &service.Config{
                                        Name:        agentname,
                                        DisplayName: AGENTNAME,
                                        Description: AGENTNAME,
                                        Arguments:   []string{"--dbconf", dbconfig, "agent"},
                                    }
                                    server := agent.NewAgentWrapper(dbconfig)
                                    s, err := service.New(server, serviceConfig)
                                    if err != nil {
                                        fmt.Println("install service wrong: " + err.Error())
                                        return err
                                    }
                                    s.Install()
                                    return nil
                                },
                            },
                        },
                    },
                    {
                        Name:  "job",
                        Usage: "job servie operator",
                        Subcommands: []*cli.Command{
                            {
                                Name:  "install",
                                Usage: "job servie install",
                                Action: func(c *cli.Context) error {
                                    jobdir := c.String("jobdirconf")
                                    if jobdir == "" {
                                        jobdir = "jobs"
                                    }
                                    jobtname := c.String("jobname")
                                    if jobtname == "" {
                                        jobtname = JOBNAME
                                    }
                                    serviceConfig := &service.Config{
                                        Name:        jobtname,
                                        DisplayName: JOBNAME,
                                        Description: JOBNAME,
                                        Arguments:   []string{"--jobdirconf", jobdir, "job"},
                                    }
                                    server := NewServerWrapper(jobdir)
                                    s, err := service.New(server, serviceConfig)
                                    if err != nil {
                                        fmt.Println("install service wrong: " + err.Error())
                                        return err
                                    }
                                    s.Install()
                                    return nil
                                },
                            },
                        },
                    },
                },
            },
        }
        err := app.Run(os.Args)
        if err != nil {
            return err
        }
        return nil
    }
    • 参考效果
    ./bin/exporter-server service
    NAME:
       exporter-server service - system service operator
    USAGE:
       exporter-server service command [command options] [arguments...]
    COMMANDS:
       agent    agent servie operator
       job      job servie operator
       help, h  Shows a list of commands or help for one command
    OPTIONS:
       --help, -h  show help (default: false)

    说明

    我们如果依赖了一些配置(比如关于路径的,需要使用绝对路径),Start 部分需要使用异步模式,kardianos-service
    暴露的Run 方法很重要,我们需要运行(服务管理模式的需要),对于我们原有的代码, 可以通过golang的组合以及包装
    接口提供类似的服务,这样可以系统对于kardianos-service 的依赖可以减少,提高代码的复用

    参考资料

    https://github.com/kardianos/service

  • 相关阅读:
    交叉熵--损失函数
    Window安装TensorFlow- GPU环境
    使用Tensorflow object detection API——训练模型(Window10系统)
    使用Tensorflow object detection API——环境搭建与测试
    由位图生成区域
    旋转位图
    获得当前颜色深度
    求多边形的面积
    MaskBlt 拷贝非矩形区域图象
    画多边型
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/13287889.html
Copyright © 2011-2022 走看看