J'essaie de déboguer une fuite de descripteur de fichier dans une application Web Java exécutée dans Jetty 7.0.1 sous Linux.
L'application fonctionnait depuis environ un mois lorsque les demandes ont échoué en raison de trop de fichiers ouverts, et Jetty a dû être redémarré.
Java.io.IOException: Cannot run program [external program]: Java.io.IOException: error=24, Too many open files
at Java.lang.ProcessBuilder.start(ProcessBuilder.Java:459)
at Java.lang.Runtime.exec(Runtime.Java:593)
at org.Apache.commons.exec.launcher.Java13CommandLauncher.exec(Java13CommandLauncher.Java:58)
at org.Apache.commons.exec.DefaultExecutor.launch(DefaultExecutor.Java:246)
Au début, je pensais que le problème concernait le code qui lance le programme externe, mais il utilise commons-exec et je ne vois rien de mal à cela:
CommandLine command = new CommandLine("/path/to/command")
.addArgument("...");
ByteArrayOutputStream errorBuffer = new ByteArrayOutputStream();
Executor executor = new DefaultExecutor();
executor.setWatchdog(new ExecuteWatchdog(PROCESS_TIMEOUT));
executor.setStreamHandler(new PumpStreamHandler(null, errorBuffer));
try {
executor.execute(command);
} catch (ExecuteException executeException) {
if (executeException.getExitValue() == EXIT_CODE_TIMEOUT) {
throw new MyCommandException("timeout");
} else {
throw new MyCommandException(errorBuffer.toString("UTF-8"));
}
}
En répertoriant les fichiers ouverts sur le serveur, je peux voir un grand nombre de FIFO:
# lsof -u jetty
...
Java 524 jetty 218w FIFO 0,6 0t0 19404236 pipe
Java 524 jetty 219r FIFO 0,6 0t0 19404008 pipe
Java 524 jetty 220r FIFO 0,6 0t0 19404237 pipe
Java 524 jetty 222r FIFO 0,6 0t0 19404238 pipe
quand Jetty commence, il n'y a que 10 FIFO, après quelques jours, il y en a des centaines.
Je sais que c'est un peu vague à ce stade, mais avez-vous des suggestions sur la prochaine étape à suivre ou sur la manière d'obtenir des informations plus détaillées sur ces descripteurs de fichiers?
Votre programme externe ne se comporte pas correctement. Jetez un œil à pourquoi il ne fait pas cela.
Le problème provient de votre application Java (ou d'une bibliothèque que vous utilisez).
First, vous devriez lire toutes les sorties (Google pour StreamGobbler), et pronto!
Javadoc dit:
Le processus parent utilise ces flux pour alimenter l'entrée et obtenir la sortie de le sous-processus. Parce que certains indigènes les plates-formes ne fournissent qu'un tampon limité taille pour entrée et sortie standard flux, échec d'écrire promptement le flux d'entrée ou lire le flux de sortie du sous-processus peut provoquer le sous-processus à bloquer, et même impasse.
Deuxièmement, waitFor()
votre processus à terminer . Vous devez ensuite fermer les flux d'entrée, de sortie et d'erreur.
Enfindestroy()
votre processus.
Mes sources:
Comme vous utilisez Linux, je suppose que vous manquez de descripteurs de fichiers. Découvrez ulimit. Voici un article décrivant le problème: http://www.cyberciti.biz/faq/linux-increase-the-max-number-of-open-files/
En plus d'examiner les problèmes liés à la cause première, tels que les fuites de fichiers, etc. afin d'augmenter de manière légitime le nombre maximal de "fichiers ouverts" et de le conserver lors des redémarrages, envisagez de le modifier
/etc/security/limits.conf
en ajoutant quelque chose comme ça
jetty soft nofile 2048
jetty hard nofile 4096
où "jetée" est le nom d'utilisateur dans ce cas. Pour plus de détails sur limits.conf, voir http://linux.die.net/man/5/limits.conf
déconnectez-vous puis connectez-vous à nouveau et exécutez
ulimit -n
pour vérifier que le changement a bien eu lieu. Les nouveaux processus de cet utilisateur doivent maintenant se conformer à cette modification. Ce lien semble décrire comment appliquer la limite aux processus en cours d'exécution, mais je ne l'ai pas essayée.
La limite par défaut 1024 peut être trop basse pour les applications Java volumineuses.
Je ne connais pas la nature de votre application, mais j'ai vu cette erreur se manifester à plusieurs reprises à cause d'une fuite de pool de connexions, il vaudrait donc la peine de la vérifier. Sous Linux, les connexions socket consomment des descripteurs de fichiers ainsi que des fichiers du système de fichiers. Juste une pensée.
Vous pouvez gérer les fds vous-même. Le exec en Java renvoie un objet Process. Vérifiez par intermittence si le processus est toujours en cours d'exécution. Une fois terminé, fermez les processus STDERR, STDIN et STDOUT (par exemple, proc.getErrorStream.close ()). Cela atténuera les fuites.
Ce problème survient lorsque vous écrivez des données dans plusieurs fichiers simultanément et que votre système d'exploitation a une limite fixe de fichiers ouverts. Sous Linux, vous pouvez augmenter la limite de fichiers ouverts.
https://www.tecmint.com/increase-set-open-file-limits-in-linux/