web-dev-qa-db-fra.com

Allez, TCP trop de fichiers ouverts déboguer

Voici un script simple de test de connexion Go http (tcp)

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

Et si je lance ceci dans Ubuntu, je reçois:

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

D'autres articles disent de s'assurer que Close la connexion, ce que je fais tout ici . Et d'autres disent d'augmenter la limite de connexion maximale avec ulimit ou essayez Sudo sysctl -w fs.inotify.max_user_watches=100000 mais ne fonctionne toujours pas.

Comment puis-je exécuter des millions de goroutines de connexions TCP sur un seul serveur? Il ne se bloque qu'avec 2 000 connexions.

Merci,

12
user4211028

Je pense que vous devez changer vos descripteurs de fichier max. J'ai déjà rencontré le même problème sur l'une de mes machines virtuelles de développement et je devais modifier les descripteurs de fichier au maximum, sans rien avec les paramètres inotify. 

FWIW, votre programme fonctionne correctement sur ma machine virtuelle.

·> ulimit -n
120000

Mais après je cours

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

Je reçois:

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

Si vous voulez exécuter des millions de routines go qui ouvrent/lisent/ferment un socket, vous ferez bien de mieux votre ulimit, ou ouvrez/lirez/fermez le socket et passez la valeur lue à la routine go, canal mis en mémoire tampon pour contrôler le nombre de descripteurs de fichiers que vous voulez pouvoir ouvrir.

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

aussi peut goruntine dans votre fonction, essayez ceci https://github.com/leenanxi/nasync

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

dans votre code

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

le maximum par défaut go goruntine dans nasync lib est 1000

1
lee

changez ulimit pour éviter l'erreur "trop ​​de fichiers ouverts" par défaut max ulimit est 4096 pour linux et 1024 pour mac, u peut changer ulimit à 4096 en tapant ulimit -n 4096 pour au-delà de 4096, vous devez modifier limits.conf dans le dossier etc/security de Linux et définir la limite stricte à 100000 en ajoutant cette ligne "* noyau dur 100000".

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.

J'ai résolu ce problème en ajoutant req.Close = true et req.Header.Set ("Connection", "close"). Je pense que c'est mieux que de changer ulimit.

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

0
余云鹏

Je devais également définir manuellement l'en-tête de connexion étroite pour éviter le problème de descripteur de fichier:

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

Sans r.Close = true et res.Body.Close (), j'atteins la limite du descripteur de fichier. Avec les deux, je pourrais en tirer autant que nécessaire.

0
Matt Borowiec

Le package http de Go ne spécifie pas les délais de demande par défaut. Vous devez toujours inclure un délai d'attente dans votre service. Que se passe-t-il si un client ne ferme pas sa session? Votre processus maintiendra les anciennes sessions en vie contre les ulimits. Un mauvais acteur pourrait intentionnellement ouvrir des milliers de sessions en effectuant un DOS sur votre serveur. Les services de charges lourdes devraient également ajuster les limites mais également les délais d’exécution.

Assurez-vous de spécifier un délai d'attente:

http.DefaultClient.Timeout = time.Minute * 10

Vous pouvez valider avant et après en surveillant les fichiers ouverts par votre processus: 

lsof -p [PID_ID]
0
PodTech.io