[go] 如何用Golang构建服务(守护进程)

[go] 如何用Golang构建服务(守护进程) 如果主题标题表述有误,我在此先行致歉。
问题的核心是……

当我在服务器(Ubuntu、Debian 等系统)上运行应用程序时,我会打开命令行并输入命令 ./app,这样我的应用程序就能运行。但当我关闭命令行时,应用程序也会随之关闭……

如何让我的程序在我关闭命令行或离开服务器后仍然保持运行?

6 回复

谢谢,我试试看

更多关于[go] 如何用Golang构建服务(守护进程)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


谢谢,当我在工具箱中运行’run’后,如果此时我不在服务器上,我的应用程序还会继续工作吗?

您可以进行远程部署,因此无需在远程机器上操作(实际上这正是设计初衷)。您可以在自己的Unix/Linux机器上进行开发和测试,然后部署到目标机器上。请查阅相关文档了解具体操作方法。

错误的方式:在 screentmux 会话中启动并分离它。

正确的方式:使用服务器的服务管理器(例如 systemd)并为其编写一个服务定义,使其能够理解你的程序。

这与 Go 语言无关,而是与你实际使用的系统相关。

在Unix/Linux系统上部署Go程序时,我使用一个特别创建的工具,它是我工具箱的一部分,该工具还支持运行和分离操作。相关文档请参阅此处

在Go语言中,要让程序在关闭命令行或退出服务器后继续运行,你需要将程序作为守护进程(daemon)运行。这可以通过几种方式实现,包括使用系统服务管理器(如systemd)或利用Go语言本身的特性结合操作系统调用。下面我将详细介绍两种常见的方法,并提供示例代码。

方法1:使用systemd(推荐用于Linux系统如Ubuntu、Debian)

systemd是现代Linux发行版中常用的初始化系统和服务管理器。你可以创建一个systemd服务单元文件来管理你的Go应用程序,使其在系统启动时自动运行,并在后台作为守护进程持续运行。

步骤:

  1. 编写一个简单的Go程序(例如,一个HTTP服务器)。
  2. 编译Go程序为可执行文件。
  3. 创建一个systemd服务文件。
  4. 启用并启动服务。

示例Go程序(main.go): 这个示例是一个简单的HTTP服务器,监听8080端口。

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, this is a Go daemon application!\n")
    })

    log.Println("Starting server on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("Server failed to start: ", err)
    }
}

编译程序:

go build -o app main.go

创建systemd服务文件:/etc/systemd/system/目录下创建一个服务文件,例如myapp.service

sudo nano /etc/systemd/system/myapp.service

文件内容如下:

[Unit]
Description=My Go Application Daemon
After=network.target

[Service]
Type=simple
User=your_username
Group=your_group
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/app/app
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
  • 替换your_usernameyour_group/path/to/your/app为实际值。例如,如果应用在/home/user/myapp目录,用户是user,组是user
  • Restart=always确保应用在崩溃时自动重启。

启用并启动服务:

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

现在,你的Go应用程序将作为守护进程运行,即使关闭命令行或退出服务器也不会停止。你可以使用以下命令检查状态:

sudo systemctl status myapp.service

方法2:使用Go语言结合os/exec和syscall包(不推荐用于生产环境)

如果你不想依赖systemd,可以在Go程序内部使用操作系统调用来实现守护进程行为。这涉及forking进程和脱离终端控制。但请注意,这种方法可能更复杂,且不如systemd稳定,通常用于简单场景或测试。

示例Go程序(daemon.go): 这个示例演示了如何通过forking进程来创建守护进程。

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    // 检查是否已经是守护进程(通过环境变量或参数判断)
    if os.Getenv("DAEMON") != "1" {
        // 第一次运行,fork子进程
        cmd := exec.Command(os.Args[0], os.Args[1:]...)
        cmd.Env = append(os.Environ(), "DAEMON=1")
        cmd.SysProcAttr = &syscall.SysProcAttr{
            Setsid: true, // 创建新会话,脱离终端
        }
        cmd.Stdout = nil // 重定向输出到/dev/null或日志文件
        cmd.Stderr = nil
        cmd.Stdin = nil

        if err := cmd.Start(); err != nil {
            log.Fatal("Failed to start daemon: ", err)
        }
        log.Printf("Daemon started with PID: %d\n", cmd.Process.Pid)
        os.Exit(0) // 父进程退出
    }

    // 守护进程的代码逻辑
    log.Println("Daemon is running...")
    // 这里放置你的应用程序主逻辑,例如HTTP服务器
    select {} // 阻塞主线程,保持运行
}

编译并运行:

go build -o app daemon.go
./app

这种方法会使程序在后台运行,但缺乏systemd的监控和自动重启功能。建议在生产环境中使用systemd。

总结

  • 对于生产环境,推荐使用systemd来管理Go应用程序作为守护进程。它提供了可靠的进程监控、日志管理和自动重启。
  • 如果只是临时需求,可以使用Go内部的forking方法,但需注意其局限性。

通过以上方法,你的Go应用程序将能够在关闭命令行后持续运行。如果有更多细节需求(如日志处理),可以进一步配置systemd服务或Go程序。

回到顶部