Frage Wie man einen Unix-Domain-Socket in der Go-Programmiersprache zuverlässig auflöst ()


Ich habe ein Go-Programm, das einen einfachen HTTP-Dienst bereitstellt localhost:8080 damit ich meine Öffentlichkeit verbinden kann nginx Gastgeber über die proxy_pass Direktive, als Reverse Proxy, um einen Teil der Anfragen meiner Site zu bedienen. Das funktioniert alles super, keine Probleme dort.

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

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

Gemeinsame Praxis ist es 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 es, die defer Anweisung in meiner Hauptfunktion (siehe unten), 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 Best Practice, wie? unlink() der Socket, wenn der Serverprozess (entweder würdevoll oder nicht ordnungsgemäß) heruntergefahren wird?

Hier ist ein Teil von mir func main() Das startet den Server als Referenz:

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

9
2018-05-22 01:07


Ursprung


Antworten:


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

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

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/JamesDonne/go-index-html Dies ist ein einfacher HTTP-Verzeichnislisting-Generator mit einigen zusätzlichen Funktionen, die Sie von Web-Servern nicht erhalten.


7
2018-05-22 21:50



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 Defer-Mechanismus nutzen und alle (signalbasierten oder nicht-heruntergefahrenen) Abschaltvorgänge sauber behandeln:

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
2017-08-11 12:10



In modernen Go, können Sie die verwenden syscall.Unlink() - Dokumente Hier:

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

1
2017-11-21 20:33