Server mit vielen Socketverbindungen - Threads oder select()?
-
Ich wuerd definitic threads nehmen, wenn ned gar prozesse ....
wenn dir deine mainloop absemmelt, ist dein kompletter chatserver tot ...
bei multiprozessing koenntest den Initialprozess, der der das Handschake macht, auch die child prozesse ueberwachen lassen und bei inaktivitaet kicken.unter linux bringt dir multithreading im vergleich zu multiprozessing ned so viel, weil das eine eher schlecht, das andere dafuer um so besser implementiert ist.
Unter windows faehrst mit multithreading besser, ist da auch efektiver implementiert.Ciao .....
-
rüdiger schrieb:
Schau dir mal libnotify an, die benutzen je nach System epoll, kqueue oä. Was deutlich besser als select skaliert.
@tntnet
150 Threads? Das halte ich für unrealistisch klein!Im prinzip hast Du recht. Ich halte das auch für klein. Das war nun einmal eine Beobachtung auf meiner Kiste. Vielleicht war die Beobachtung auch falsch. Ich habe nicht so genau hin geschaut, da ich sowieso die Workqueue-Variante verwende. Aber ich nehme das zum Anlaß, noch mal nachzuprüfen. Also demnächst auf diesem Kanal mehr.

Tntnet
-
@tntnet
Würde mich sehr wundern. Ein paar tausend Threads sollten kein Problem auslösen (wird eben erst kritisch, wenn der Speicher knapp wird)@RHBaum
Wenn in Thread abstürzt ist der Server auch weg... Außerdem wären Prozesse und Threads bei einem Server, der Kommunikation unter den einzelnen Verbindungen benötigt, nicht so leicht implementierbar.
-
Hallo,
erstmal vielen dank für die zahlreichen antworten.
@tntnet: ich will nicht an deiner beobachtung zweifeln, doch 150 kommt mir doch ein bisschen wenig vor. Gerade dann wenn 99% davon im idle-status ist.
Aber die Idee mit der Jobqueue hört sich wirklcih gut an, doch davon habe ich keine erfahrung. Wenns anderes nicht so toll geht, nehme ich das aber mal in angriff :).Ich frage mich gerade, ob folgende Lösung sinnvoll ist:
Es wird alles mit select() abgehandelt, wenn die Datenmenge sehr klein ist (halt kurze bis normal lange chatnachrichten), sodass alle drankommen, denn das ist ja schnell versendet. Sollen allerdings in einer Session größere Mengen übertragen werden wird dies in einen Thread ausgelagert (was wahrscheinlich nicht sehr oft parallel passiert).
Wäre das zu realisieren oder müsste ich da was beachten?@RHBaum: Hm ich denke schon dass ich auf Multithreading setze, wenn ich mich für die Threadlösung entscheide. Denn mit Prozessen müsste ich noch viel zwischen den Prozessen per Sockets kommunizieren, was ich mit Threads nicht machen mus. Aber ich behalts mir im Hinterkopf

