2013-05-22 5 views
9

Ich habe ein Go-Programm, das einen einfachen HTTP-Service unter localhost:8080 hostet, so dass ich meinen öffentlichen nginx-Host über die proxy_pass-Direktive als Reverse-Proxy verbinden kann Teil der Anfragen meiner Website. Das funktioniert alles super, keine Probleme dort.Wie man einen Unix-Domain-Socket in der Programmiersprache Go zuverlässig trennt

Ich möchte das Go-Programm konvertieren, um den HTTP-Dienst auf einem Unix-Domain-Socket anstelle eines lokalen TCP-Sockets für verbesserte Sicherheit zu hosten und den unnötigen Protokoll-Overhead von TCP zu reduzieren.

PROBLEM: Das Problem ist, dass Steckdosen Unix-Domain nicht wiederverwendet werden können, wenn sie bind() zu, auch nach Beendigung des Programms sind. Das zweite Mal (und jedes Mal danach) führe ich das Go-Programm mit einem schwerwiegenden Fehler "address already in use" aus.

Gemeinsame Praxis ist unlink() Unix-Domain-Sockets (d. H. Entfernen Sie die Datei), wenn der Server heruntergefahren wird. Dies ist jedoch schwierig in Go. Mein erster Versuch war, die defer-Anweisung in meinem Hauptfunc (siehe unten) zu verwenden, aber es wird nicht ausgeführt, wenn ich den Prozess mit einem Signal wie CTRL-C abbringe. Ich nehme an, das ist zu erwarten. Enttäuschend, aber nicht unerwartet.

FRAGE: Gibt es eine bewährte Methode, wie die Steckdose unlink(), wenn der Server-Prozess (entweder elegant oder ungracefully) heruntergefahren?

Hier ist ein Teil meines func main(), die den Server als Referenz zu hören beginnt:

// 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 Danke. Diese Frage/Antwort ist ein Jahr alt und Go verbessert sich immer noch, also wollte ich sehen, ob etwas Neues/Besseres aufkam. –

+2

Versuchen Sie 'os.Remove ("/tmp/mysocket ")' vor der ersten Zeile, funktioniert es? –

Antwort

7

die komplette Lösung, die ich verwendet. Der Code, den ich in meine Frage geschrieben habe, war eine vereinfachte Version für klare Demonstrationszwecke.

Ich hoffe, dass dies ein guter und effektiver Go-Code ist, der die Go-Autoren stolz machen würde. Das sieht mir sicher so aus. Wenn nicht, wäre das peinlich für mich. :)

Für alle Neugierigen ist dies ein Teil von https://github.com/JamesDunne/go-index-html, die eine einfache HTTP-Verzeichnis Auflistung Generator mit einigen zusätzlichen Funktionen, die Web-Server nicht aus der Box geben.

+1

'l.Close()' wird die Verknüpfung der Socket-Datei aufheben, wenn der Listener vom Typ 'UnixListener' ist. 'os.Remove (socketAddr)' ist redundant. – Gvozden

2

Sie können Ihre Hauptfunktion mit dem Signal-Handler beenden und stattdessen separate Go-Routinen für Ihre anderen Aufgaben erstellen. Auf diese Weise können Sie den Zurückstellungsmechanismus nutzen und behandeln alle (signalbasierte oder nicht) Abschaltungen sauber:

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

In modernen Go, können Sie die syscall.Unlink() verwenden - 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; 
}