„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Warum haben wir die reaktive Systemarchitektur aus unserem Code verworfen?

Warum haben wir die reaktive Systemarchitektur aus unserem Code verworfen?

Veröffentlicht am 02.09.2024
Durchsuche:774

In diesem Artikel geht es um unsere Entscheidung, in unserem Softwareprojekt von der reaktiven Architektur abzuweichen. Wir werden uns mit den Grundprinzipien reaktiver Systeme, den Vorteilen nicht blockierender E/A und den Herausforderungen befassen, denen wir bei einem reaktiven Ansatz gegenüberstanden.

Verständnis des reaktiven Architekturstils

Reactive umfasst eine Reihe von Prinzipien und Richtlinien zum Aufbau reaktionsfähiger verteilter Systeme und Anwendungen, gekennzeichnet durch:

  1. Reaktionsfähigkeit: Kann Anfragen auch unter hoher Belastung schnell bearbeiten.
  2. Resilienz: Kann sich nach Ausfällen mit minimaler Ausfallzeit erholen.
  3. Elastizität: Kann sich durch entsprechende Skalierung der Ressourcen an sich ändernde Arbeitslasten anpassen.
  4. Nachrichtengesteuert: Nutzt asynchrone Nachrichten, um die Fehlertoleranz zu verbessern und Komponenten zu entkoppeln.

Ein wesentlicher Vorteil reaktiver Systeme ist die Verwendung nicht blockierender E/A. Dieser Ansatz vermeidet das Blockieren von Threads während E/A-Vorgängen, sodass ein einzelner Thread mehrere Anforderungen gleichzeitig bearbeiten kann. Dies kann die Systemeffizienz im Vergleich zu herkömmlichen blockierenden E/A erheblich verbessern.
Beim herkömmlichen Multithreading stellen Blockierungsvorgänge erhebliche Herausforderungen bei der Optimierung von Systemen dar (Abbildung 1). Gierige Anwendungen, die übermäßig viel Arbeitsspeicher verbrauchen, sind ineffizient und benachteiligen andere Anwendungen, was oft die Anforderung zusätzlicher Ressourcen wie Arbeitsspeicher, CPU oder größerer virtueller Maschinen erforderlich macht.

Why we discarded Reactive systems architecture from our code?

Abbildung 1 – Traditionelles Multi-Threading


E/A-Operationen sind ein wesentlicher Bestandteil moderner Systeme, und ihre effiziente Verwaltung ist von größter Bedeutung, um gieriges Verhalten zu verhindern. Reaktive Systeme verwenden nicht blockierende E/A, sodass eine geringe Anzahl von Betriebssystem-Threads zahlreiche gleichzeitige E/A-Vorgänge verarbeiten kann.

Reaktives Ausführungsmodell

Obwohl nicht blockierendes I/O erhebliche Vorteile bietet, führt es ein neuartiges Ausführungsmodell ein, das sich von herkömmlichen Frameworks unterscheidet. Zur Behebung dieses Problems wurde eine reaktive Programmierung entwickelt, die die Ineffizienz von Plattform-Threads verringert, die während Blockierungsvorgängen im Leerlauf sind (Abbildung 2).

Why we discarded Reactive systems architecture from our code?

Abbildung 2 – Reaktive Ereignisschleife


Quarkus und Reaktiv

Quarkus nutzt eine reaktive Engine, die auf Eclipse Vert.x und Netty basiert und nicht blockierende I/O-Interaktionen ermöglicht. Mutiny, der bevorzugte Ansatz zum Schreiben von reaktivem Code mit Quarkus, übernimmt ein ereignisgesteuertes Paradigma, bei dem Reaktionen durch empfangene Ereignisse ausgelöst werden.

Mutiny bietet zwei ereignisgesteuerte und Lazy-Typen:

  1. Uni: Gibt ein einzelnes Ereignis (ein Element oder einen Fehler) aus, das zur Darstellung asynchroner Aktionen mit null oder einem Ergebnis geeignet ist.
  2. Multi: Gibt mehrere Ereignisse aus (n Elemente, ein Fehler oder ein Abschluss), die möglicherweise unbegrenzte Elementströme darstellen.

Herausforderungen mit Reactive

Während reaktive Systeme Vorteile bieten, sind wir während der Entwicklung auf mehrere Herausforderungen gestoßen:

  • Paradigmenwechsel: Reaktive Programmierung erfordert einen grundlegenden Wandel in der Denkweise der Entwickler, der eine Herausforderung darstellen kann, insbesondere für Entwickler, die an imperative Programmierung gewöhnt sind. Im Gegensatz zu Hilfstools wie der Streams-API erfordert der reaktive Ansatz eine vollständige Überarbeitung der Denkweise.
  • Lesbarkeit und Verständnis des Codes: Reaktiver Code bereitet neuen Entwicklern Schwierigkeiten, ihn zu verstehen, was dazu führt, dass mehr Zeit für die Entschlüsselung und das Verständnis aufgewendet wird. Die durch reaktive Paradigmen eingeführte Komplexität verschärft dieses Problem.

