Das Reaktormuster ist spezifischer als "ereignisgesteuerte Programmierung". Dies ist eine spezielle Implementierungstechnik, die bei der ereignisgesteuerten Programmierung verwendet wird. Der Begriff wird jedoch in typischen Konversationen nicht sehr genau verwendet. Sie sollten daher vorsichtig sein, wenn Sie ihn verwenden, und erwarten, dass Ihr Publikum Sie versteht, und Sie sollten vorsichtig sein, wie Sie den Begriff interpretieren, wenn Sie auf seine Verwendung stoßen.
Eine Möglichkeit, das Reaktormuster zu betrachten, besteht darin, es als eng mit der Idee von "nicht blockierenden" Operationen verbunden zu betrachten. Der Reaktor sendet Benachrichtigungen, wenn bestimmte Vorgänge ohne Blockierung abgeschlossen werden können. Zum Beispiel select(2)
kann für das Lesen von und Schreiben auf Steckdosen mit den Standard BSD Socket - APIs (die Reactor zu implementieren verwendet werden recv(2)
, send(2)
usw.). select
Hier erfahren Sie, wann Sie sofort Bytes von einem Socket empfangen können, da die Bytes beispielsweise im Kernel-Empfängerpuffer für diesen Socket vorhanden sind.
Ein weiteres Muster, das Sie beim Nachdenken über diese Ideen berücksichtigen sollten , ist das Proaktormuster . Im Gegensatz zum Reaktormuster werden bei dem Proaktormuster Operationen gestartet, unabhängig davon, ob sie sofort beendet werden können oder nicht, sie werden asynchron ausgeführt und anschließend eine Benachrichtigung über ihre Fertigstellung bereitgestellt .
Die IOCP-API (Windows I / O Completion Ports) ist ein Beispiel, in dem das Proaktormuster angezeigt wird. Wenn Sie einen Send auf einem Socket mit IOCP ausführen, wird der Sendevorgang gestartet, unabhängig davon, ob im Kernel-Sendepuffer Platz für diesen Socket vorhanden ist. Der Sendevorgang wird fortgesetzt (in einem anderen Thread, möglicherweise einem Thread im Kernel), während der WSASend
Aufruf sofort abgeschlossen wird. Wenn der Sendevorgang tatsächlich abgeschlossen ist (was bedeutet, dass nur die gesendeten Bytes in den Kernel-Sendepuffer für diesen Socket kopiert wurden), wird eine dem WSASend
Aufruf bereitgestellte Rückruffunktion aufgerufen (in einem neuen Thread in der Anwendung).
Dieser Ansatz, Operationen zu starten und dann benachrichtigt zu werden, wenn sie abgeschlossen sind, ist von zentraler Bedeutung für die Idee asynchroner Operationen. Vergleichen Sie es mit nicht blockierenden Vorgängen, bei denen Sie warten, bis ein Vorgang unmittelbar abgeschlossen werden kann, bevor Sie versuchen, ihn auszuführen.
Jeder Ansatz kann für die ereignisgesteuerte Programmierung verwendet werden. Unter Verwendung des Reaktormusters wartet ein Programm auf das Ereignis (zum Beispiel), dass ein Socket lesbar ist, und liest dann daraus. Unter Verwendung des Proactor wartet das Programm stattdessen für das Ereignis einer Fassung Abschluss lesen.
Genau genommen missbraucht Twisted den Begriff Reaktor . Der Twisted-Reaktor, der auf select(2)
( twisted.internet.selectreactor
) basiert, wird mit nicht blockierenden E / A implementiert, die sehr reaktorartig sind. Die Schnittstelle, die für Anwendungscode verfügbar gemacht wird , ist jedoch asynchron , wodurch sie proaktorähnlicher wird. Twisted hat auch einen Reaktor, der auf IOCP basiert. Dieser Reaktor stellt dieselbe asynchrone API für Anwendungen bereit und verwendet die proaktorähnlichen IOCP-APIs. Dieser hybride Ansatz, der sich in seinen Details von Plattform zu Plattform unterscheidet, macht weder den Begriff "Reaktor" noch "Proaktor" besonders genau, aber da die von exponierte API twisted.internet.reactor
im Grunde genommen vollständig asynchron statt nicht blockierend ist, ist Proaktor wäre wahrscheinlich eine bessere Wahl des Namens gewesen.
select
und empfangen haben , um das Reaktormuster zu erklären, sollten Sie sie verwenden, um das Proaktormuster zu erklären. Ansonsten ist es sehr verwirrend, da der Kontrast zu vage wird, weil sich im Szenario mehr als eine Sache ändert. Tatsächlich gibt es zwei verschiedene Szenarien. Sie sollten nur eine verwenden!Ich denke, dass diese Trennung "nicht blockierend" und "asynchron" falsch ist, da die Hauptimplikation von "asynchron" "nicht blockierend" ist. Im Reaktormuster geht es um asynchrone (also nicht blockierende) Anrufe, aber um synchrone (blockierende) Verarbeitung dieser Anrufe. Bei Proactor geht es um asynchrone (nicht blockierende) Anrufe und die asynchrone (nicht blockierende) Verarbeitung dieser Anrufe.
quelle
Für die Verarbeitung von TCP-Verbindungen gibt es zwei konkurrierende Webarchitekturen, nämlich eine threadbasierte Architektur und eine ereignisgesteuerte Architektur.
Thread-basierte Architektur
Die älteste Methode zum Implementieren eines Multithread-Servers ist der Ansatz "Thread pro Verbindung". Um die Anzahl der laufenden Threads zu steuern und zu begrenzen, kann ein einzelner Dispatcher-Thread zusammen mit einer begrenzten Blockierungswarteschlange und einem Thread-Pool verwendet werden.
Der Dispatcher blockiert einen TCP-Socket für neue Verbindungen und bietet sie der begrenzten Blockierungswarteschlange an. TCP-Verbindungen, die die Grenze der Warteschlange überschreiten, werden getrennt, sodass die akzeptierten Verbindungen mit einer wünschenswerten und vorhersehbaren Latenz arbeiten können.
Ereignisgesteuerte Architektur
Die ereignisgesteuerte Architektur trennt Threads von Verbindungen und ermöglicht nur die Verwendung von Threads für Ereignisse auf bestimmten Handlern.
Dieses kreative Konzept ermöglicht es Reactor Pattern, aus dem Regal zu kommen und sich zu präsentieren. Ein auf dieser Architektur basierendes System besteht aus Ereigniserstellern und Ereigniskonsumenten.
Das Reaktormuster
Das Reaktormuster ist die beliebteste Implementierungstechnik einer ereignisgesteuerten Architektur für die Handhabung von TCP-Verbindungen. In einfachen Worten, es verwendet eine Single-Threaded-Ereignisschleife, die Ereignisse blockiert und diese Ereignisse an entsprechende Handler sendet.
Es ist nicht erforderlich, dass andere Threads E / A blockieren, solange Handler für Ereignisse registriert sind, die sich um sie kümmern. In Anbetracht einer TCP-Verbindung können wir Ereignisse leicht auf diese Instanzen verweisen: verbunden, eingabebereit, ausgabebereit, Zeitüberschreitung und getrennt.
Das Reaktormuster entkoppelt den modularen Code auf Anwendungsebene von der Implementierung eines wiederverwendbaren Reaktors. Um dies zu erreichen, besteht die Architektur des Reaktormusters aus zwei wichtigen Teilnehmern - Reaktor und Handler.
Reaktor
Ein Reaktor wird in einem separaten Thread ausgeführt und reagiert auf E / A-Ereignisse wie verbunden, eingabebereit, ausgangsbereit, Zeitüberschreitung und getrennt, indem die Arbeit an den entsprechenden registrierten Handler gesendet wird.
Handler
Ein Handler führt die eigentliche Arbeit oder die Antwort aus, die mit einem E / A-Ereignis ausgeführt werden muss. Ein Reaktor reagiert auf E / A-Ereignisse, indem er den entsprechenden Handler sendet.
"Pattern Languages of Program Design" von Jim Coplien und Douglas C. Schmidt, das bereits 1995 veröffentlicht wurde, ist eines der Bücher, in denen das Reaktormuster ausführlich erläutert wurde.
quelle