我有一个Go程序在localhost:8080
上托管一个简单的HTTP服务,因此我可以通过nginx
指令将我的公共proxy_pass
主机连接到它,作为反向代理服务于我的网站的要求。这一切都很好,没有问题。
我想将Go程序转换为在Unix域套接字而不是本地TCP套接字上托管HTTP服务,以提高安全性并减少TCP的不必要的协议开销。
问题:
问题是,即使在程序终止之后,一旦它们bind()
,就无法重用Unix域套接字。第二次(以及之后的每一次)我运行Go程序它会以致命错误"address already in use"
退出。
通常的做法是在服务器关闭时unlink()
Unix域套接字(即删除文件)。但是,这在Go中很棘手。我的第一次尝试是在我的主函数中使用defer
语句(见下文),但如果我用CTRL-C这样的信号中断进程,它就不会运行。我想这是可以预料的。令人失望,但并非意外。
问题:在服务器进程关闭时(优雅或不正常),是否有关于如何unlink()
套接字的最佳做法?
这是启动服务器监听参考的func main()
的一部分:
// Create the HTTP server listening on the requested socket:
l, err := net.Listen("unix", "/tmp/mysocket")
if err != nil {
log.Fatal(err)
} else {
// Unix sockets must be unlink()ed before being reused again.
// Unfortunately, this defer is not run when a signal is received, e.g. CTRL-C.
defer func() {
os.Remove("/tmp/mysocket")
}()
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
}
答案 0 :(得分:8)
这是我使用的完整解决方案。我在我的问题中发布的代码是一个简化版本,用于明确的演示目的。
// Create the socket to listen on:
l, err := net.Listen(socketType, socketAddr)
if err != nil {
log.Fatal(err)
return
}
// Unix sockets must be unlink()ed before being reused again.
// Handle common process-killing signals so we can gracefully shut down:
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
go func(c chan os.Signal) {
// Wait for a SIGINT or SIGKILL:
sig := <-c
log.Printf("Caught signal %s: shutting down.", sig)
// Stop listening (and unlink the socket if unix type):
l.Close()
// And we're done:
os.Exit(0)
}(sigc)
// Start the HTTP server:
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
我确信希望这是一个优秀而有效的Go代码,可以让Go作者感到自豪。对我来说当然看起来如此。如果不是,那将是我的尴尬。 :)
对于任何好奇的人来说,这是https://github.com/JamesDunne/go-index-html的一部分,它是一个简单的HTTP目录列表生成器,具有Web服务器无法开箱即用的一些额外功能。
答案 1 :(得分:2)
您可以使用信号处理程序结束主要功能,并为您的其他任务生成单独的go例程。这样,您可以利用延迟机制并干净地处理所有(基于信号或不基于)关闭:
func main() {
// Create the HTTP server listening on the requested socket:
l, err := net.Listen("unix", "/tmp/mysocket")
if err != nil {
log.Fatal(err)
return
}
// Just work with defer here; this works as long as the signal handling
// happens in the main Go routine.
defer l.Close()
// Make sure the server does not block the main
go func() {
log.Fatal(http.Serve(l, http.HandlerFunc(indexHtml)))
}()
// Use a buffered channel so we don't miss any signals
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
// ...and exit, running all the defer statements
}
答案 2 :(得分:1)
在现代Go中,您可以使用syscall.Unlink()
- 文档here:
import (
"net"
"syscall"
...
)
...
socketpath := "/tmp/somesocket"
// carry on with your socket creation:
addr, err := net.ResolveUnixAddr("unixgram", socketpath)
if err != nil {
return err;
}
// always remove the named socket from the fs if its there
err = syscall.Unlink(socketpath)
if err != nil {
// not really important if it fails
log.Error("Unlink()",err)
}
// carry on with socket bind()
conn, err := net.ListenUnixgram("unixgram", addr);
if err != nil {
return err;
}