Mehrere TCP/IP Verbindungen
-
Ach nöö. Ein Tread pro Client und fertig ist die Laube.
ist eine einfache Lösung - ganz klar - und reicht sicherlich für seine Anforderungen
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich, da skaliert dann nichts mehr schön - erst wieder wenn man die Threads ueber verschiedene Prozesse(nicht Threads) verteilt, noch dazu ziehen die Threads auch ganz schoen Hauptspeicher weg (was auch bei Gigabytes RAM irgendwann bloed wird)
ansonsten werden die meisten Systeme die viele Verbindungen verwalten muessen mit
n Verbindungen pro Thread und n Prozessen geloest (z.B. Apache) - das "skaliert" dann auch wirklichAm besten, man frickelt dann was in assembler, statt auf die törichte Idee zu kommen, von vorne herein geplant zu haben
ich hoffe das war ein Witz - skalierbarkeit kann nur geplant funktionieren - die Zeit von Weg-"Optimierung" von Lastproblemen sind lange vorbeit, oder der Code ist wirklich totaler Müll (z.B. permanentes allokieren, kopieren, den Rest der schlimmen Dinge) - dann geht das schon noch

Skalierung bedeutet heute: wenig Threads mit (trivial) IO-Aufgaben, Multi-Threading und Multi-Prozessing, wenig Locking, sinnvolle Lockingstrategie, KEIN Assembler
-
Gast3 schrieb:
Ach nöö. Ein Tread pro Client und fertig ist die Laube.
ist eine einfache Lösung - ganz klar - und reicht sicherlich für seine Anforderungen
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich,
Ist mir recht egal, ob man Prozesse oder Threads nimmt. Warum dauert das Umschalten beo 300 Threads so lange?
ansonsten werden die meisten Systeme die viele Verbindungen verwalten muessen mit
n Verbindungen pro Thread und n Prozessen geloest (z.B. Apache) - das "skaliert" dann auch wirklichBeachte, daß http-serven trivial ist. Apache ist im Prinzip ein single-Threaded Nacheinanderausführer. Multi-Sachen nur, damit sich Clients nicht gegenseitig stören und um alle Prozessoren zu bewegen. Anfragen kann man prima lagern, bis Zeit da ist, dann komplett abarbeiten, und dann irgendwann raussenden, wenn wieder Zeit da ist. Allerdings werden logische Folgen wie if(einloggen());tun(); schon zerhackt, was man umständlich mit Session-Variablen und Wiedereinstiegspunkten simuliert.
Wenn man die Threadzahl einfach beschränken würde auf 75% Speicherauslastung, was hätte man dann für Mehrkosten? Pro Anfrage einmal Threadstart und -end, und kalte Sprungvorhersagetabellen? Macht großzügig aufgerundet hundert Mikrosekündchen? Aber die Webseite nudelt ein Script durch mod-reqrite, PHP und mySQK, und zahlt dafür 30ms.
-
Gast3 schrieb:
aber ab paar hundert(+) (denke so ab 300) Verbindungen mit Win7,x64,guter Prozessor wird das Umschalten zwischen den Threads schon mehr Zeit fressen als die TCP/IP-Verarbeitung an sich, da skaliert dann nichts mehr schön - erst wieder wenn man die Threads ueber verschiedene Prozesse(nicht Threads) verteilt, noch dazu ziehen die Threads auch ganz schoen Hauptspeicher weg (was auch bei Gigabytes RAM irgendwann bloed wird)
heisse luft
wo sind deine zahlen?
wo sind die sources mit denen man die benchmarks selbst wiederholen kann?
-
volkard schrieb:
Pro Anfrage einmal Threadstart und -end, und kalte Sprungvorhersagetabellen? Macht großzügig aufgerundet hundert Mikrosekündchen? Aber die Webseite nudelt ein Script durch mod-reqrite, PHP und mySQK, und zahlt dafür 30ms.
100 µs sind schon einmal die richtige Größenordnung. Invertiert sind das gerade einmal 10 kHz. Maximal gehen damit also 10000 Verbindungen pro Sekunde und Hardware-Thread. Das reicht sicher für 99% der Anwendungen. Wenn man aber über Performance spricht, meint man eigentlich immer die 1% der Anwendungen, bei denen tatsächlich optimiert werden muss. Bei den 1% lässt man sich dann etwas schlaueres einfallen als Threads.
Wenn man PHP benutzt, ist das sowieso alles egal. Dann kann man auch seinen Webserver in Bash mit
netcatschreiben.Beim Ausliefern von statischen Dateien wäre ein Thread pro Verbindung ein unnötiger Flaschenhals.
-
TyRoXx schrieb:
Beim Ausliefern von statischen Dateien wäre ein Thread pro Verbindung ein unnötiger Flaschenhals.
Ja.
TyRoXx schrieb:
Wenn man aber über Performance spricht, meint man eigentlich immer die 1% der Anwendungen, bei denen tatsächlich optimiert werden muss. Bei den 1% lässt man sich dann etwas schlaueres einfallen als Threads.
Akso sollten Leute, die noch fragen müssen, ob sie dies oder jenes machen, zu 99% die Antwort "Ein Thread pro Client" bekommen. Das sehe ich auch so.
Sowas da http://highscalability.com/blog/2013/5/13/the-secret-to-10-million-concurrent-connections-the-kernel-i.html betrifft weniger als 1% fürchte ich beinahe.
-
volkard schrieb:
Sowas da http://highscalability.com/blog/2013/5/13/the-secret-to-10-million-concurrent-connections-the-kernel-i.html betrifft weniger als 1% fürchte ich beinahe.
Und wenn es einen betrifft will man eh einen custom Treiber verwenden.

