2013-05-22 31 views
9

Mam program Go hostujący prostą usługę HTTP pod numerem localhost:8080, dzięki czemu mogę podłączyć do niego mój publiczny host nginx poprzez dyrektywę proxy_pass, jako odwrotne proxy do wyświetlenia część żądań mojej witryny. To wszystko działa świetnie, bez problemów.Jak skutecznie rozłączyć() gniazdo domeny uniksowej w języku programowania Go

Chcę przekonwertować program Go na host usługi HTTP na gniazdo domeny uniksowej zamiast lokalnego gniazda TCP w celu poprawy bezpieczeństwa i zmniejszenia niepotrzebnego obciążenia protokołu TCP.

PROBLEM: Problem polega na tym, że gniazda Unix domeny nie może być ponownie użyty raz są bind() się, nawet po zakończeniu programu. Za drugim razem (i za każdym razem po) uruchamiam program Go, który kończy się z błędem krytycznym "address already in use".

Powszechną praktyką jest unlink() gniazd domen uniksowych (to znaczy usunięcie pliku) po wyłączeniu serwera. Jednak w Go to jest trudne. Moją pierwszą próbą było użycie instrukcji defer w moim głównym funku (zobacz poniżej), ale nie jest ona uruchamiana, jeśli przerywam proces sygnałem takim jak CTRL-C. Przypuszczam, że tego można się spodziewać. Rozczarowujące, ale nie niespodziewane.

PYTANIE: Czy istnieje najlepszych praktyk, jak unlink() gniazdo gdy proces serwera wyłącza (albo wdzięcznie lub niezgrabnie)?

Oto część mojego func main() który uruchamia serwer nasłuchuje na odniesienie:

// 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 Dzięki. To pytanie/odpowiedź ma rok i Go wciąż się poprawia, więc chciałem zobaczyć, czy coś nowego/lepszego pojawiło się. –

+2

Spróbuj 'os.Remove ("/tmp/mysocket ")' przed pierwszym wierszem, czy działa? –

Odpowiedz

7

Oto kompletne rozwiązanie użyłem. Kod, który zamieściłem w moim pytaniu, był uproszczoną wersją dla jasnych celów demonstracyjnych.

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

Mam nadzieję, że jest to dobre i skuteczne Kod, który sprawi, że autorzy Go będą dumni. Z pewnością mi to wygląda. Jeśli nie, to byłoby z mojej strony kłopotliwe. :)

Dla każdego ciekawskiego, jest to część https://github.com/JamesDunne/go-index-html, która jest prostym generatorem listy katalogów HTTP z dodatkowymi funkcjami, które serwery internetowe nie dają po wyjęciu z pudełka.

+1

'l.Close()' spowoduje rozłączenie pliku gniazda, jeśli detektor jest typu 'UnixListener'. 'os.Remove (socketAddr)' jest zbędny. – Gvozden

2

Możesz zakończyć swoją główną funkcję za pomocą funkcji obsługi sygnału i zamiast tego odegrać oddzielne procedury wykonywania innych zadań. W ten sposób można wykorzystać mechanizm odroczenia i obsługiwać wszystkie (sygnał opartych lub nie) przestojów czysto:

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

We współczesnej idź, można użyć 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; 
}