it-swarm.com.de

Los, tcp zu viele geöffnete Dateien debuggen

Hier ist ein einfaches Go http (tcp) -Verbindungs-Testskript

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    for i := 0; i < 2000; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            resp, err := http.Get(ts.URL)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%s", i, greeting)
        }(i)
    }
    wg.Wait()
}

Und wenn ich das in Ubuntu laufen lasse, bekomme ich:

panic: Get http://127.0.0.1:33202: dial tcp 127.0.0.1:33202: too many open files

Andere Beiträge sagen, dass Close die Verbindung sicherstellt, was ich hier alles mache . Andere sagen, dass sie die Grenze der maximalen Verbindung mit ulimit erhöhen oder Sudo sysctl -w fs.inotify.max_user_watches=100000 versuchen sollten, aber trotzdem nicht funktionieren.

Wie führe ich Millionen von TCP-Verbindungsroutinen auf einem einzelnen Server aus? Es stürzt nur bei 2.000 Verbindungen ab.

Vielen Dank,

12
user4211028

Ich denke, Sie müssen Ihre maximalen Dateideskriptoren ändern. Ich habe das gleiche Problem auf einer meiner Entwicklungs-VMs schon einmal gemacht und musste die Dateideskriptoren maximal ändern, nicht irgendetwas mit inotify-Einstellungen. 

FWIW, Ihr Programm läuft gut auf meiner VM.

·> ulimit -n
120000

Aber nachdem ich renne

·> ulimit -n 500
·> ulimit -n
500

Ich bekomme:

panic: Get http://127.0.0.1:51227: dial tcp 127.0.0.1:51227: socket: too many open files
22
sberry

Wenn Sie Millionen von Go-Routinen ausführen möchten, die einen Socket öffnen/lesen/schließen, sollten Sie Ihr ulimit besser aufbauen oder den Socket öffnen/lesen/schließen und den gelesenen Wert an die Go-Routine übergeben, aber ich würde eine gepufferter Kanal, um zu steuern, wie viele Dateideskriptoren Sie öffnen möchten.

const (
    // this is where you can specify how many maxFileDescriptors
    // you want to allow open
    maxFileDescriptors = 100
)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    maxChan := make(chan bool, maxFileDescriptors)
    for i := 0; i < 1000; i++ {
        maxChan <- true
        go func(url string, i int, maxChan chan bool, wg *sync.WaitGroup) {
            wg.Add(1)
            defer wg.Done()
            defer func(maxChan chan bool) { <-maxChan }(maxChan)
            resp, err := http.Get(url)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                panic(err)
            }
            err = resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%d: %s", i, string(greeting))
        }(ts.URL, i, maxChan, &wg)
    }
    wg.Wait()
}
3
Popmedic

auch wenn Sie in Ihrer Funktion goruntine sind, versuchen Sie diese https://github.com/leenanxi/nasync

 //it has a simple usage
 nasync.Do(yourAsyncTask)

in Ihrem Code

for i := 0; i < 2000; i++ {
    nasync.Do(func() {
        resp, err := http.Get("https://www.baidu.com")
        ...
    })
}

der voreingestellte max go-Goruntine in nasync lib ist 1000

1
lee

Ändern Sie das ulimit, um den Fehler "zu viele geöffnete Dateien" zu vermeiden Standardmäßig ist max ulimit 4096 für Linux und 1024 für Mac. Durch die Eingabe von ulimit -n 4096. Für mehr als 4096 müssen Sie noch die limits.conf im Ordner etc/security für Linux ändern und den Hard Limit auf 100000 setzen, indem Sie diese Zeile "* hard core 100000" hinzufügen.

0
sp111
HTTP/1.1 uses persistent connections by default:
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html
The solution was to inform the server that the client wants to close the connection after the transaction is complete. This can be done by setting the Connection header,
req.Header.Set("Connection", "close") or by setting the Close property to true on the http.Request:
req.Close = true After doing that, the “too many open files” issue went away as the program was no longer keeping HTTP connections open and thus not using up file descriptors.

Ich habe das Problem gelöst, indem ich req.Close = true und req.Header.Set ("Connection", "close") hinzugefügt habe. Ich denke, es ist besser als ulimit zu ändern.

quelle: http://craigwickesser.com/2015/01/golang-http-to-many-open-files/

0
余云鹏

Ich musste auch den Header für die Close Connection manuell einstellen, um das Problem der Dateibeschreibung zu vermeiden:

r, _ := http.NewRequest(http.MethodDelete, url, nil)
r.Close = true
res, err := c.Do(r)
res.Body.Close();

Ohne r.Close = true und res.Body.Close () habe ich die Dateideskriptor-Grenze erreicht. Mit beiden konnte ich so viele abfeuern, wie ich brauchte.

0
Matt Borowiec

Das http-Paket von Go gibt standardmäßig keine Anforderungszeitlimits an. Sie sollten immer einen Timeout in Ihren Service aufnehmen. Was ist, wenn ein Kunde seine Sitzung nicht beendet? Ihr Prozess wird alte Sitzungen am Leben erhalten, die auf ulimits treffen. Ein schlechter Schauspieler kann absichtlich Tausende von Sitzungen öffnen und Ihren Server DOSieren. Schwerlastdienste sollten auch die Werte für ulimits anpassen, aber Timeouts für das Anhalten.

Stellen Sie sicher, dass Sie ein Timeout angeben:

http.DefaultClient.Timeout = time.Minute * 10

Sie können vor und nach der Überprüfung die von Ihrem Prozess geöffneten Dateien überprüfen: 

lsof -p [PID_ID]
0
PodTech.io