2009-06-25 10 views
5

Ho un componente di terze parti che tenta di inviare troppi messaggi UDP a troppi indirizzi separati in una determinata situazione. Questo è un burst che accade quando il software viene avviato e la situazione è temporanea. In realtà non sono sicuro che sia la semplice quantità dei messaggi o il fatto che ognuno di essi vada a un indirizzo IP separato.IOExcezione Java: nessuno spazio buffer disponibile durante l'invio di pacchetti UDP su Linux

In ogni caso, la modifica del protocollo sottostante o del componente problematico non è un'opzione, quindi sto cercando una soluzione alternativa. Lo StackTrace assomiglia a questo:

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

Questo problema si verifica (almeno) con le versioni di Java 1.6.0_13 e 1.6.0_10 e le versioni di Linux Ubuntu 9.04 e RHEL 4.6.

Esistono alcune proprietà del sistema Java o modifiche alla configurazione di Linux che potrebbero essere utili?

risposta

3

Quando si inviano molti messaggi, soprattutto su gigabit ethernet in Linux, i parametri di magazzino per il proprio kernel non sono generalmente ottimali. È possibile aumentare la dimensione del buffer del kernel Linux per il networking tramite:

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 

Come utente root.

O uso sysctl

sysctl -w net.core.rmem_max=8388608 

ci sono tonnellate di opzioni di rete

Vedi Linux Network Tuning by IBM e More tuning information

+0

Grazie. Oltre a questi parametri, ho provato a modificare anche net.ipv4.udp_mem e net.ipv4.udp_wmem_min. Per prima cosa ho raddoppiato, i valori, poi li ho raddoppiati e alla fine li ho modificati per essere 10 volte più grandi dei valori predefiniti. Niente ha aiutato finora però. – auramo

+0

@auramo, quale JVM stai usando? La build del sole o la roba OpenJDK/JVM dalla tua distribuzione? Ti consiglio di usarne uno per la tua distro, l'open se possibile in quanto sarà meno "sicuro" e l'interfacciamento più accurato con il kernel/libc. –

+0

Sto usando le build Sun di 1.6.0_13 e 1.6.0_10. Potrei facilmente provare con le versioni OpenJDK, ma cambiando dall'implementazione Sun, OpenJDK per il prodotto finale sarebbe un grosso problema a questo punto del progetto. – auramo

1

potrebbe essere un po 'complicato, ma che ne so, Java utilizza l'SPI modello per la sottocloteca di rete. Ciò consente di modificare l'implementazione utilizzata per varie operazioni di rete. Se si utilizza OpenJDK, è possibile ottenere alcuni suggerimenti su come e cosa avvolgere con la propria implementazione. Quindi, nella tua implementazione, rallenti l'I/O con alcuni posti letto, ad esempio.

Oppure, solo per divertimento, è possibile sovrascrivere il DatagramSocket predefinito con l'implementazione modificata. Avere lo stesso nome del pacchetto e, come so, avrà la precedenza sulla classe JRE predefinita. Almeno questo metodo ha funzionato per me su qualche buggy libreria di terze parti.

Edit:

Service Provider Interface è un metodo per separare client e il servizio codice all'interno di un'API. Questa separazione consente diverse implementazioni di client e provider diversi. Può essere riconosciuto dal nome che termina in Impl in genere, proprio come nella traccia dello stack java.net.PlainDatagramSocketImpl è l'implementazione del provider in cui DatagramSocket è l'API lato client.

Hai commentato che non vuoi rallentare la comunicazione per tutto il percorso. Esistono diversi attacchi per evitarlo, ad esempio misurare l'ora nel codice e rallentare la comunicazione entro i primi 1-2 minuti a partire dalla prima chiamata al metodo in ingresso. Quindi puoi saltare il sonno.

Un'altra opzione sarebbe quella di identificare la classe di comportamento anomalo nella libreria, JAD e risolverlo. Quindi sostituire il file di classe originale nella libreria.

+0

Puoi dirmi che cos'è un pattern SPI? Voglio superare un intervallo di avvio di 1-2 minuti. Per questo non voglio assolutamente rallentare il mio UDP I/O che deve essere veloce per tutto il tempo in cui l'applicazione è in esecuzione (è un'applicazione server). – auramo

+0

Ok, quel vecchio schema :-) – auramo

0

Attualmente sto riscontrando questo problema anche con entrambi Debian & RHEL. A questo punto credo di averlo isolato fino alla NIC e/o al driver della NIC. Quale configurazione hardware hai questo presenta anche questo problema? Ciò sembra verificarsi solo sui nuovi server Dell PowerEdge che abbiamo recentemente acquisito con schede NIC Gigabit Ethernet NetXtreme II BCM5708 di Broadcom Corporation.

Anche io posso confermare che è la generazione rapida di pacchetti UDP in uscita a molti diversi indirizzi IP in una finestra corta. Ho tentato di scrivere una semplice applicazione Java in grado di riprodurla (poiché la nostra si sta verificando con snmp4j).

EDIT

Guardate la mia risposta qui: Java IOException: No buffer space available while sending UDP packets on Linux

+0

Il mio problema si è verificato in molte configurazioni hw, su una workstation HP e su un server rack. Alla fine abbiamo finito per hackerare il componente sottostante (componente Java di un altro team all'interno della nostra azienda) che ha fatto l'eccessivo messaging di rete a innescare il problema. Ora quel componente fa molto meno richieste/risposte UDP e il problema è risolto per noi. – auramo

7

ho finalmente deciso qual è il problema. Java IOException è fuorviante in quanto è "Nessun spazio disponibile per il buffer", ma il problema di root è che la tabella ARP locale è stata compilata. Su Linux, la ricerca della tabella ARP predefinita è 1024 (file/proc/sys/net/ipv4/neigh/default/gc_thresh1,/proc/sys/net/ipv4/neigh/default/gc_thresh2,/proc/sys/net/ipv4/nitrito/default/gc_thresh3).

Cosa stava accadendo nel mio caso (e presumo il tuo caso), è che il tuo codice Java sta inviando pacchetti UDP da un indirizzo IP che si trova nella stessa subnet dei tuoi indirizzi di destinazione. Quando questo è il caso, la macchina Linux eseguirà una ricerca ARP per tradurre l'indirizzo IP nell'indirizzo MAC dell'hardware. Dato che stai facendo saltare i pacchetti su molti diversi IP, la tabella ARP locale si riempie rapidamente, colpisce 1024, e cioè quando viene lanciata l'eccezione Java.

La soluzione è semplice, né aumentare il limite modificando i file che ho menzionato in precedenza, o spostare il server in una subnet diversa rispetto i vostri indirizzi di destinazione, che provoca poi la macchina Linux di eseguire più vicini ARP lookup (invece sarà essere gestito da un router sulla rete).

+0

Non è il primo posto che si guarderebbe. Come l'hai rotto? –

+0

@ ThorbjørnRavnAndersen: Non saprei come ora cercare questo, ma circa 8 anni fa '/ proc/slabinfo' aveva una voce separata per le voci" neigh "(ad es. ARP). Quando hai ottenuto 'ENOBUFS' hai appena visto' slabinfo' per vedere quali buffer erano. Al giorno d'oggi è probabilmente fuso in alcune delle voci 'kmalloc-size'. – ninjalj

0

Ho rilevato questo errore quando ho provato a eseguire il cluster di coerenza in due JVM locali utilizzando la connessione WIFI al database .. Se lo eseguo tramite Ethernet, funziona correttamente.

Problemi correlati