MfG
-
Das schöne an Open-Source ist, daß man schauen (und klauen) kann, wie es andere gemacht haben. Schau Dir mein Tntnet an. Da insbesondere die Dateien tnt/job.h, tnt/poller.h, tnt/worker.h, job.cpp, poller.cpp und worker.cpp im Verzeichnis framework/runtime. Insbesondere die Jobqueue in job.h wird dir möglicherweise helfen.
Übrigens habe ich da noch eine kleine Sicherheitsfunktion eingebaut. Wenn der Server startet, dann forkt er ein Kindprozess, welcher die Arbeit macht. Der Vaterprozess wartet nur darauf, daß der Kindprozess abstürzt und startet ihn dann neu. Dadurch gehen zwar die Verbindungen (was bei http gar nicht schlimm ist) und die Sessions (ärgerlich) verloren, aber der Server funktioniert weiter.
Und Du darfst ruhig an meiner Beobachtung zweifeln. Ich tue es ja selbst
. Heute Abend werde ich das nochmals in Angriff nehmen.Tntnet
-
Hi,
ich muß mich (zum Glück) korrigieren. Ich weiß nicht, wie ich auf die 150 gekommen bin, aber ich kann es nicht mehr nachvollziehen.
Ich habe ein Testprogramm geschrieben, welches so lange Threads erzeugt, wie es geht. Merkwürdigerweise schwankt die maximale Anzahl der Threads von Lauf zu Lauf ein wenig. Mal bekomme ich nach 8050, mal nach 8053 Threads den Fehler 11 (EAGAIN).
Tntnet
-
Black Shadow:
Unter windows wuerd ich auch threads favourisieren, unter Linux wuerde es zu ner Disskussion fuehren.
Am ende bestimmen aber deine anforderungen was das beste fuer dich ist ...
ruediger:
Wenn in Thread abstürzt ist der Server auch weg
Wenn er ne exception und damit nen terminate ausloest ja, aber wenn er sich nur aufhaengt, in nen undefinierten zustand geht?
Koennt man ja nen eigenen thread schreiben der nur die anderen threads ueberwacht und auf alive signale wartet ...
wie gesagt, kommt auch hier an, was man erreichen will. fuer nen einfacheren chat wuerd ich mir sowas auch ersparen ....Ciao ...
-
Ich tendiere auch zu Threads. Am Server wo ich schreibe hab ich das ganze auch mit Multithreading gelöst. Und dort gibts schon genug probleme zu lösen was Synchronisation angeht Aber das bei 150 schluss ist wäre mir neu. Der vorteil ist auch das ggf Blockierende Methoden nicht das gesamte system blockieren.
In der alten Version unserer Netzwerkschnittstelle war eben das senden nicht gethreaded und mit unter rauchte uns eine clientverbindung ab. Bis der Server gepeilt hat das diese Verbindung auf die gerade gesendet werden sollte nicht mehr da ist, stand die gesamte Main loop. Bei Threads ist es doch egal ob der aktuelle thread gerade blockiert. [Das mit der stehenden Mainloop war überhaupt der Grund das ich die netzwerkschnittstelle neu schreiben musst]
Daher also pro Threads.
-
RHBaum schrieb:
Wenn in Thread abstürzt ist der Server auch weg
Wenn er ne exception und damit nen terminate ausloest ja, aber wenn er sich nur aufhaengt, in nen undefinierten zustand geht?
Koennt man ja nen eigenen thread schreiben der nur die anderen threads ueberwacht und auf alive signale wartet ...
wie gesagt, kommt auch hier an, was man erreichen will. fuer nen einfacheren chat wuerd ich mir sowas auch ersparen ....Ciao ...
Es ist schlimmer: Wenn ein Thread nicht das tut, was er soll, könnte es sein, daß er in Speicher greift, der ihm nicht gehört. Das führt zum Absturz des Prozesses und nicht nur des Threads. Oder er modifiziert unkontrolliert Daten im lokalen Prozess, welcher die anderen Threads auch zum Amok laufen veranlassen könnte. Da hilft auch kein Überwachungsthread.
Der Vorteil bei Threads ist zum einen der geringere Resourcenverbrauch (unabhängig vom Betriebssystem) und zum anderen die wesentlich einfachere Kommunikation der Threads untereinander, was für einen Chat ja notwendig ist.
Tntnet
-
Das kommt sehr darauf an was man für Ziele hat, kurz- und langfristig.
Die Variante mit einem Thread pro Connection ist sicherlich relativ einfach zu programmieren (was nicht heisst dass man da nicht auch gewaltig Mist bauen kann).
Grösster Nachteil: Die skalierbarkeit ist Scheisse. Ein Thread braucht zwar nicht unbedingt 1 MB Stack (was unter Windows z.B. Default ist), dennoch wird man mit ein paar Kilobyte nicht auskommen. Und ein Context-Switch ist - gerade mit native Threads (aber auch mit den meisten Usermode Implementierungen) - nicht gerade billig. Wenn man also etwas baut was gut skalieren können soll, also eines Tages vielleicht 10000 oder 50000 Connections handlen soll, dann sind Threads ganz sicher die falsche Wahl.
Und mehrere Prozesse zu verwenden ist ganz sicher nicht die richtige Wahl.Die Alternative: asynchrones IO, z.B. das Proactor Pattern (siehe Boost asio).
Der grösste Nachteil dabei ist, dass es etwas schwieriger zu implementieren ist. Wenn man allerdings etwas Zeit in ein sauberes Design investiert kann man damit sehr schöne klare Programme schreiben. Wenn man blocking IO gewöhnt ist wird man zwar gezwungen sein seine Denkweise etwas anzupassen, was aber auch kein Schaden sein sollte.
Der grosse Vorteil: weniger Resourcen pro Verbindung, bessere Skalierbarkeit.
-
Black Shadow schrieb:
Hallo,
Ich frage mich gerade, ob folgende Lösung sinnvoll ist:
Es wird alles mit select() abgehandelt, wenn die Datenmenge sehr klein ist (halt kurze bis normal lange chatnachrichten), sodass alle drankommen, denn das ist ja schnell versendet. Sollen allerdings in einer Session größere Mengen übertragen werden wird dies in einen Thread ausgelagert (was wahrscheinlich nicht sehr oft parallel passiert).
Wäre das zu realisieren oder müsste ich da was beachten?Es ist sinnvoll.
Theoretisch kann man auch mit einem Select ganz ohne Threads anfangen und erst nach einigen Testläufen schauen wo das Programm blockiert um dort dann Teile in Threads zu stecken.Ich mache sowas eigentlich imemr recht gleich, ein Threadpool für die Clients, also ein Thread für z.B. 100 Clients, was je nach Einsatzzweck schwankt.
Damit verteilt sich die Last der reinen Netzwerkfunktionen.Die eigentlichen Berechnungen/Methoden werden dann je nach Aufwand nochmal in Threads ausgelagert, da gehst du denke ich schon den richtigen Weg.
Der Vorteil von Select ist ja eben der, dass man den Einsatz von Threads besser einteilen kann.
Kurz gesagt, ein Thread pro Client ist Verschwendung und zu aufwändig, ein einziger Thread für alles reicht nicht aus und blockiert am Ende noch.
-
Tellerrand schrieb:
Black Shadow schrieb:
Hallo,
Ich frage mich gerade, ob folgende Lösung sinnvoll ist:
Es wird alles mit select() abgehandelt, wenn die Datenmenge sehr klein ist (halt kurze bis normal lange chatnachrichten), sodass alle drankommen, denn das ist ja schnell versendet. Sollen allerdings in einer Session größere Mengen übertragen werden wird dies in einen Thread ausgelagert (was wahrscheinlich nicht sehr oft parallel passiert).
Wäre das zu realisieren oder müsste ich da was beachten?Es ist sinnvoll.
Theoretisch kann man auch mit einem Select ganz ohne Threads anfangen und erst nach einigen Testläufen schauen wo das Programm blockiert um dort dann Teile in Threads zu stecken.Ich mache sowas eigentlich imemr recht gleich, ein Threadpool für die Clients, also ein Thread für z.B. 100 Clients, was je nach Einsatzzweck schwankt.
Damit verteilt sich die Last der reinen Netzwerkfunktionen.Die eigentlichen Berechnungen/Methoden werden dann je nach Aufwand nochmal in Threads ausgelagert, da gehst du denke ich schon den richtigen Weg.
Der Vorteil von Select ist ja eben der, dass man den Einsatz von Threads besser einteilen kann.
Kurz gesagt, ein Thread pro Client ist Verschwendung und zu aufwändig, ein einziger Thread für alles reicht nicht aus und blockiert am Ende noch.Ich würde auf keinen Fall empfehlen dynamisch irgendwelche Threads zu erzeugen "wenn grössere Datenmengen übertragen werden". Schonmal deswegen nicht weil es mir viel zu heikel zu implementieren wäre. Normalerweise verwendet man einen Threadpool mit fixer Grösse -- das einzige was man hier relativ einfach und ungefährlich dynamisch machen könnte ist die anzahl der Threads im Pool abhängig von der Verbindungsanzahl anzupassen.
-
-
hustbaer schrieb:
Ich würde auf keinen Fall empfehlen dynamisch irgendwelche Threads zu erzeugen "wenn grössere Datenmengen übertragen werden". Schonmal deswegen nicht weil es mir viel zu heikel zu implementieren wäre. Normalerweise verwendet man einen Threadpool mit fixer Grösse -- das einzige was man hier relativ einfach und ungefährlich dynamisch machen könnte ist die anzahl der Threads im Pool abhängig von der Verbindungsanzahl anzupassen.
Ein Threadpool skaliert aber nicht.
Ich sehe auch gar nicht den Sinn von Threads, da diese ja bei einem Chatserver eh die größte Zeit nur darauf warten, dass der andere Thread die Daten gelesen hat und weiterreicht.
Ich würde so etwas wie libnotify benutzen (das kann glaube ich epoll, poll, select, kqueue, SIG sowieso und sollte sehr gut skalieren).
-
rüdiger schrieb:
hustbaer schrieb:
Ich würde auf keinen Fall empfehlen dynamisch irgendwelche Threads zu erzeugen "wenn grössere Datenmengen übertragen werden". Schonmal deswegen nicht weil es mir viel zu heikel zu implementieren wäre. Normalerweise verwendet man einen Threadpool mit fixer Grösse -- das einzige was man hier relativ einfach und ungefährlich dynamisch machen könnte ist die anzahl der Threads im Pool abhängig von der Verbindungsanzahl anzupassen.
Ein Threadpool skaliert aber nicht.
Ich sehe auch gar nicht den Sinn von Threads, da diese ja bei einem Chatserver eh die größte Zeit nur darauf warten, dass der andere Thread die Daten gelesen hat und weiterreicht.
Ich würde so etwas wie libnotify benutzen (das kann glaube ich epoll, poll, select, kqueue, SIG sowieso und sollte sehr gut skalieren).
Ich weiss nicht was du mit "ein thread pool skaliert nicht" meinst

