[Konzept] Optimiertes Clienthandling

Cromon

Erfahrenes Mitglied
Hallo zusammen!

Nachdem ich nach langer Zeit ein kleines 3D-Spiel endlich fertig programmiert habe (und es den Zweck etwas neues zu lernen massiv übertroffen hat (ich glaube ich habe dabei mehr gelernt als in all den tausenden von Seiten die ich in Büchern gelesen habe)) habe ich mich nun ans nächste grosse Kapitel dafür gesetzt, den Server. Auch hier gehe ich wieder mit der Absicht dran Erfahrungen zu sammeln und mein Wissen zu vergrössern.

So viel zum Vorspann, nun gleich mal vorneweg worum es mir in diesem Post geht. Bevor ich beginne etwas am Server zu implementieren habe ich mir mal einige grundsätzliche Fragen gestellt. Eine davon ist wie ich die optimale Bilanz zwischen Resourcenschonung und Latenz auf für theoretisch Zehntausende von Spielern gewährleisten kann.

Genau zu oben erwähtem Aspekt habe ich mir einige Gedanken gemacht. Die erste Möglichkeit die mir in den Sinn kam war einen Socket zu verwenden, welcher eine vordefinierte Anzahl von Sockets via select verwaltet. Die Vorteile sind grundsätzlich einfach, es wird nur etwas getan, wenn wirklich etwas geschehen muss, die Implementation ist einfach. Aber die Nachteile sind doch etwas gross. Da wäre die Tatsache, dass gewisse Requests doch ein wenig dauern was dann alle anderen Connections blockiert. Ausserdem ist man limitiert auf die bei Kompilierzeit angegebene Anzahl Sockets (was sich ja aber leicht beheben liesse). Ausserdem ist der Thread ständig damit beschäftigt die Sockets zu überprüfen auf ihre Lesbarkeit auch wenn nichts drin ist, das ist von mir aus gesehen auch eher suboptimal.

Die nächste Möglichkeit die mir in den Sinn kam waren blockierende Sockets in ihrem eigenen Thread. Jede Verbindung bekommt ihren Thread in der der Socket blockiert und auf Input wartet. Dadurch passiert wirklich nichts bis irgendwo Input gekommen ist. Auch hier sind die Vorteile wieder klar: Schont den Prozessor und ist leicht zu implementieren. Die grosse Nachteil ist meines Erachtens, dass man Wohl sehr schnell ans Limit kommt wegen der Anzahl gleichzeitiger Threads, welche das OS unterstützt. Ansonsten sagt mir diese Möglichkeit eigentlich sehr zu!

Was mir bis jetzt am besten erscheint ist eine Kombination aus diesen beiden Varianten. Nichtblockierende Sockets in Threadgruppen. Man hat beispielweise 200 fixe Threadgruppen. Jede Threadgruppe läuft in ihrem eigenen Thread. Kommt eine Verbindung rein, so wird in der ersten Threadgruppe diese Verbindung verwaltet, die nächste in der zweiten, usw. Nach 200 Verbindungen kommt die 201te Verbindung wieder in die erste Threadgruppe. Hat man also z.B. 10000 aktive Spieler, hat es pro Threadgruppe 50 Verbindungen, die verwaltet werden. Dadurch werden also durch einen längeren Request maximal 50 andere Verbindungen etwas verzögert. Die Sockets an und für sich sind dann nicht blockierend und werden einfach über einen Empfangsaufruf auf ihre Lesbarkeit geprüft (je nach dem ob Bytes gelesen wurden).

Die einzige Frage die sich mir stellt: Sind 200 Thread sinnvoll in einer Applikation? Man liest ja oft, dass so 10-20 Threads ausreichen sollten, ich finde jedoch dass gerade bei so etwas die Threadzahl schnell auch mal darüber gehen kann.

Wie ist eure Meinung? Findet ihr meine Idee sinnvoll? Entspricht sie gem. euren Erfahrungen den Kriterien, die ich wünsche? Kennt ihr vielleicht noch bessere Varianten?

Gruss
Cromon
 
Hallo Cromon

Die erste Möglichkeit die mir in den Sinn kam war einen Socket zu verwenden, welcher eine vordefinierte Anzahl von Sockets via select verwaltet. Die Vorteile sind grundsätzlich einfach, es wird nur etwas getan, wenn wirklich etwas geschehen muss, die Implementation ist einfach. Aber die Nachteile sind doch etwas gross. Da wäre die Tatsache, dass gewisse Requests doch ein wenig dauern was dann alle anderen Connections blockiert.
Den Nachteil sollte man umgehen können, wenn man die Überwachung der Sockets von der Bearbeitung der Requests trennt. Das heißt jder Request bekommt einen eigenen Thread und belastet nicht den Überwachungsthread. Außerdem wird der Request-Thread ja nach der Bearbeitung freigegeben. Es laufen nur die Threads, die was zu tun haben und es brauchen keine auf Vorrat angelegt zu werden.
Ausserdem ist der Thread ständig damit beschäftigt die Sockets zu überprüfen auf ihre Lesbarkeit auch wenn nichts drin ist, das ist von mir aus gesehen auch eher suboptimal.
Select() arbeitet genauso resourcenschonend wie ein blockierender Socket.

Wie du siehst, gefällt mir die Select-Variante ganz gut :) , so dass ich zu den anderen Ideen erst mal nichts weiter schreibe. Bei sehr vielen Verbindungen werden einem ohnehin die Threads ausgehen, egal welche Variante man wählt. Stark frequentierte Systeme bestehen in der Regel aus mehreren Servern, Stichwort: Server Load Balancing.

Gruß
MCoder
 
Zurück