J'ai récemment commencé le développement d'une application faisant un usage intensif du réseautage. Une première tentative a été faite en utilisant RMI et pour plusieurs raisons, nous sommes passés à des sockets purs. Cependant, lors du test de sockets sur un réseau, ou même sur localhost, nous avons baissé à un taux de 25 demandes/seconde. Tout en utilisant RMI, il était supérieur de deux ordres de grandeur.Goulot d'étranglement des performances du socket Java: où?
Avec un peu plus de tests, nous avons obtenu à la suite (pour localhost):
- Envoi toujours le même objet: 31628 requêtes/s
- Envoi toujours un nouvel objet: 25 demandes/sec
- Seul le taux de création d'objet: 3-4 millions par seconde (si ce n'est pas le goulot d'étranglement)
Voici le code client: (côté serveur répond juste un « accusé de réception »)
public static void main(String[] args) throws IOException, ClassNotFoundException
{
Socket kkSocket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
kkSocket = new Socket("barium", 4444);
out = new ObjectOutputStream(kkSocket.getOutputStream());
in = new ObjectInputStream(kkSocket.getInputStream());
long throughput;
long millis;
TcpRequest hello = null;
throughput = 0;
millis = System.currentTimeMillis();
while (System.currentTimeMillis() < millis + 1000)
{
hello = new TcpRequest();
hello.service = "hello";
hello.payload = Math.random();
throughput++;
}
System.out.println("-------------------------------------------------------");
System.out.println("| Objects created: " + (throughput) + " requests/sec.");
System.out.println("-------------------------------------------------------");
throughput = 0;
millis = System.currentTimeMillis();
while (System.currentTimeMillis() < millis + 1000)
{
out.writeObject(hello);
Object res = in.readObject();
throughput++;
}
System.out.println("-------------------------------------------------------");
System.out.println("| Same object throughput: " + (throughput) + " requests/sec.");
System.out.println("-------------------------------------------------------");
throughput = 0;
millis = System.currentTimeMillis();
while (System.currentTimeMillis() < millis + 1000)
{
hello = new TcpRequest();
out.writeObject(hello);
Object res = in.readObject();
throughput++;
}
System.out.println("-------------------------------------------------------");
System.out.println("| New objetcs throughput: " + (throughput) + " requests/sec.");
System.out.println("-------------------------------------------------------");
out.close();
in.close();
kkSocket.close();
}
La classe TcpRequest est juste une classe factice sans rien de spécial. Donc, si créer un objet est rapide, si l'envoyer sur le réseau est rapide ... pourquoi envoyer un nouvel objet sur le réseau si lentement?!?!
Et si vous gardez un même objet et de modifier son contenu avant de l'envoyer, vous aurez également un taux élevé de transfert ... mais tomber dans le piège méchant:
Lorsque vous travaillez avec sérialisation il est important de garder à l'esprit que le ObjectOutputStream maintient une hashtable mappage des objets écrits dans le flux vers un descripteur. Lorsqu'un objet est écrit dans le flux pour la première fois, son contenu sera copié dans le flux. Toutefois, les écritures ultérieures résultent en un handle vers l'objet en cours d'écriture dans le flux .
... ce qui nous est arrivé et a causé quelques heures de débogage avant de le découvrir.
Donc, fondamentalement ... comment atteindre un débit élevé avec les sockets? (... Je veux dire, avec RMI étant une enveloppe autour de nous nous étions déjà deux ordres de grandeur plus haut!)
résolu:
En remplaçant:
out = new ObjectOutputStream(kkSocket.getOutputStream());
Avec:
out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()))
Les performances sont à nouveau normale (à peu près le même débit élevé avec le même cas de l'objet)
Identique à ce numéro: http://stackoverflow.com/questions/2251051/ performance-issue-using-javas-objet-streams-with-sockets ... pas de réponses non plus. – dagnelies
Si vos objets ne se réfèrent pas mutuellement, vous pouvez appeler ObjectOutputStream.reset() après l'écriture pour empêcher le flux de se souvenir de tous les objets qu'il a écrits. Bien qu'il provoque généralement des problèmes de mémoire sur le côté de lecture, pas de performances sur le côté serveur. Avez-vous essayé de profiler votre application pour voir où elle passe le plus de temps? –