英文:
How do I run a simple Go server as a daemon in Debian?
问题
我最近用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 &
,服务器就可以正常工作了。
我已经在过去几天里一直在Google这个问题。要么是我的搜索查询不好,要么这不是一个明显的问题。我该如何将我的Go服务器作为守护进程运行?
编辑:我用Python编写的服务器也出现了完全相同的问题:当使用./python_server.py
执行时,它可以正常工作,但是如果作为服务启动,HTTP请求将被忽略。两个文件都已经设置为可执行,并且无论守护用户是root还是其他用户都没有影响。不确定这是否有帮助,但我认为这可能与问题相关。
英文:
I recently wrote a simple server in 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.")
}
}
It works perfectly well if compiled and then executed by ./go_http_server &.
The problem is that it doesn't survive reboots. So after some reading, I attempted to daemonize it by placing a script in /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_
;;
restart|reload|force-reload)
do_stop
do_start
;;
status)
status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
...then running update-rc.d go_http_server defaults, and poof! It runs on boot, as verified by ps -ef | grep go_http_server.
But it doesn't receive GET requests while running as a service. Thinking it might be running before the network interface was up, I tried service go_http_server stop, followed by service go_http_server start; still refused to receive GET requests. Stopping the service again and then executing ./go_http_server & makes the server function correctly once more.
I've been Googling this on and off for a couple days now. Either my search queries suck, or this isn't an obvious problem. How do I daemonize my Go server?
EDIT: The exact same thing happens with a server I wrote in Python: it works as it should when executed using ./python_server.py, but--if started as service--HTTP requests are ignored. Both files have been made executable, and it doesn't matter if the daemon user is root or any other user. Not sure if this helps, but I thought it might be relevant.
答案1
得分: 2
Supervisor在这里非常适合,它可以自动捕获和轮转写入stdout的日志,崩溃时重新启动并管理端口/权限。
以下是一个Go Web服务的示例配置:
其中'mygoapp'是您的应用程序的名称
$ sudo vim /etc/supervisor/conf.d/mygoapp.conf
[program:yourapp]
command=/home/yourappuser/bin/yourapp # 您的应用程序的位置
autostart=true
autorestart=true
startretries=10
user=yourappuser # 您的应用程序应该以哪个用户身份运行(即不是root!)
directory=/srv/www/yourapp.com/ # 您的应用程序运行的位置
environment=APP_SETTINGS="/srv/www/yourapp.com/prod.toml" # 环境变量
redirect_stderr=true
stdout_logfile=/var/log/supervisor/yourapp.log # 日志文件的名称。
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
我写了一篇文章[1],介绍了具体的步骤,但是Supervisor文档非常详尽。
类似地,Debian系统也使用systemd[2],也可以实现这个功能。
英文:
Supervisor is a good fit here, and can automatically capture and rotate logs written to stdout, restart on crash and manage ports/permissions.
Here's what an example configuration would look like for a Go web service:
# 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
I wrote an article[1] that takes you through the steps, but the Supervisor documentation is extremely comprehensive.
Similarly, Debian systems also use systemd[2], which can achieve this as well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论