Ich brauche einen Python-TCP-Server, der mindestens zehntausende gleichzeitige Socket-Verbindungen verarbeiten kann. Ich habe versucht, Python SocketServer-Paketfunktionen sowohl im Multiprozessor- als auch im Multithread-Modus zu testen, aber beide waren weit von der gewünschten Leistung entfernt.
Zunächst werde ich den Client beschreiben, weil er in beiden Fällen üblich ist.
client.py
%Vor%Multiprozessorserver:
foked_server.py
%Vor%Multithread-Server:
threaded_server.py
%Vor%Im ersten Fall werden mit forked_server.py nur 40 Prozesse erstellt, von denen etwa 20 eine Weile mit dem folgenden Fehler beginnen:
Fehler: [Errno 104] Verbindung durch Peer zurückgesetzt
auf einer Client-Seite.
Threaded-Version ist viel haltbarer und hält mehr als 4000 Verbindungen, aber zeigt schließlich
gaierror: [Errno -5] Keine Adresse mit Hostname assoziiert
Die Tests wurden auf meinem lokalen Rechner, Kubuntu 14.04 x64, auf Kernel v3.13.0-32 durchgeführt. Dies sind die Schritte, die ich gemacht habe, um die allgemeine Leistung des Systems zu erhöhen:
sysctl -w fs.file-max=10000000
sysctl -w net.core.netdev_max_backlog = 2500
sysctl -w net.core.somaxconn = 250000
Die Fragen lauten also:
socketserver
wird keine 10k-Verbindungen verarbeiten. Auf der aktuellen Hardware und den aktuellen Betriebssystemen wird kein Thread- oder Server mit Garbage-Funktion verwendet. Tausende von Threads bedeuten, dass Sie mehr Zeit mit der Kontextumstellung und -planung verbringen als tatsächlich arbeiten. Modernes Linux wird sehr gut beim Planen von Threads und Prozessen, und Windows ist ziemlich gut mit Threads (aber schrecklich mit Prozessen), aber es gibt eine Grenze dafür, was es tun kann.
Und socketserver
versucht nicht , hoch zu sein.
Und natürlich macht CPythons GIL alles noch schlimmer. Wenn Sie nicht 3.2+ verwenden; Jeder Thread, der selbst eine triviale Menge an CPU-gebundener Arbeit erledigt, wird alle anderen Threads ersticken und Ihre E / A blockieren. Mit der neuen GIL, wenn Sie nicht-triviale CPU vermeiden, fügen Sie too nicht viel zum Problem hinzu, aber es macht Kontext-Switches immer noch teurer als rohe Pthreads oder Windows-Threads.
Also, was tun wollen Sie?
Sie möchten einen single-threaded "Reaktor", der Ereignisse in einer Schleife bedient und Handler startet. (Unter Windows und Solaris bietet es Vorteile, stattdessen einen "Proactor" zu verwenden, einen Pool von Threads, die alle die gleiche Ereigniswarteschlange bedienen, aber da Sie Linux verwenden, sollten Sie sich keine Sorgen machen.) Moderne Betriebssysteme haben sehr gute Eigenschaften Multiplex-APIs zum Erstellen von% code_% auf BSD / Mac, kqueue
auf Linux, epoll
auf Solaris, IOCP auf Windows - das kann problemlos 10K-Verbindungen sogar auf Hardware von vor Jahren bewältigen.
/dev/poll
ist kein schrecklicher Reaktor, es bietet nur keine gute Möglichkeit, asynchrone Arbeit, nur Threads oder Prozesse zu versenden. Theoretisch könntest du ein socketserver
(mit dem Erweiterungsmodul GreenletMixIn
) oder ein greenlet
(vorausgesetzt, du hast oder weißt wie man ein Trampolin und einen Scheduler schreibt) ohne zu viel Arbeit auf CoroutineMixIn
aufbauen, und das ist vielleicht nicht zu schwer. Aber ich bin mir nicht sicher, welchen Nutzen Sie zu diesem Zeitpunkt von socketserver
haben.
Parallelism kann helfen, aber nur um langsame Jobs aus dem Hauptarbeits-Thread zu versenden. Holen Sie zuerst Ihre 10K-Verbindungen hoch und machen Sie dabei nur minimale Arbeit. Wenn die tatsächliche Arbeit, die Sie hinzufügen möchten, E / A-gebunden ist (z. B. Dateien lesen oder Anforderungen an andere Dienste senden), fügen Sie einen Pool von Threads hinzu, an den gesendet werden soll. Wenn Sie eine Menge CPU-gebundene Arbeit hinzufügen müssen, fügen Sie stattdessen einen Pool von Prozessen hinzu (oder in einigen Fällen sogar einen von jedem).
Wenn Sie Python 3.4 verwenden können, hat die stdlib eine Antwort in socketserver
(und da ist ein Backport auf PyPI für 3.3, aber es ist inhärent unmöglich, zu früheren Versionen zurück zu portieren).
Wenn nicht ... nun, Sie können selbst etwas auf asyncio
erstellen. in 3.4+, wenn Sie sich nicht für Windows interessieren, oder selectors
in 2.6 + wenn Sie nur an Linux, * BSD und Mac interessiert sind und bereit sind, zwei Versionen Ihres Codes zu schreiben, aber es wird eine Menge Arbeit sein. Oder Sie schreiben Ihre Kernereignisschleife in C (oder verwenden Sie einfach eine vorhandene wie select
oder libev
oder libuv
) und wickeln Sie sie in ein Erweiterungsmodul ein.
Aber Sie möchten sich wahrscheinlich an Bibliotheken von Drittanbietern wenden. Es gibt viele von ihnen, mit sehr unterschiedlichen APIs, von libevent
(die versucht, Ihren Code wie präventiv gethreadeten Code aussehen zu lassen, aber tatsächlich in Greenlets in einer single-threaded Ereignisschleife läuft) zu gevent
(was auf explizit basiert Callbacks und Futures, ähnlich wie viele moderne JavaScript-Frameworks).
StackOverflow ist kein guter Ort, um Empfehlungen für bestimmte Bibliotheken zu erhalten, aber ich kann Ihnen eine allgemeine Empfehlung geben: Schauen Sie sie an, wählen Sie diejenige, deren API am besten für Ihre Anwendung klingt, testen Sie, ob sie gut genug ist und nur fallen zurück zu einem anderen, wenn der, den du magst, es nicht schneiden kann (oder wenn du falsch liegst, weil du die API magst). Fans von einigen dieser Bibliotheken (besonders Twisted
und gevent
werden Ihnen sagen, dass ihr Favorit "Schnellster" ist), aber wen interessiert das? Wichtig ist, ob sie schnell genug und verwendbar sind um deine App zu schreiben.
Ganz oben auf meinem Kopf würde ich nach tornado
, gevent
, eventlet
, concurrence
, cogen
, twisted
, tornado
, monocle
und% co_de suchen %. Das ist wahrscheinlich keine großartige Liste, aber wenn Sie alle diese Begriffe zusammen googlen, wette ich, dass Sie einen aktuellen Vergleich oder ein passendes Forum finden werden.
Dieser Typ schien eine ziemlich gute Lösung zu haben mit threading
und subprocess
.
Erlaubt aufgrund von Windows XP-Einschränkungen maximal 250 Threads pro Prozess. In Anbetracht dessen, dass er im Vergleich zu heutigen Standards ziemlich schlechte Hardware hatte. Er konnte einen maximalen Thread von 15k erreichen, indem er dieses Skript als mehrere Prozesse wie hier gezeigt ausführte:
%Vor%Hoffe das hilft dir aus!
Tags und Links python multithreading sockets