Go 语言开发的程序部署到 Linux 服务器 这篇文章中提到了让应用程序在后台运行的方法,这篇文章讲的是如何将 Go 程序安装成系统服务(这篇文章中主要讲的 Windows 系统 和 Linux/(systemd) 系统)。

代码实现

最简单的方式是使用 github.com/kardianos/service 这个库,它支持 Windows XP+,Linux/(systemd | Upstart | SysV),和OSX/Launchd。使用示例如下:

package main

import (
    "log"
    "github.com/kardianos/service"
)

var logger service.Logger

type program struct{}

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 {
        logs.Error("创建服务失败,", err)
        return
    }
    if len(os.Args) > 1 {
        if os.Args[1] == "install" {
            err := s.Install()
            if err != nil {
                logs.Error("服务安装失败,", err)
            } else {
                logs.Info("服务安装成功")
            }
            return
        }

        if os.Args[1] == "uninstall" {
            err := s.Uninstall()
            if err != nil {
                logs.Error("服务卸载失败,", err)
            } else {
                logs.Info("服务卸载成功")
            }
            return
        }

        if os.Args[1] == "start" {
            err := s.Start()
            if err != nil {
                logs.Error("启动服务失败,", err)
            } else {
                logs.Info("启动服务成功")
            }
            return
        }

        if os.Args[1] == "stop" {
            err := s.Stop()
            if err != nil {
                logs.Error("停止服务失败,", err)
            } else {
                logs.Info("停止服务成功")
            }
            return
        }
    }

    logs.Info("服务正在启动...")
    err = s.Run()
    if err != nil {
        logs.Error("服务运行失败,", err)
        return
    }
    logs.Info("服务正常停止")
}

如上面的代码,编译程序后可以在控制台使用 ./your-service 命令直接执行程序。如果想集成服务可以使用 ./your-service install 命令安装服务,使用 ./main uninstall 命令卸载服务,使用 ./your-service start 命令启动服务,使用 ./your-service stop 命令停止服务。


在 Windows 平台,可以运行 services.msc 查看服务启动及运行情况。


在 Linux 平台,执行 ./main install 命令安装服务后会生成一个 /etc/systemd/system/<your-service>.service 文件(systemd )。可以使用 systemctl | grep your-service 查看服务是否已安装。

当然你也可以使用 service your-service start启动服务、使用 service your-service stop 停止服务、使用service your-service status命令查看服务运行状态及启动错误信息。如果想要开机启动,可以使用service your-service enable 命令。


Linux systemd 集成

我使用 systemd 来集成 Golang 程序作为一个系统服务的方法如下:

首先新建 systemd 的 service。假设程序放到 /usr/local/bin 目录,首先新建 service 文件,名称叫做 your-service

touch /etc/systemd/system/your-service.service

第二步,编辑该文件,输入的内容如下

[Unit]
Description=服务描述

[Service]
Type=simple
Restart=always
RestartSec=5s
ExecStart=/usr/local/bin/your-service-dir/your-service
WorkingDirectory=/usr/local/bin/your-service-dir/

[Install]
WantedBy=multi-user.target

其中 ExecStart 是 Go 可执行文件的路径。WorkingDirectory 要注意,如果程序中使用了相对路径来加载一些配置文件,如果在 service 中没有配置 WorkingDirectory ,则默认是根路径,所以配置文件就从根路径来寻找,会造成一些意想不到的情况。当然你也可以在程序中使用如下代码指定运行目录:

func main() {
    // 更改程序目录 changed path for service
    path, err := os.Executable()
    if err != nil {
        return
    }
    dir, err := filepath.Abs(filepath.Dir(path))
    if err != nil {
        return
    }
    err = os.Chdir(dir)
    if err != nil {
        return
    }
     
    // you code
 }

最后就可以使用上面提到的命令 service your-service start启动服务、使用 service your-service stop 停止服务、使用service your-service status命令查看服务运行状态及启动错误信息。如果想要开机启动,可以使用service your-service enable 命令。

标签: Windows, Linux, GO, Golang, 服务

添加新评论