7 Stimmen

Java IOException: Kein Pufferplatz verfügbar beim Senden von UDP-Paketen unter Linux

Ich habe eine Komponente eines Drittanbieters, die in einer bestimmten Situation versucht, zu viele UDP-Nachrichten an zu viele verschiedene Adressen zu senden. Dies ist ein Burst, der beim Starten der Software auftritt, und die Situation ist vorübergehend. Ich bin mir nicht sicher, ob es die schlichte Menge der Nachrichten ist oder die Tatsache, dass jede von ihnen an eine separate IP-Adresse geht.

Wie auch immer, das zugrunde liegende Protokoll oder die problematische Komponente zu ändern, ist keine Option, also suche ich nach einem Workaround. Der StackTrace sieht wie folgt aus:

java.io.IOException: No buffer space available
    at java.net.PlainDatagramSocketImpl.send(Native Method)
    at java.net.DatagramSocket.send(DatagramSocket.java:612)

Dieses Problem tritt (zumindest) mit den Java-Versionen 1.6.0_13 und 1.6.0_10 und den Linux-Versionen Ubuntu 9.04 und RHEL 4.6 auf.

Gibt es irgendwelche Java-Systemeigenschaften oder Linux-Konfigurationsoptimierungen, die helfen könnten?

10voto

Matthew B. Jones Punkte 196

Ich habe endlich herausgefunden, was das Problem ist. Die Java IOException ist irreführend, da sie lautet "Kein Pufferspeicher verfügbar", aber das eigentliche Problem ist, dass die lokale ARP-Tabelle gefüllt ist. Unter Linux ist der Standardwert für die ARP-Tabelle 1024 (Dateien /proc/sys/net/ipv4/neigh/default/gc_thresh1, /proc/sys/net/ipv4/neigh/default/gc_thresh2, /proc/sys/net/ipv4/neigh/default/gc_thresh3).

In meinem Fall (und ich nehme an, auch in Ihrem Fall) sendet Ihr Java-Code UDP-Pakete von einer IP-Adresse aus, die sich im selben Subnetz wie Ihre Zieladressen befindet. Wenn dies der Fall ist, führt der Linux-Rechner eine ARP-Suche durch, um die IP-Adresse in die Hardware-MAC-Adresse zu übersetzen. Da Sie Pakete an viele verschiedene IPs senden, füllt sich die lokale ARP-Tabelle schnell, erreicht die Zahl 1024, und dann wird die Java-Ausnahme ausgelöst.

Die Lösung ist einfach: Entweder Sie erhöhen das Limit, indem Sie die bereits erwähnten Dateien bearbeiten, oder Sie verschieben Ihren Server in ein anderes Subnetz als die Zieladressen, was dann dazu führt, dass der Linux-Rechner keine ARP-Nachbarschaftssuche mehr durchführt (stattdessen wird dies von einem Router im Netzwerk übernommen).

3voto

Aiden Bell Punkte 27753

Wenn Sie viele Nachrichten senden, insbesondere über Gigabit-Ethernet in Linux, sind die Standardparameter für Ihren Kernel normalerweise nicht optimal. Sie können die Puffergröße des Linux-Kernels für die Netzwerkübertragung erhöhen:

echo 1048576 > /proc/sys/net/core/wmem_max
echo 1048576 > /proc/sys/net/core/wmem_default
echo 1048576 > /proc/sys/net/core/rmem_max
echo 1048576 > /proc/sys/net/core/rmem_default

Als Root.

Oder verwenden Sie sysctl

sysctl -w net.core.rmem_max=8388608 

Es gibt eine Vielzahl von Netzwerkoptionen

Voir Linux-Netzwerk-Tuning von IBM y Weitere Informationen zum Tuning

1voto

akarnokd Punkte 66673

Könnte ein wenig kompliziert sein, aber wie ich weiß, verwendet Java die SPI 1 Muster für die Netzwerk-Unterbibliothek. Dies ermöglicht es Ihnen, die für verschiedene Netzwerkoperationen verwendete Implementierung zu ändern. Wenn Sie OpenJDK verwenden, können Sie einige Hinweise erhalten, wie und was Sie mit Ihrer Implementierung einpacken können. Dann verlangsamen Sie in Ihrer Implementierung die E/A zum Beispiel mit einigen Schläfchen.

Oder, nur zum Spaß, Sie könnten die Standard-DatagramSocket mit Ihrer modifizierten Implementierung überschreiben. Geben Sie ihr denselben Paketnamen, und - wie ich weiß - wird sie Vorrang vor der Standard-JRE-Klasse haben. Zumindest hat diese Methode bei mir mit einer fehlerhaften 3rd-Party-Bibliothek funktioniert.

Editar:

1 Service Provider Interface ist eine Methode zur Trennung von Client- und Servicecode innerhalb einer API. Diese Trennung ermöglicht unterschiedliche Client- und Provider-Implementierungen. Erkennbar an der Endung des Namens in Impl Normalerweise, genau wie in Ihrem Stack-Trace java.net.PlainDatagramSocketImpl ist die Provider-Implementierung, wobei DatagramSocket die clientseitige API ist.

Sie haben gesagt, dass Sie die Kommunikation nicht auf dem gesamten Weg verlangsamen wollen. Es gibt mehrere Hacks, um dies zu vermeiden. Messen Sie beispielsweise die Zeit in Ihrem Code und verlangsamen Sie die Kommunikation innerhalb der ersten 1-2 Minuten, beginnend mit dem ersten eingehenden Methodenaufruf. Dann können Sie den Ruhezustand überspringen.

Eine andere Möglichkeit wäre, die fehlerhafte Klasse in der Bibliothek zu identifizieren, sie mit JAD zu bearbeiten und zu korrigieren. Ersetzen Sie dann die ursprüngliche Klassendatei in der Bibliothek.

0voto

Matthew B. Jones Punkte 196

Ich bin auch derzeit sehen dieses Problem als gut mit Debian & RHEL. Zu diesem Zeitpunkt glaube ich, dass ich es auf die NIC und/oder den NIC-Treiber zurückführen kann. Bei welcher Hardwarekonfiguration tritt dieses Problem auch bei Ihnen auf? Das Problem scheint nur auf neuen Dell PowerEdge-Servern aufzutreten, die wir kürzlich erworben haben und die mit Broadcom Corporation NetXtreme II BCM5708 Gigabit Ethernet NICs ausgestattet sind.

Auch ich kann bestätigen, dass es sich um die schnelle Erzeugung von ausgehenden UDP-Paketen an viele verschiedene IP-Adressen in einem kurzen Zeitfenster handelt. Ich habe versucht, eine einfache Java-Anwendung zu schreiben, die das Problem reproduzieren kann (da es bei uns mit snmp4j auftritt).

エディテージ

Sehen Sie sich meine Antwort hier an: Java IOException: Kein Pufferplatz verfügbar beim Senden von UDP-Paketen unter Linux

0voto

Gorbush Punkte 353

Ich habe diesen Fehler, wenn ich versucht, Kohärenz-Cluster in zwei lokalen JVM mit der WIFI-Verbindung zur Datenbank laufen. Wenn ich es über das Ethernet laufen lasse, läuft es gut.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X