【问题标题】:How to reliably unlink() a Unix domain socket in Go programming language如何在 Go 编程语言中可靠地 unlink() Unix 域套接字
【发布时间】:2013-05-22 01:07:58
【问题描述】:

我有一个 Go 程序在 localhost:8080 上托管一个简单的 HTTP 服务,因此我可以通过 proxy_pass 指令将我的公共 nginx 主机连接到它,作为反向代理来处理我网站的部分请求。这一切都很好,没有问题。

我想将 Go 程序转换为在 Unix 域套接字而不是本地 TCP 套接字上托管 HTTP 服务,以提高安全性并减少 TCP 不必要的协议开销。

问题: 问题是 Unix 域套接字一旦被 bind() 到,就不能被重用,即使在程序终止之后也是如此。第二次(以及之后的每次)我运行 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)))
}

【问题讨论】:

  • @Tom 谢谢。这个问题/答案已经有一年了,Go 仍在改进,所以我想看看是否有任何新的/更好的出现。
  • 在第一行之前尝试os.Remove("/tmp/mysocket"),它有效吗?
  • 我用一个简单的信号处理程序让它工作得很好。现在没问题,因为用例非常简单,但我担心会有很多无法访问的开放资源进行更复杂的清理操作。

标签: go signals unix-socket


【解决方案1】:

这是我使用的完整解决方案。我在问题中发布的代码是用于清晰演示的简化版本。

// 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 服务器不提供的额外功能。

【讨论】:

【解决方案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
}

【讨论】:

    【解决方案3】:

    在现代 Go 中,您可以使用 syscall.Unlink() - docs 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-06
      • 2014-06-02
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多