2013-05-22 14 views
9

localhost:8080に簡単なHTTPサービスをホストしているGoプログラムがあるので、proxy_passディレクティブを使用して公開するnginxホストをリバースプロキシとして接続します私のサイトの要求の一部。これはすごくうまくいっていて問題はありません。Goプログラミング言語でUnixドメインソケットを確実にunlink()する方法

セキュリティを強化し、TCPの不要なプロトコルオーバーヘッドを減らすために、GoプログラムをローカルTCPソケットの代わりにUnixドメインソケットでホストするように変換したいとします。

問題: 問題は、彼らがにbind()あるいったんUNIXドメインソケットでもプログラム終了後、再利用することができないということです。 2回目(そして毎回)、Goプログラムを実行して致命的なエラー"address already in use"で終了します。

通常、サーバがシャットダウンすると、unlink() Unixドメインソケット(ファイルを削除)が行われます。しかし、これはGoでは難しいです。私の最初の試みは、メインのfunc(下記参照)にdefer文を使用することでしたが、CTRL-Cのようなシグナルでプロセスを中断すると実行されません。これが期待されると思います。失望していますが、予期せぬものではありません。

QUESTION:サーバプロセスが(どちらか正常または不正に)シャットダウン時にソケットを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

'l.Close()'は、リスナーが 'UnixListener'型の場合、ソケットファイルのリンクを解除します。 'os.Remove(socketAddr)'は冗長です。 – Gvozden

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 
} 
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; 
} 
関連する問題