如何在 Debian 中将简单的 Go 服务器作为守护进程运行?

Posted

技术标签:

【中文标题】如何在 Debian 中将简单的 Go 服务器作为守护进程运行?【英文标题】:How do I run a simple Go server as a daemon in Debian? 【发布时间】:2016-06-05 15:54:18 【问题描述】:

我最近用 Go 写了一个简单的服务器:

package main
import (
    "net/http"
    "fmt"
    "os/exec"
)

func main() 
    http.HandleFunc("/", handler)
    http.ListenAndServe(":****", nil)
    

func handler(output http.ResponseWriter, input *http.Request) 

    instruction := "Instructed to " + input.URL.Path[1:] + "."

    fmt.Printf(instruction)

    if input.URL.Path[1:] == "********" 

            *************
            *************
            *************                

            if err != nil 
                    fmt.Println("There was a problem executing the script.")
                    
             else 

            fmt.Println(" I'm unfamiliar with this instruction.")
            

    

如果编译后由./go_http_server &执行,效果很好。

问题是它无法在重新启动后继续存在。所以在阅读了一些之后,我尝试通过在 /etc/init.d 中放置一个脚本来守护它:

#!/bin/sh

### BEGIN INIT INFO
# Provides:          myservice
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Put a short description of the service here
# Description:       Put a long description of the service here
### END INIT INFO

# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/****/****
DAEMON=$DIR/go_http_server
DAEMON_NAME=*********

# Add any command line options for your daemon here
DAEMON_OPTS=""

# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=*****

# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid

. /lib/lsb/init-functions

do_start () 
    log_daemon_msg "Starting system $DAEMON_NAME daemon"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
    log_end_msg $?

do_stop () 
    log_daemon_msg "Stopping system $DAEMON_NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    log_end_msg $?


case "$1" in

    start|stop)
        do_$1
        ;;

    restart|reload|force-reload)
        do_stop
        do_start
        ;;

    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?

...然后运行 ​​update-rc.d go_http_server defaults,噗!它在启动时运行,由 ps -ef | 验证。 grep go_http_server.

但它在作为服务运行时不接收 GET 请求。考虑到它可能在网络接口启动之前运行,我尝试了 service go_http_server stop,然后是 service go_http_server start;仍然拒绝接收 GET 请求。再次停止服务然后执行 ./go_http_server & 使服务器再次正常运行。

我已经在谷歌上搜索了几天了。要么我的搜索查询很糟糕,要么这不是一个明显的问题。如何守护我的 Go 服务器?


编辑:完全相同的事情发生在我用 Python 编写的服务器上:它在使用 ./python_server.py 执行时可以正常工作,但是——如果作为服务启动——HTTP 请求被忽略。这两个文件都已成为可执行文件,并且守护程序用户是 root 用户还是任何其他用户都没有关系。不确定这是否有帮助,但我认为这可能是相关的。

【问题讨论】:

您应该记录从http.ListenAndServe 返回的错误,这在启动服务器时会有所帮助。 会的。如果它有助于我查明问题,我将进行修改并报告。感谢您的想法! 作为后续,我花了一个小时试图弄清楚如何通过 http.ListenAndServe 登录到文本文件,但是关于 http 的文档对此并不是特别有用,我Go 非常新。因此,如果我能够弄清楚如何登录到文本文件并且它可以帮助我进行故障排除,我会更新。 Supervisord 和 systemd 是管理服务的首选:elithrar.github.io/article/… @elithrar 太棒了! Supervisord 在创纪录的时间内彻底解决了这个问题。服务器在重新启动后仍然存在,并且一直在运行而没有问题。如果您想“回答”这个问题,我会将您的问题标记为答案;否则,我会感谢您并自己提供简短的解释。 【参考方案1】:

Supervisor 非常适合这里,它可以自动捕获和轮换写入标准输出的日志、在崩溃时重新启动以及管理端口/权限。

以下是 Go Web 服务的示例配置:

# where 'mygoapp' is the name of your application
$ sudo vim /etc/supervisor/conf.d/mygoapp.conf 

[program:yourapp]
command=/home/yourappuser/bin/yourapp # the location of your app
autostart=true
autorestart=true
startretries=10
user=yourappuser # the user your app should run as (i.e. *not* root!)
directory=/srv/www/yourapp.com/ # where your application runs from
environment=APP_SETTINGS="/srv/www/yourapp.com/prod.toml" # environmental variables
redirect_stderr=true
stdout_logfile=/var/log/supervisor/yourapp.log # the name of the log file.
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10

我写了一篇文章[1] 带您完成步骤,但Supervisor documentation 非常全面。

同样,Debian 系统也使用 systemd[2],同样可以实现。

【讨论】:

您的文章既清晰又非常有帮助,Supervisor 彻底解决了我的问题。谢谢!不过,出于我自己的好奇心,您是否知道服务器的守护进程失败的原因?它发生在 Go 服务器和 Python 中实际上相同的服务器上,它似乎与网络接口未启动没有任何关系,因为停止和重新启动服务(当网络肯定启动时)已经没有效果。我作为哪个用户运行守护程序也没有区别。 很难说:您的服务在哪个端口上运行?哪个用户?也有可能(如果 init.d 正在分叉您的程序)您遇到了这个问题:github.com/golang/go/issues/227(不能 fork/daemon() Go 程序)。这个初始化文件在哪个操作系统上? 我尝试了各种端口,但没有一个低于 root 必须调用的范围。我还尝试了不同的用户,包括 root。操作系统是 Jessie (Debian),在 Raspberry Pi 2 上运行。我仍然印象深刻的是,这发生在我用 Python 编写的相同 HTTP 服务器上。如果问题不是很明显,请不要为难自己;我只是想知道我做错了什么。

以上是关于如何在 Debian 中将简单的 Go 服务器作为守护进程运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Go 中将 websocket 与 NewServeMux 一起使用?

如何在 Go 编程中将 []byte 转换为 int

go语言中将函数作为变量传递

如何在 web 服务响应中将 json 响应作为块返回?

如何为Linux安装Go语言

3.5 Go语言中将函数作为值使用