2009-03-11 10 views
9

Ich habe kürzlich gelesen this document, die eine Reihe von Strategien auflistet, die verwendet werden könnten, um einen Socket-Server zu implementieren. Das heißt, sie sind:Schreiben eines Socket-basierten Servers in Python, empfohlene Strategien?

  1. viele Kunden mit jedem Thread dienen und nicht blockierend verwenden I/O und pegelgetriggert Bereitschaft Benachrichtigung
  2. Serve viele Kunden mit jedem Thread, und verwenden Sie nicht blockierende E/A und die Bereitschaft Änderungsbenachrichtigung
  3. dienen vielen Clients mit jedem Server-Thread, und verwenden asynchrone I/O
  4. einen Client mit jedem Server-Thread dienen, und die Blockierung/I O des Server-Code in den Kernel
Baue
  • verwenden

    Jetzt würde ich mich über einen Hinweis freuen, der verwendet werden sollte in CPython, die wir wissen, hat einige gute Punkte und einige schlechte Punkte. Ich bin vor allem an Performance unter hoher Parallelität interessiert, und ja, einige der aktuellen Implementierungen sind zu langsam.

    Also wenn ich mit dem einfachen beginnen kann, ist "5" out, da ich nichts in den Kernel hacken werde.

    "4" Sieht auch wie es muss wegen der GIL aus sein. Natürlich könnte man hier Multiprocessing anstelle von Threads verwenden, und das gibt einen deutlichen Schub. Das Blockieren von IO hat auch den Vorteil, dass es leichter zu verstehen ist.

    Und hier mein Wissen schwindet ein wenig:

    „1“ ist traditionell wählen oder Umfrage, die trivialen mit Multiprozessing kombiniert werden könnte.

    „2“ ist die Bereitschaft-Änderungsmitteilung, durch die neuere epoll verwendet und kqueue

    „3“ Ich bin nicht sicher, dass alle Kernel-Implementierungen für diese gibt, die Python-Wrapper haben.

    Also, in Python haben wir eine Tasche mit tollen Tools wie Twisted. Vielleicht sind sie ein besserer Ansatz, obwohl ich Twisted getestet habe und es auf einem Multiprozessor-Rechner zu langsam fand. Vielleicht weiß ich es nicht, wenn ich 4 Twists mit einem Load Balancer habe. Jeder Rat würde geschätzt werden.

  • Antwort

    7

    asyncore ist im Grunde "1" - Es intern verwendet select, und Sie haben nur ein Thread alle Anfragen behandeln. Laut den Dokumenten kann es auch poll verwenden. (EDIT: Entfernte Twisted-Referenz, ich dachte, es verwendet Asyncore, aber ich habe mich geirrt).

    "2" könnte mit python-epoll implementiert werden (Just googled es - noch nie zuvor gesehen). EDIT: (aus den Kommentaren) In Python 2.6 die select module hat epoll, kqueue und kevent build-in (auf unterstützten Plattformen). Sie benötigen also keine externen Bibliotheken, um flankengetriggert zu arbeiten.

    Sie ausschließen „4“ nicht, wie die GIL wird fallen gelassen werden, wenn ein Thread tatsächlich für IO-Operationen zu tun oder warten (die meiste Zeit wahrscheinlich). Es macht keinen Sinn, wenn Sie natürlich eine große Anzahl von Verbindungen haben. Wenn Sie viel zu verarbeiten haben, macht Python mit keinem dieser Schemata Sinn.

    Für Flexibilität suchen vielleicht bei Twisted?

    In der Praxis läuft Ihr Problem darauf hinaus, wie viel Verarbeitung Sie für Anfragen ausführen werden. Wenn Sie viel arbeiten und den parallelen Multi-Core-Betrieb nutzen müssen, benötigen Sie wahrscheinlich mehrere Prozesse. Auf der anderen Seite, wenn Sie nur auf eine Menge Verbindungen hören müssen, dann wählen Sie oder epoll, mit einer kleinen Anzahl von Threads sollte funktionieren.

    +0

    Ich denke, dass epoll ist in der stdlib in 2.6+ und easy_installable für 2.5. Das Paket heißt Select-Something. Entschuldigung für Unklarheit. –

    +0

    Twisted kann auch Epoll verwenden. Tatsächlich verwandelt Twisted alle unterstützten Ereignisbenachrichtigungs-APIs in eine einheitliche API, die es Ihnen präsentiert. Also, wenn das Beste, was die Plattform tun kann, wählen Sie, wählt Ihre App aus. Wenn es epoll hat, verwendet Ihre App epoll. Alles transparent für dich. –

    +0

    Es ist buchstabiert "asyncore" – new123456

    1

    http://docs.python.org/library/socketserver.html#asynchronous-mixins

    Wie für Multi-Prozessor (Multi-Core) Maschinen. Mit CPython aufgrund GIL benötigen Sie mindestens einen Prozess pro Kern, um zu skalieren. Wie Sie sagen, dass Sie CPython benötigen, können Sie versuchen, das mit ForkingMixIn zu benchmarken. Mit Linux 2.6 könnte man einige interessante Ergebnisse erzielen.

    Andere Möglichkeit ist, Stackless Python zu verwenden. Das ist how EVE solved it. Aber ich verstehe, dass es nicht immer möglich ist.

    +0

    Danke, aber haben Sie diese Dinge gebenchmarkt? Sie sind langsam. Ich würde das Rad nicht erfinden, wenn ich es nicht müsste. –

    +0

    +1 stackless/EVE, aber ich habe gesagt CPython –

    1

    Ich mag Douglas' Antwort, aber so nebenbei ...

    Sie einen zentralen Dispatch Thread/Prozess verwenden könnten, die für die Bereitschaft Benachrichtigungen mit select und Delegierten auf einen Pool von Arbeitsthreads horcht/processes zu erreichen helfen Ihre Parallelitätsziele.

    Wie Douglas erwähnt, wird die GIL jedoch während der meisten langen I/O-Operationen nicht gehalten (da keine Python-API-Dinge passieren). Wenn Sie also Reaktionslatenz haben, können Sie versuchen, die kritische Teile Ihres Codes für CPython API.

    2

    Kann ich weitere Links vorschlagen?

    cogen ist eine crossplatform-Bibliothek für netzwerkorientierte, Coroutine-basierte Programmierung mit den erweiterten Generatoren von Python 2.5. Auf der Hauptseite von Cogen Project gibt es Links zu mehreren Projekten mit ähnlichem Zweck.

    3

    Wie wäre es mit "Gabel"? (Ich gehe davon aus, dass ForkingMixIn dies tut) Wenn die Anforderungen in einer "shared nothing" (anders als DB oder Dateisystem) Architektur behandelt werden, startet fork() ziemlich schnell bei den meisten * nixen, und Sie müssen sich keine Sorgen machen über all die dummen Bugs und Komplikationen beim Threading.

    Themen sind eine Design-Krankheit, die wir von OSes mit allzu schwergewichtigen Prozessen gezwungen, IMHO. Das Klonen einer Seitentabelle mit Copy-on-Write-Attributen scheint ein kleiner Preis, besonders wenn Sie ohnehin einen Interpreter ausführen.

    Leider kann ich nicht genauer sein, aber ich bin eher ein Perl-Übergang-zu-Ruby-Programmierer (wenn ich nicht über Massen von Java bei der Arbeit schuftend)


    -Update : Ich habe in meiner "Freizeit" endlich mal Timings auf Thread vs Fork gemacht. Check it out:

    http://roboprogs.com/devel/2009.04.html

    Expanded: http://roboprogs.com/devel/2009.12.html

    +0

    Darüber hinaus können Sie den Code für andere Module, die Sie verwenden, bevor Sie die untergeordneten Prozesse starten. Dies verhindert, dass sie immer wieder mit Token (JIT-ed, was auch immer) versehen werden. Zweitens, halten Sie Daten im Elternteil klein, verwenden Sie "Exit" als Super Garbage Collector. – Roboprog

    3

    One sollution GEVENT. Gevent Maries eine Libevent basierte Ereignisabfrage mit leichten kooperativen Task-Switching von Greenlet implementiert.

    Was man bekommt, ist die Leistung und Skalierbarkeit eines Event-System mit der Eleganz und einfachem Modell IO-Programmierung zu blockieren.

    (ich weiß nicht, was die SO-Konvention über die Beantwortung alte Fragen wirklich ist, aber entschied ich mich noch meine 2 Cent hinzufügen würde)