2013-05-22 22 views
9

我有一個Go程序託管在localhost:8080上的簡單HTTP服務,因此我可以通過proxy_pass指令將我的公開nginx主機連接到它,作爲反向代理服務我網站的部分請求。這一切都很好,沒有問題。如何在Go編程語言中可靠地取消鏈接()一個Unix域套接字

我想將Go程序轉換爲在Unix域套接字上承載HTTP服務,而不是本地TCP套接字,以提高安全性並減少TCP不必要的協議開銷。

問題: 問題是Unix域套接字不能重複使用,一旦他們bind()來,即使程序終止後。第二次(以及之後)我運行Go程序時,它會以致命錯誤"address already in use"退出。

通常的做法是到unlink()當服務器關閉時,Unix域套接字(即刪除文件)。但是,這在Go中很棘手。我的第一個嘗試是在我的主func中使用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

HTTP ://stackoverflow.com/questions/11268943/golang-is-it-possible-to-capture-a-ctrlc-sigterm-signal-and-run-a-cleanup-fu – thwd

+0

@Tom謝謝。這個問題/答案已經過去了一年,Go仍然在不斷改進,所以我想看看是否有新的東西出現。 –

+2

在第一行之前嘗試'os.Remove(「/ tmp/mysocket」)',它工作嗎? –

回答

7

這裏是我使用的完整的解決方案。我在我的問題中發佈的代碼是用於明確演示目的的簡化版本。

// 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

如果偵聽器的類型爲'UnixListener',則l.Close()'將取消套接字文件的鏈接。 'os.Remove(socketAddr)'是多餘的。 – Gvozden

2

您可以用信號處理程序結束您的主要功能,併爲您的其他任務生成單獨的去程序。這樣的話,你可以利用延遲機制和清潔處理所有(信號爲基礎與否)停機:

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 
} 
1

在現代圍棋,你可以使用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; 
} 
相關問題