Golang Web服务器突然停止运行的原因及解决方法
Golang Web服务器突然停止运行的原因及解决方法 我正在寻找一些建议,以缩小排查范围,找出我的Go服务器在运行一小段时间后停止的原因。
我的Go代码很简单。不涉及SQL。只有HTML模板。
package main
import (
"fmt"
"html/template"
"net/http"
"strings"
)
func main() {
http.HandleFunc("/", page)
http.ListenAndServe(":5050", nil)
}
var tpl *template.Template
func init() {
tpl = template.Must(template.ParseGlob("public/templates/*.html"))
http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./public/img"))))
http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./public/css"))))
http.Handle("/icn/", http.StripPrefix("/icn/", http.FileServer(http.Dir("./public/icn"))))
http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js"))))
}
func page(w http.ResponseWriter, r *http.Request) {
path := strings.Trim(r.URL.Path, "/")
fmt.Println(path)
switch path {
case "favicon.ico":
http.ServeFile(w, r, "/static/favicon.ico")
case "norec":
tpl.ExecuteTemplate(w, "norec.html", "")
default:
tpl.ExecuteTemplate(w, "home.html", "")
}
}
https://play.golang.org/p/sFs4CqZ3Olt
有什么线索可以找到原因吗?HTML、CSS和Javascript验证都没有错误。
更多关于Golang Web服务器突然停止运行的原因及解决方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
正如我所说,无论原帖作者使用什么进行日志记录
更多关于Golang Web服务器突然停止运行的原因及解决方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
func main() {
fmt.Println("hello world")
}
它是直接退出,还是出现了恐慌或其他情况?
正如我所说,http.ListenAndServe 永远不会 返回 nil。
NobbZ:
log.Fatal总是会记录到进程的stderr/fd:2
那么我该如何读取这个日志呢?var/log/stderr 是空的。
再次声明,我对 webmin 的功能一无所知。你需要自行查明它是否将进程的 stdout 和 stderr 持久化存储在某处。
skillian:
它只是退出了还是出现了恐慌或其他情况?
从服务器终端:
已终止 命令失败,退出状态为 36608
NobbZ:
你真的应该修改这个。
err := ListenAndServe(":5050", nil) fmt.Println("%v", err)
我得到:
undefined: ListenAndServe
解决方案?
Sibert:
undefined: ListenAndServe
解决方案?
阅读错误信息和代码… 我忘记限定调用了,它当然是
http.ListenAndServe…
我在多个地方读到,将 ListenAndServe 放在 log.Fatal 中是一个好主意,例如:
log.Fatal(http.ListenAndServe( “:8080” , nil))
正如这里所述。
NobbZ:
err := ListenAndServe(":5050", nil) fmt.Println("%v", err)
终端没有收到来自此代码的任何消息。服务器只是静默地关闭了。
同一服务器上运行的其他 Go Web 服务器都没有任何问题。是否有任何 JavaScript 可能导致这种情况?
还有其他建议吗?
NobbZ:
我忘了限定调用,当然是
http.ListenAndServe…
谢谢,但是不应该也有一个 if 语句吗?还是不需要?像这样:
err := http.ListenAndServe(":5050", nil)
if err != nil {
fmt.Println("%v", err)
}
我不确定您是如何管理您的服务的。但我真的不相信它会静默退出。如果确实如此,您需要检查它的退出代码,也许它被系统通过信号杀死了?
您是否检查过系统日志,看看是否是 OOM 杀手导致的?
尝试一下这个高票答案:“Finding which process was killed by Linux OOM killer - Stack Overflow” https://stackoverflow.com/questions/624857/finding-which-process-was-killed-by-linux-oom-killer
Sibert:
Command failed with exit status 36608
这是一个垃圾值。在 Linux 上,退出码只有 8 位…
不过,如果我查看它的各个字节,我们得到的是 143 0。
如果我们假设可以忽略 0 字节,那么退出码就是 143,这在 Linux 上通常意味着“被 SIGTERM 信号关闭”。
当你在终端使用 kill $pid 命令时,默认就会发生这种情况。现在你需要找出是什么发送了 SIGTERM 信号以及原因。
Sibert:
http.ListenAndServe(":5050", nil)
你真的应该修改这个。
err := ListenAndServe(":5050", nil)
fmt.Println("%v", err)
(或者使用你习惯的其他日志记录方式)
这样你的日志里就会留下一些信息。
http.ListenAndServe 的返回值保证永远不会是 nil,所以如果它返回了,就意味着发生了一个被你忽略的错误。
如果你确实没有在日志中看到这样的错误,那么问题就更深层次了,可能是进程被外部杀死了。
Sibert: 如何让这个日志打印到 var/log/stderr ?
我不太确定你具体指的是什么。但如果它是一个文件,那么打开它并将你的日志写入其中。具体如何操作取决于你使用的日志记录库。
log.Fatal 总是会将日志记录到进程的 stderr/fd:2,然后退出程序。
Systemd 和 Docker 通常会获取程序的 stdout 和 stderr,并将它们写入各自的日志中,之后你可以按服务/容器来浏览这些日志。
我不清楚你的 Webmin 是如何处理这类事情的。
NobbZ:
我不确定您是如何管理您的服务的。
通过Webmin管理的Debian 10。var/log/message 中除了正常消息外什么都没有:rsyslogd was HUPed,并且 var/log/stderr 是空的。
除了正在运行的 http.Server 之外没有其他进程,并且它由于某种原因反复终止。无论是 fmt.Println(err) 还是 log.Fatal 都没有报告任何问题。内存使用率没有超过 30%。
HTML、CSS 和 Javascript 都已通过验证。同一服务器上的其他 Go 服务器在其他端口上运行完美。
我已经激活了防火墙,但到目前为止没有区别。
有什么线索可以找到原因吗?
NobbZ: 另外,当你的服务退出时,它的退出代码是什么?
已终止 命令失败,退出状态码为 36608
NobbZ: 同时确保你的所有端点都正确记录日志。