Aber du hast wahrscheinlich Recht, für einen einfachen chat Server braucht man wohl nur einen Thread. Zumindest solange es nur einen "Raum" gibt macht ein 2. Thread kaum Sinn.
-
hustbaer schrieb:
Ich weiss nicht was du mit "ein thread pool skaliert nicht" meinst
naja, du erstellst N Threads und sobald du mehr als N Verbindungen hast, hast du ein Problem

Aber du hast wahrscheinlich Recht, für einen einfachen chat Server braucht man wohl nur einen Thread. Zumindest solange es nur einen "Raum" gibt macht ein 2. Thread kaum Sinn.
Bei mehreren Räumen hört sich das sogar vernünftig an. Aber das geht ja nur gut, wenn jeder Nutzer für jeden Raum, den er betritt eine Verbindung öffnen müsste.
Natürlich würde es auch mit Threading klappen. Aber dann lieber kein Shared-State Threading, sondern eher Message basiertes Threading (ala Erlang)
-
rüdiger schrieb:
hustbaer schrieb:
Ich weiss nicht was du mit "ein thread pool skaliert nicht" meinst
naja, du erstellst N Threads und sobald du mehr als N Verbindungen hast, hast du ein Problem

Nein, ich meinte du hast N Threads in einem Pool die alle bloss "haben fertig" Nachrichten von asynchronen IOs abarbeiten. Im Prinzip denke ich dasselbe wie du vorgeschlagen hast, bloss eben mit einem Thread Pool anstelle eines einzelnen Threads. Und das skaliert dann recht gut