The kernel isn’t the solution. The kernel is the problem.
Besser hätte ich es nicht schreiben können.
-
Das eigentliche Hauptargument gegen Thread-per-Connection ist/war, dass es zwar skalierbar sein kann, es aber Systeme gibt/gab, wo der Scheduler eben nicht mit O(1) läuft, sodass diese Lösung nicht skalierbar UND plattformunabhängig war/ist.
Der Artikel ist ganz nett, sieht aber wie ein Horror-Szenario/Wunschdenken aus. Relativ betrachtet werden vielleicht 1% der Leute sich mit Millionen Verbindungen beschäftigen müssen, aber die absolute Zahl wird bestimmt ansteigen. Die sollen dann alle ihre eigenen Kernel-Mode-Treiber schreiben (á la Microsoft IIS' HTTP-Treiber) und lock-freie Algorithmen benutzen: allesamt extrem hoch spezialisierte Einzelbereiche. Kaum auszudenken, wie einem da die Bugs, ABA-Probleme, Deadlocks (und dank Treiber auch noch) Kernel-panics/Bluescreens um die Ohren fliegen werden.
-
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
-
mehr schrieb:
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
Machen das nicht alle Softwareentwickler die ganze Zeit? Erfahrung fällt nicht vom Himmel.
-
mehr schrieb:
TL;DR: Keiner von euch hat Erfahrung und ihr ratet alle rum.
Bei welchem Punkt?
Aber das alle rumraten, das glaube ich nicht.
-
Jodocus schrieb:
Die sollen dann alle ihre eigenen Kernel-Mode-Treiber schreiben (á la Microsoft IIS' HTTP-Treiber) und lock-freie Algorithmen benutzen: allesamt extrem hoch spezialisierte Einzelbereiche. Kaum auszudenken, wie einem da die Bugs, ABA-Probleme, Deadlocks (und dank Treiber auch noch) Kernel-panics/Bluescreens um die Ohren fliegen werden.
Oder sie nutzen halt Projekte wie PF_RING.

-
Oder die 99.99% der Entwickler die Software schreiben wo es vollkommen egal ist halten einfach den Rand, anstatt dass sie versuchen ihre Kollegen zu missionieren.
Ich möchte nicht wissen wie viel Geld vernichtet wurde durch schlechte Ratschläge wie "mach mal asynchron weil sonst is voll langsam". Und dann sitzt Otto-Normalprogrammierer da und programmiert sich nen Ast ab für den < 1 call/second Server. Was sonst in 10 Minuten erledigt gewesen wäre.
-
Wären 2 Threads pro Client nicht angebrachter, da TCP Sockets full duplex sind?
-
hustbaer schrieb:
Mein Vorschlag wäre pro Verbindung einen Thread zu nehmen, und dann alles mit synchronem IO zu machen.
Super einfach und wer behaupten möchte dass es von der Performance her schlecht ist, der soll es mir erstmal beweisen
Danke, da ich bisher wenig mit Threads gearbeitet habe. Kannst du es mir bitte etwas genauer erläutern?
Gruß
-
socket = socket() bind(socket) listen(socket) while(true) { new_socket = accept(socket) new_thread(new_socket); } void new_thread(socket) { recv(socket); send(socket); ... closesocket(socket); }
-
Danke für die Antwort.
Achso logich der Thread bleibt beim ersten resv hängen aber dafür wird dann der nachfolgende Thread ausgeführt.
kapiert!
-
^ Oder ?
Also wenn anschließend ein zweiter Thread kommen würde. Würde dann der erste bei resv einen blocking call erhalten und der nächste würde ausgeführt werden?
Danke Gruß
-
Der Hauptthread empfängt neue Clients und deligiert sie in neue Threads, die dann blocken/whatever machen. Er selber blockiert nur während er auf neue Clients wartet, sonst nicht. Falls doch, dann würden halt Clients nicht mehr akzeptiert werden und früher oder später abgewiesen.
-
hustbaer schrieb:
Ich möchte nicht wissen wie viel Geld vernichtet wurde durch schlechte Ratschläge wie "mach mal asynchron weil sonst is voll langsam". Und dann sitzt Otto-Normalprogrammierer da und programmiert sich nen Ast ab für den < 1 call/second Server. Was sonst in 10 Minuten erledigt gewesen wäre.
Ach, aber "Otto" kommt von selbst auf die Idee einen neuen Thread für die Verbindung zu erstellen?
"Otto" hat typischerweise auch keine Ahnung von Multi-Threading und produziert fröhlich Abstürze. Ich möchte nicht wissen, wie viel Geld seiner Kunden dadurch vernichtet wurde.
Ich halte diese Otto-Normalidiotenbeispiele für schädlicher als die guten Ratschläge. Die zementieren nur die Don't-Care-Mentalität, die ohnehin schon zu präsent ist. Wenn man von seinen Mitmenschen erwartet, dass sie sich idiotisch verhalten, werden so das auch tun.
Sinnvoller fände ich es den Otto mal grundsätzlich über Nebenläufigkeit aufzuklären. Wenn er verstanden hat, was alles möglich ist, kann er gerne einen Thread pro Client verwenden. Wenn man ihm zusäzlich erklärt hat, wie man Software-Komponenten voneinander trennt, kann er später problemlos seinen Thread-Ansatz überarbeiten, falls es mal erforderlich werden sollte.
-
Hier mal ein kleiner Echo-Server (für Linux), der pro Verbindung einen neuen Thread aufmacht:
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <iostream> #include <thread> #include <vector> int main() { using namespace std; int listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listen_socket < 0) { cerr << "socket() failure\n"; return -1; } sockaddr_in saddr{ }; saddr.sin_port = htons(40001); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listen_socket, reinterpret_cast<sockaddr*>(&saddr), sizeof saddr) < 0) { cerr << "bind() failure\n"; close(listen_socket); return -2; } if(listen(listen_socket, SOMAXCONN) < 0) { cerr << "listen() failure\n"; close(listen_socket); return -3; } for(;;) { sockaddr_in client_addr{ }; socklen_t addr_len = sizeof client_addr; int client_socket = accept(listen_socket, reinterpret_cast<sockaddr*>(&client_addr), &addr_len); if(client_socket < 1) { cerr << "accept() failure\n"; } else { thread([=]() { vector<char> buffer(512); int result = 0; do { result = recv(client_socket, &buffer[0], buffer.size(), 0); if(result < 0) { cerr << "recv() failure\n"; close(client_socket); return; } else if(result != 0) { if(send(client_socket, &buffer[0], result, 0) < 0) { cerr << "send() failure\n"; close(client_socket); return; } } else { close(client_socket); return; } } while(true); }).detach(); } } }Edit: Trotzdem sei dir schwer geraten, eine Library zu benutzen. Dann musst du dich auch nicht mit Plattformabhängigkeit und I/O-Strategien wie 1 Thread pro Verbindung etc. rumplagen.