Was ist der Unterschied zwischen ereignisgesteuertem Modell und Reaktormuster? [geschlossen]


72

Aus dem Wikipedia Reactor Pattern Artikel:

Das Reaktordesignmuster ist ein Ereignisbehandlungsmuster zum Behandeln von Serviceanforderungen, die gleichzeitig von einer oder mehreren Eingaben an einen Servicehandler übermittelt werden.

Es nannte einige Beispiele, zum Beispiel nodejs, twisted,eventmachine

Aber was ich oben verstehe, ist ein beliebtes ereignisgesteuertes Framework. Machen Sie sie also auch zu einem Reaktormuster-Framework?

Wie kann man zwischen diesen beiden unterscheiden? Oder sind sie gleich?

Antworten:


124

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.). selectHier 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 WSASendAufruf 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 WSASendAufruf 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.reactorim Grunde genommen vollständig asynchron statt nicht blockierend ist, ist Proaktor wäre wahrscheinlich eine bessere Wahl des Namens gewesen.


1
1. Kann ich folgendermaßen sagen: Das Reaktormuster ist ein Muster, das Ereignisse asynchron in die Warteschlange stellt, sie jedoch demultiplext und synchron versendet. während Proactor Warteschlangen strukturiert, demultiplext und alle asynchron versendet? 2. In Anbetracht der select (2) -API, welchen Nutzen haben die modernen Bibliotheken wie libevent, wenn sie mit Ereignissen programmieren (ich weiß, dass dies nicht mit dieser Frage zusammenhängt, aber Sie sind Experte auf diesem Gebiet.) Vielen Dank.
Howard

Zu 1: So wird es auf der Wikipedia-Seite ausgedrückt, daher zögere ich, einfach "Nein" zu sagen :) Als Praktizierender ist dies jedoch keine Formulierung, die mir von Menschen in der realen Welt begegnet ist. Da die Begriffe ziemlich dunkel sind und wenn sie verwendet werden, werden sie oft missbraucht. Ich denke, Sie könnten das sicher sagen, aber auch sicher etwas anderes sagen (wie Reaktor -> nicht blockierend, Proaktor -> asynchron). Vielleicht sind die beiden nicht einmal so weit voneinander entfernt (Sie könnten sich vorstellen, dass "synchron" bedeutet "synchron, nicht blockierend" - schließlich würde "synchron, blockierend" den weiteren Betrieb des Reaktors beeinträchtigen)
Jean-Paul Calderone

4
Zu 2: select (2) ist sehr niedrig und schwer zu bedienen. Es ist auch für eine große Anzahl von Ereignisquellen ineffizient. Moderne Plattformen sind darüber hinausgegangen und bieten gleichwertige, aber effizientere APIs (/ dev / poll, epoll, kqueue usw.). Ein Vorteil von libevent (oder Twisted) ist die Abstraktion all dieser verschiedenen Mechanismen, sodass Anwendungen in eine einheitliche API geschrieben werden können und gleichzeitig von der effizientesten API auf der Laufzeitplattform profitiert werden kann. Plus die offensichtlichen zusätzlichen Funktionen in Form von Protokollimplementierungen, APIs auf hoher Ebene für die Verwaltung der Asynchronität usw.
Jean-Paul Calderone

1
Einen Vergleich von select (2) mit epoll (4) finden Sie unter stackoverflow.com/questions/2032598/…
Jean-Paul Calderone

1
Wenn Sie Bytes von einem Socket genommen selectund 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!
Nawaz

6

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.


1

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.