20 Empfängt pro Sekunde mit SocketAsyncEventArgs

9

Ein TCP-Server wird mit SocketAsyncEventArgs und asynchronen Methoden als Windows-Dienst entwickelt. Ich habe diese 2 Codezeilen am Anfang von Main:

%Vor%

Und beide geben wahr zurück (zurückgegebene Werte werden protokolliert). Jetzt beginnen 2000 bis 3000 Clients, Nachrichten an diesen Server zu senden und es beginnt Verbindungen zu akzeptieren (ich zähle die Anzahl der Verbindungen und es ist wie erwartet - Es gibt einen Verbindungspool). Die Threadanzahl des Serverprozesses wird auf ~ 2050 bis ~ 3050 anwachsen. So weit so gut!

Jetzt gibt es eine Received-Methode, die entweder aufgerufen wird, nachdem ReceiveAsync true oder Completed von SocketAsyncEventArgs zurückgegeben hat.

Und hier beginnen die Probleme: Unabhängig davon, wie viele Clients verbunden sind und wie viele Nachrichten sie senden, wird Received höchstens 20 Mal in einer Sekunde aufgerufen! Und wenn die Anzahl der Clients zunimmt, fällt diese Anzahl (20) auf ~ 10 ab.

Umgebung: TCP Server und Clients werden auf demselben Rechner simuliert. Ich habe den Code auf 2 Maschinen getestet, einer hat eine 2-Kern-CPU und 4 GB RAM und der andere hat eine 8-Kern-CPU und 12 GB RAM. Es gibt (noch) keinen Datenverlust und manchmal erhalte ich bei jeder Empfangsoperation mehr als eine Nachricht. Das ist gut. Aber wie kann die Anzahl der Empfangsoperationen erhöht werden?

Zusätzliche Hinweise zur Implementierung: Der Code ist groß und enthält viele verschiedene Logiken. Eine allgemeine Beschreibung wäre: Ich habe eine einzelne SocketAsyncEventArgs zum Akzeptieren neuer Verbindungen. Es funktioniert großartig. Jetzt erstelle ich für jede neue akzeptierte Verbindung ein neues SocketAsyncEventArgs zum Empfangen von Daten. Ich stelle dieses (die SocketAsyncEventArgs, die für Empfang erstellt werden) in einem Pool. Es wird nicht wiederverwendet, aber UserToken wird zum Verfolgen von Verbindungen verwendet. zum Beispiel werden diese Verbindungen, die getrennt sind oder die Verbindung, die keine Daten für 7 Minuten gesendet hat, geschlossen und entsorgt (Das AcceptSocket von SocketAsyncEventArgs wird heruntergefahren (beide), geschlossen und entsorgt und das SocketAsyncEventArgs-Objekt selbst). Hier ist eine Sudoklasse, die diese Aufgabe ausführt, aber alle anderen Logik- und Protokollierungs- und Fehlerüberprüfungen und alles andere wird entfernt, um es einfach und klar zu machen (vielleicht ist es dann leichter, den problematischen Code zu erkennen):

%Vor%     
Kaveh Shahbazian 26.12.2012, 18:52
quelle

1 Antwort

4

Der Grund für die Verlangsamung ist, dass jeder dieser Threads einen Kontextwechsel erfordert, und das ist eine relativ teure Operation. Je mehr Threads Sie hinzufügen, desto mehr Prozent Ihrer CPU werden nur für den Kontextwechsel und nicht für Ihren eigentlichen Code ausgegeben.

Sie haben diesen Ein-Thread-pro-Client-Engpass auf eine ziemlich merkwürdige Weise getroffen. Der springende Punkt des serverseitigen Async ist es, die Anzahl der Threads zu reduzieren, um nicht einen Thread pro Client zu haben, sondern idealerweise nur einen oder zwei für jeden der logischen Prozessoren in Ihrem System. p>

Der von Ihnen gepostete Async-Code sieht gut aus, daher kann ich nur raten, dass Ihre Process -Methode etwas Nicht-Async hat, das I / O blockiert, d. h. einen Datenbank- oder Dateizugriff. Wenn I / O blockiert, erkennt der .NET-Thread-Pool dies und spult automatisch einen neuen Thread auf - er ist hier praktisch außer Kontrolle geraten, wobei der I / O in Process ein Engpass ist.

Eine asynchrone Pipeline muss wirklich 100% asynchron sein, um einen signifikanten Nutzen daraus zu ziehen. Bei Half-In schreiben Sie komplexen Code, der genauso schlecht funktioniert wie einfacher Sync-Code.

Wenn Sie die Process -Methode absolut nicht asynchron ausführen können, haben Sie möglicherweise Glück, sie zu fälschen. Lassen Sie die Dinge in einer Warteschlange zur Verarbeitung in einem kleinen Threadpool mit begrenzter Größe warten.

    
Cory Nelson 26.12.2012, 19:45
quelle