Go Playground - The Go Programming Language
NobbZ: 详尽的日志记录可能是帮助你调试此问题的唯一方法。
我如何让这个日志打印到 var/log/stderr ?
log.Fatal(http.ListenAndServe(":8080", nil))
NobbZ: 你需要自己弄清楚,它是否将进程的
stdout和stderr持久化到了某个地方。
这是一个 Debian 系统的问题。
-
首先我创建了一个 main.exe Go Playground - The Go Programming Language
-
然后在终端中将其重定向到 /var/log/messages
./main 2>> /var/log/messages -
最后我执行了 main
cd /logtest ./main
Aug 18 00:00:05 …rsyslogd was HUPed Message
问题是如何在 Go 代码内部实现 #2(重定向到 /var/log/messages)?
或者更好的做法是重定向到 stderr?
服务器突然停止通常由以下几个原因导致:
1. panic未恢复
最常见的原因是未处理的panic。添加recovery中间件:
func recoveryMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
fmt.Printf("Panic recovered: %v\n", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next(w, r)
}
}
func main() {
http.HandleFunc("/", recoveryMiddleware(page))
http.ListenAndServe(":5050", nil)
}
2. 模板执行错误
模板执行失败可能导致panic:
func page(w http.ResponseWriter, r *http.Request) {
path := strings.Trim(r.URL.Path, "/")
fmt.Println(path)
switch path {
case "favicon.ico":
http.ServeFile(w, r, "/static/favicon.ico")
case "norec":
if err := tpl.ExecuteTemplate(w, "norec.html", ""); err != nil {
fmt.Printf("Template error: %v\n", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
default:
if err := tpl.ExecuteTemplate(w, "home.html", ""); err != nil {
fmt.Printf("Template error: %v\n", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
}
3. 文件路径问题
静态文件路径可能导致panic:
func init() {
var err error
tpl, err = template.ParseGlob("public/templates/*.html")
if err != nil {
panic(fmt.Sprintf("Template parsing error: %v", err))
}
// 检查目录是否存在
dirs := []string{"./public/img", "./public/css", "./public/icn", "./public/js"}
for _, dir := range dirs {
if _, err := os.Stat(dir); os.IsNotExist(err) {
fmt.Printf("Warning: directory %s does not exist\n", dir)
}
}
http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./public/img"))))
http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./public/css"))))
http.Handle("/icn/", http.StripPrefix("/icn/", http.FileServer(http.Dir("./public/icn"))))
http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./public/js"))))
}
4. 添加详细日志
启用详细日志记录:
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
fmt.Printf("[%s] %s %s\n", r.Method, r.URL.Path, r.RemoteAddr)
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next(rw, r)
fmt.Printf("[%d] %s %s completed in %v\n",
rw.statusCode, r.Method, r.URL.Path, time.Since(start))
}
}
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
func main() {
http.HandleFunc("/", loggingMiddleware(recoveryMiddleware(page)))
fmt.Println("Server starting on :5050")
if err := http.ListenAndServe(":5050", nil); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
5. 检查系统资源
添加资源监控:
func monitorResources() {
go func() {
for {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc = %v MiB, TotalAlloc = %v MiB, Sys = %v MiB, NumGC = %v\n",
m.Alloc/1024/1024, m.TotalAlloc/1024/1024, m.Sys/1024/1024, m.NumGC)
time.Sleep(30 * time.Second)
}
}()
}
func main() {
monitorResources()
http.HandleFunc("/", loggingMiddleware(recoveryMiddleware(page)))
fmt.Println("Server starting on :5050")
if err := http.ListenAndServe(":5050", nil); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}
6. favicon.ico路径问题
修正favicon处理:
func page(w http.ResponseWriter, r *http.Request) {
path := strings.Trim(r.URL.Path, "/")
fmt.Println("Request path:", path)
switch path {
case "favicon.ico":
// 使用相对路径或绝对路径
faviconPath := "./public/static/favicon.ico"
if _, err := os.Stat(faviconPath); os.IsNotExist(err) {
http.NotFound(w, r)
return
}
http.ServeFile(w, r, faviconPath)
case "norec":
if err := tpl.ExecuteTemplate(w, "norec.html", ""); err != nil {
fmt.Printf("Template error: %v\n", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
default:
if err := tpl.ExecuteTemplate(w, "home.html", ""); err != nil {
fmt.Printf("Template error: %v\n", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
}
这些修改会帮助捕获错误并记录服务器状态,从而确定停止的具体原因。


