J'ai une application Java sur linux qui ouvre le socket UDP et attend les messages.
Après quelques heures sous forte charge, il y a une perte de paquets, c'est-à-dire que les paquets sont reçus par le noyau mais pas par mon application (nous voyons les paquets perdus dans sniffer, nous voyons les paquets UDP perdus dans netstat, nous ne voyons pas ces paquets dans nos journaux d'application).
Nous avons essayé d'agrandir les tampons de socket mais cela n'a pas aidé - nous avons commencé à perdre des paquets plus tard qu'avant, mais c'est tout.
Pour le débogage, je veux savoir à quel point le tampon udp du système d'exploitation est complet, à tout moment. Googlé, mais n'a rien trouvé. Pouvez-vous m'aider?
P.S. Les gars, je suis conscient que UDP n'est pas fiable. Cependant, mon ordinateur reçoit tous les messages UDP, tandis que mon application ne peut pas en consommer certains. Je veux optimiser mon application au maximum, c'est la raison de la question. Merci.
Linux fournit les fichiers /proc/net/udp
et /proc/net/udp6
, qui répertorie toutes les sockets UDP ouvertes (pour IPv4 et IPv6, respectivement). Dans les deux, les colonnes tx_queue
et rx_queue
affiche les files d'attente sortantes et entrantes en octets.
Si tout fonctionne comme prévu, vous ne verrez généralement aucune valeur différente de zéro dans ces deux colonnes: dès que votre application génère des paquets, ils sont envoyés via le réseau, et dès que ces paquets arrivent du réseau, votre application se réveillera et recevez-les (l'appel recv
revient immédiatement). Vous pouvez voir le rx_queue
monte si votre application a le socket ouvert mais n'invoque pas recv
pour recevoir les données, ou si elle ne traite pas ces données assez rapidement.
UDP est un protocole parfaitement viable. C'est le même vieux cas du bon outil pour le bon travail!
Si vous avez un programme qui attend les datagrammes UDP, puis s'éteint pour les traiter avant de retourner en attendre un autre, votre temps de traitement écoulé doit toujours être plus rapide que le taux d'arrivée des datagrammes le plus défavorable. Si ce n'est pas le cas, la file d'attente de réception de socket UDP commencera à se remplir.
Cela peut être toléré pour de courtes rafales. La file d'attente fait exactement ce qu'elle est censée faire: mettre les datagrammes en file d'attente jusqu'à ce que vous soyez prêt. Mais si le taux d'arrivée moyen provoque régulièrement un retard dans la file d'attente, il est temps de repenser votre programme. Il y a deux choix principaux ici: réduire le temps de traitement écoulé via des techniques de programmation astucieuses et/ou multi-thread votre programme. L'équilibrage de charge sur plusieurs instances de votre programme peut également être utilisé.
Comme mentionné, sous Linux, vous pouvez examiner le système de fichiers proc pour obtenir un état sur ce que fait UDP. Par exemple, si je cat
le /proc/net/udp
node, j'obtiens quelque chose comme ceci:
$ cat /proc/net/udp
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
40: 00000000:0202 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 3466 2 ffff88013abc8340 0
67: 00000000:231D 00000000:0000 07 00000000:0001E4C8 00:00000000 00000000 1006 0 16940862 2 ffff88013abc9040 2237
122: 00000000:30D4 00000000:0000 07 00000000:00000000 00:00000000 00000000 1006 0 912865 2 ffff88013abc8d00 0
De cela, je peux voir qu'un socket appartenant à l'ID utilisateur 1006 écoute sur le port 0x231D (8989) et que la file d'attente de réception est à environ 128 Ko. Comme 128 Ko est la taille maximale de mon système, cela me dit que mon programme est terriblement faible pour suivre les datagrammes qui arrivent. Jusqu'à présent, il y a eu 2237 suppressions, ce qui signifie que la couche UDP ne peut plus placer de datagrammes dans la file d'attente de socket et doit les supprimer.
Vous pouvez observer le comportement de votre programme au fil du temps, par exemple en utilisant:
watch -d 'cat /proc/net/udp|grep 00000000:231D'
Notez également que la commande netstat fait à peu près la même chose: netstat -c --udp -an
Ma solution pour mon programme weenie, sera le multi-thread.
À votre santé!
rx_queue vous indiquera la longueur de la file d'attente à un instant donné, mais il ne vous dira pas à quel point la file d'attente a été remplie, c'est-à-dire la marque des hautes eaux. Il n'y a aucun moyen de surveiller constamment cette valeur, et aucun moyen de l'obtenir par programme (voir Comment puis-je obtenir la quantité de données en file d'attente pour le socket UDP? ).
La seule façon que j'imagine de surveiller la longueur de la file d'attente est de déplacer la file d'attente dans votre propre programme. En d'autres termes, démarrez deux threads - l'un lit la socket aussi vite que possible et vide les datagrammes dans votre file d'attente; et l'autre est votre programme tirant de cette file d'attente et traitant les paquets. Bien entendu, cela suppose que vous pouvez vous assurer que chaque thread se trouve sur un processeur distinct. Vous pouvez maintenant surveiller la longueur de votre propre file d'attente et garder une trace de la ligne des hautes eaux.
Le processus est simple:
Si vous le souhaitez, suspendez le processus de demande.
Ouvrez le socket UDP. Vous pouvez l'attraper du processus en cours en utilisant /proc/<PID>/fd
si nécessaire. Ou vous pouvez ajouter ce code à l'application elle-même et lui envoyer un signal - le socket sera déjà ouvert, bien sûr.
Appelez recvmsg
en boucle le plus rapidement possible.
Comptez le nombre de paquets/octets que vous avez obtenus.
Cela supprimera tous les datagrammes actuellement mis en mémoire tampon, mais si cela casse votre application, votre application était déjà cassée.