Aber du hast wahrscheinlich Recht, für einen einfachen chat Server braucht man wohl nur einen Thread. Zumindest solange es nur einen "Raum" gibt macht ein 2. Thread kaum Sinn.
Bei mehreren Räumen hört sich das sogar vernünftig an. Aber das geht ja nur gut, wenn jeder Nutzer für jeden Raum, den er betritt eine Verbindung öffnen müsste.
Natürlich würde es auch mit Threading klappen. Aber dann lieber kein Shared-State Threading, sondern eher Message basiertes Threading (ala Erlang)
Nö, es müsste nicht jeder User für jeden Raum den er betritt eine eigene Verbindung aufmachen. Ich denke bloss so: bei mehreren Räumen muss nicht jede Nachricht von jedem User in der selben (einzigen) Queue landen, bzw. die Nachrichten aus einer Queue müssen auch nicht an alle User verteilt werden. Man hat mehrere Räume, deren Queues man z.B. mit unterschiedlichen Mutexes schützen kann. So können N Threads in N unterschiedlichen Räumen gleichzeitig Nachrichten reinhängen bzw. auslesen, und dadurch macht es u.U. dann Sinn mehrere Threads zu verwenden. Vorausgesetzt es sind mehrere Cores da die man beschäftigen möchte. Mit bloss einem Core ist es wieder ziemlich witzlos. Genauso mit bloss einem Raum -- dann kann man sich nämlich wenn man bloss einen Thread verwendet den ganzen Synchronisierungsoverhead sparen.