2009-07-18 5 views
2

Parfois, lors de l'envoi d'une grande quantité de données via SocketChannel.write(), le tampon TCP sous-jacent est rempli, et je dois réessayer continuellement write() jusqu'à ce que les données sont toutes envoyées.Problème de thread Java NIO avec SocketChannel.write()

Alors, je pourrais avoir quelque chose comme ceci:

public void send(ByteBuffer bb, SocketChannel sc){ 
    sc.write(bb); 
    while (bb.remaining()>0){ 
     Thread.sleep(10); 
     sc.write(bb);   
    } 
} 

Le problème est que la question de temps en temps avec un grand ByteBuffer et un tampon TCP sous-jacente débordement signifie que cet appel à envoyer() bloquera pour un inattendu quantité de temps. Dans mon projet, il y a des centaines de clients connectés simultanément, et un retard causé par une connexion socket peut amener l'ensemble du système à une analyse jusqu'à ce que ce retard d'un SocketChannel soit résolu. Lorsqu'un retard survient, il peut provoquer une réaction en chaîne du ralentissement dans d'autres zones du projet, et une faible latence est importante.

J'ai besoin d'une solution qui prendra en charge ce problème de débordement de tampon TCP de manière transparente et sans que tout bloque lorsque plusieurs appels à SocketChannel.write() sont nécessaires. J'ai envisagé de mettre send() dans une classe séparée en étendant Thread afin qu'il s'exécute comme son propre thread et ne bloque pas le code appelant. Cependant, je suis préoccupé par le surcoût nécessaire pour créer un thread pour CHAQUE connexion socket que je maintiens, surtout quand 99% du temps, SocketChannel.write() réussit au premier essai, ce qui signifie qu'il n'y a pas besoin que le thread soit là . (En d'autres termes, mettre send() dans un thread séparé n'est vraiment nécessaire que si la boucle while() est utilisée - seulement dans les cas où il y a un problème de buffer, peut-être 1% du temps). seulement 1% du temps, je n'ai pas besoin de la surcharge d'un thread pour l'autre 99% des appels à envoyer(). J'espère que cela a du sens ... Je pourrais vraiment utiliser quelques suggestions. Merci!

Répondre

1

Avant Java NIO, vous avait pour utiliser un thread par socket pour obtenir de bonnes performances. C'est un problème pour toutes les applications basées sur des sockets, pas seulement Java. La prise en charge des E/S non bloquantes a été ajoutée à tous les systèmes d'exploitation pour résoudre ce problème. L'implémentation Java NIO est basée sur Selectors.

Voir l'article The definitive Java NIO book et cet article On Java pour commencer. Notez cependant que c'est un sujet complexe et qu'il apporte encore quelques problèmes de multithreading dans votre code. Google "NIO non bloquant" pour plus d'informations.

1

Vous n'avez pas besoin de sleep() car l'écriture retournera immédiatement ou bloquera. Vous pourriez avoir un exécuteur auquel vous passez l'écriture si elle n'écrit pas la première fois. Une autre option consiste à avoir un petit pool de thread pour effectuer les écritures. Toutefois, la meilleure option pour vous est peut-être d'utiliser un sélecteur (comme cela a été suggéré) afin de savoir quand une prise est prête à effectuer une autre écriture.

+0

Le sommeil a du sens si le socket n'est pas bloquant avec un buffer complet, ce que ne dit pas l'original. –

+0

Il suppose que le tampon prendra toujours au moins 10 ms à effacer. Personnellement, si le tampon ne disparaît pas presque immédiatement, soit le tampon est trop petit, soit le lecteur est trop lent. Je fermerais la connexion. –

+0

Si le tampon d'octets dans l'exemple est plus grand que le tampon de sortie du socket du noyau, le code est inefficace, car il peut probablement écrire plus vite que la carte réseau peut écrire et ainsi se saturer. En 10ms de sommeil, la carte réseau pourrait écrire environ 1 Mo sur un réseau de 1 Go, bien plus que la taille de tampon de sortie de socket typique. Si le bytebuffer n'est que de quelques Ko c'est probablement le lecteur qui est trop lent, alors il est judicieux de mettre un peu en pause, en utilisant OP_WRITE est plus complexe mais éviterait de bloquer un thread pour dormir ainsi que buffer underruns. –

0

Pour des centaines de connexions, vous n'avez probablement pas besoin de vous embêter avec NIO. De bonnes vieilles douilles et fils de blocage vous feront plaisir. Avec NIO, vous pouvez enregistrer un intérêt dans OP_WRITE pour la touche de sélection, et vous serez averti quand il y a de la place pour écrire plus de données.

0

Il y a quelques choses que vous devez faire, en supposant que vous ayez déjà une boucle en utilisant Selector.select(); pour déterminer quelles prises sont prêtes pour les E/S.

  • Définissez le canal de socket sur non-bloquant après l'avoir créé, sc.configureBlocking (false);
  • Écrivez (éventuellement des parties de) le tampon et vérifiez s'il reste quelque chose. Le tampon lui-même prend soin de la position actuelle et combien reste.

Quelque chose comme

sc.write(bb); 
if(sc.remaining() == 0) 
    //we're done with this buffer, remove it from the select set if there's nothing else to send. 
else 
    //do other stuff/return to select loop 
  • Débarrassez-vous de votre boucle while qui dort
0

Je suis face à certains des mêmes problèmes en ce moment:
- Si vous avez une petite quantité de connexions, mais avec de gros transferts, je créerais simplement un pool de threads, et laisserais les blocs d'écriture pour les threads d'écriture.
-. Si vous avez beaucoup de connexions, vous pouvez utiliser pleinement Java NIO, et inscrivez-vous OP_WRITE sur vos prises ées accept(), puis attendez que le sélecteur de venir en

Le livre Orielly Java NIO a tous ce.
également: http://www.exampledepot.com/egs/java.nio/NbServer.html?l=rel

Certains en ligne de recherche m'a amené à croire NIO est assez surpuissant, sauf si vous avez beaucoup de connexions entrantes. Sinon, si c'est juste quelques gros transferts - alors utilisez simplement un thread d'écriture. Il aura probablement une réponse plus rapide. Un certain nombre de personnes ont des problèmes avec NIO pas repsonding aussi vite qu'ils le veulent. Puisque votre thread d'écriture est sur son propre blocage, il ne vous blessera pas.

0

I deuxième Toms répondre à propos de l'enregistrement OP_WRITE sur la clé de sélection. Je voudrais recommander le Rox Java NIO Tutorial.

0

Plus je lis sur Java NIO, plus ça me donne les willies. Quoi qu'il en soit, je pense que cet article répond à votre problème ...

http://weblogs.java.net/blog/2006/05/30/tricks-and-tips-nio-part-i-why-you-must-handle-opwrite

On dirait que ce gars-là a une solution plus élégante que la boucle de sommeil.

Aussi je suis rapidement arrivé à la conclusion que l'utilisation de Java NIO en soi est trop dangereux. Où je peux, je pense que j'utiliserai probablement Apache MINA qui fournit une belle abstraction sur Java NIO et ses petites «surprises».

Questions connexes