„Tatsächlich liegt das Verhältnis der Zeit, die mit Lesen und Schreiben verbracht wird, weit über 10 zu 1. Wir lesen ständig alten Code, um neuen Code zu schreiben es einfacher zu schreiben.“
Robert C. Martin, Clean Code: Ein Handbuch für agile Software-Handwerkskunst

  • Herausforderungen beim Debuggen: Das Debuggen von reaktivem Code erweist sich mit Standard-IDE-Debuggern als nahezu unmöglich, da Lambdas den größten Teil des Codes kapseln. Darüber hinaus erschwert der Verlust aussagekräftiger Stack-Traces bei Ausnahmen die Debugging-Bemühungen zusätzlich. Erhöhter Entwicklungs- und Testaufwand: Die inhärente Komplexität von reaktivem Code kann aufgrund des Zeitaufwands für das Schreiben, Ändern und Testen zu längeren Entwicklungszyklen führen.

Hier ist ein Beispiel für reaktiven Code, der Mutiny verwendet, um die Komplexität zu veranschaulichen:

Multi.createFrom().ticks().every(Duration.ofSeconds(15))
    .onItem().invoke(() - > Multi.createFrom().iterable(configs())
    .onItem().transform(configuration - > {
  try {
    return Tuple2.of(openAPIConfiguration,
        RestClientBuilder.newBuilder()
            .baseUrl(new URL(configuration.url()))
            .build(MyReactiveRestClient.class)
            .getAPIResponse());

  } catch (MalformedURLException e) {
    log.error("Unable to create url");

  }
  return null;
}).collect().asList().toMulti().onItem().transformToMultiAndConcatenate(tuples - > {

  AtomicInteger callbackCount = new AtomicInteger();
  return Multi.createFrom().emitter(emitter - > Multi.createFrom().iterable(tuples)
      .subscribe().with(tuple - >
          tuple.getItem2().subscribe().with(response - > {
              emitter.emit(callbackCount.incrementAndGet());

  if (callbackCount.get() == tuples.size()) {
    emitter.complete();
  }
                    })
                ));

}).subscribe().with(s - > {},
Throwable::printStackTrace, () - > doSomethingUponComplete()))
    .subscribe().with(aLong - > log.info("Tic Tac with iteration: "   aLong));

Zukunftsausblick – Projekt Loom und darüber hinaus

Project Loom, eine aktuelle Entwicklung im Java-Ökosystem, verspricht, die mit Blockierungsvorgängen verbundenen Probleme zu mildern. Durch die Möglichkeit der Erstellung Tausender virtueller Threads ohne Hardwareänderungen könnte Project Loom in vielen Fällen möglicherweise die Notwendigkeit eines reaktiven Ansatzes überflüssig machen.

"Project Loom wird Reactive Programming töten"
Brian Goetz

Abschluss

Zusammenfassend lässt sich sagen, dass unsere Entscheidung, vom reaktiven Architekturstil abzuweichen, ein pragmatischer Ansatz für die langfristige Wartbarkeit unseres Projekts ist. Während reaktive Systeme potenzielle Vorteile bieten, überwogen die Herausforderungen, die sie für unser Team darstellten, diese Vorteile in unserem spezifischen Kontext.

Wichtig ist, dass diese Verschiebung die Leistung nicht beeinträchtigte. Dies ist ein positives Ergebnis, da es zeigt, dass eine gut konzipierte nicht reaktive (imperative) Architektur die erforderliche Leistung liefern kann, ohne die Komplexität, die in unserem Fall mit einer reaktiven Architektur verbunden ist.

Wenn wir in die Zukunft blicken, liegt der Fokus weiterhin auf dem Aufbau einer Codebasis, die nicht nur funktional, sondern auch für Entwickler aller Erfahrungsstufen leicht zu verstehen und zu warten ist. Dies verkürzt nicht nur die Entwicklungszeit, sondern fördert auch eine bessere Zusammenarbeit und den Wissensaustausch innerhalb des Teams.

In der folgenden Grafik stellt die X-Achse die zunehmende Komplexität unserer Codebasis im Laufe ihrer Entwicklung dar, während die Y-Achse die für diese Entwicklungsänderungen erforderliche Zeit darstellt.

Why we discarded Reactive systems architecture from our code?

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/yanev/why-we-discarded-reactive-systems-architecture-from-our-code-19ni?1 Bei Verstößen wenden Sie sich bitte an [email protected] um es zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3