Connect with us

Entwicklung & Code

Clean Architecture und Co.: Softwarearchitektur mit Mustern strukturieren


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Strukturierte Software basiert auf einem Plan, der die spezifischen Anforderungen an ein System berücksichtigt und in lose gekoppelte Bausteine überführt. In der arbeitsteiligen Softwareentwicklung benötigen Entwicklungsteams solche gemeinsamen Pläne, um eine harmonische und einheitliche Architektur zu entwickeln, ohne jedes Detail vorab miteinander abstimmen zu müssen. Bewähren sich die Pläne, entwickeln sich daraus Muster und Prinzipien auf unterschiedlichen Architekturebenen.

Weiterlesen nach der Anzeige


Matthias Eschhole

Matthias Eschhole

Matthias Eschhold ist Lead-Architekt der E-Mobilität bei der EnBW AG. Als Experte für Domain-driven Design gestaltet er die IT-Landschaft und Team-Topologien der E-Mobilität. Trotz strategischer Schwerpunkte bleibt er mit Java und Spring Boot nah am Code, entwickelt Prototypen und führt Refactorings durch. Als Trainer vermittelt er seit Jahren praxisnahe Softwarearchitektur, die Theorie und Projektrealität verbindet.

Bei der grundlegenden Strukturierung eines Systems muss man zwischen Architekturstilen und Architekturmustern unterscheiden, wobei sie sich nicht immer sauber abgrenzen. Ein Architekturstil ist ein Mittel, das dem System eine grundlegende Struktur verleiht. Beim Stil Event-driven Architecture basiert die Anwendung beispielsweise auf asynchroner Kommunikation, und Events beeinflussen die Architektur und den Code an vielen Stellen. Gleiches gilt für REST, das eine ressourcenorientierte Struktur vorgibt.

Entscheidet sich ein Entwicklungsteam für Microservices als Architekturstil, wählt es eine verteilte Systemarchitektur, beim Stil Modularer Monolith ist das Gegenteil der Fall. In komplexen Systemen kombinieren Architektinnen und Architekten in der Regel mehrere Stile. Manche Architekturstile ergänzen sich, etwa REST und Microservices, während sich andere gegenseitig ausschließen, wie Microservices und der Modulare Monolith.

Ob Microservices oder Modularer Monolith – beides sagt wenig über die Gestaltung der internen Strukturen aus. Auf dieser inneren Architekturebene, der Anwendungsarchitektur, kommen Muster zum Einsatz, die Entwurfsprinzipien und -regeln kombinieren und eine Basisstruktur der Anwendung prägen. Architekturmuster der Anwendungsarchitektur nutzen Verantwortungsbereiche und Beziehungsregeln als Strukturierungsmittel. Im Muster Clean Architecture sind dies beispielsweise konzentrische Ringe, wobei die Beziehungsrichtung stets zum inneren Kern des Ringmodells führt. Die geschichtete Architektur (Layered Architecture) hingegen unterteilt die Verantwortungsbereiche in hierarchische Schichten, wobei jede Schicht nur mit der darunter liegenden kommunizieren darf (siehe Abbildung 1).


Infografik Vergleich zwischen Clean Architecture und Schichtenarchitektur

Infografik Vergleich zwischen Clean Architecture und Schichtenarchitektur

Vergleich zwischen Clean Architecture und Schichtenarchitektur (Abb. 1).

Eine Mustersprache ergänzt Architekturmuster für einen ganzheitlichen Konstruktionsplan – von Modulen und Paketen bis hin zum Klassendesign. Sie bildet das Fundament für eine konsistente und verständliche Umsetzung der Muster und beschreibt eine Reihe von Entwurfsmustern für die Programmierung auf der Klassenebene.

Weiterlesen nach der Anzeige

Die Klassen der Mustersprache bilden Geschäftsobjekte, Fachlogik und technische Komponenten ab. Sie werden unter Einhaltung der definierten Beziehungsregeln in einem Klassenverbund implementiert. Diese Regeln bestimmen, wie die Klassen miteinander interagieren, wie sie voneinander abhängen und welche Aufgaben sie haben. Ein Geschäftsobjekt ist charakterisiert durch seine Eigenschaften und sein Verhalten, während ein Service Geschäftslogik und fachliche Ablaufsteuerung implementiert. Eine derartige, genaue Differenzierung gestaltet Architektur klar und nachvollziehbar.

Ein wichtiger Aspekt einer Mustersprache ist die Organisation des Codes in einer gut verständlichen Hierarchie. Dadurch fördert sie die Verteilung von Verantwortlichkeiten auf unterschiedliche Klassen. Prinzipiell kann jedes Projekt seine eigene Mustersprache definieren oder eine bestehende als Basis verwenden und mit individuellen Anforderungen ausbauen. Eine Mustersprache sorgt auch im Team dafür, dass alle Mitglieder dieselben Begriffe und Prinzipien verwenden.

Dieser Artikel wählt die DDD Building Blocks als Grundlage für eine Mustersprache, wie die folgende Tabelle und Abbildung 2 zeigen.

Value Object Ein Value Object repräsentiert einen unveränderlichen Fachwert ohne eigene Entität. Das Value Object ist verantwortlich für die Validierung des fachlichen Werts und sollte nur in einem validen Zustand erzeugt werden können. Ferner implementiert ein Value Object dazugehörige Fachlogik.
Entity Eine Entity ist ein Objekt mit einer eindeutigen Identität und einem Lebenszyklus. Die Entität wird beschrieben durch Value Objects und ist verantwortlich für die Validierung fachwertübergreifender Geschäftsregeln sowie die Implementierung dazugehöriger Fachlogik.
Aggregate Ein Aggregate ist eine Sammlung von Entitäten und Value Objects, die durch eine Root Entity (oder Aggregate Root bzw. vereinfacht Aggregate) zusammengehalten werden. Die Root Entity definiert eine fachliche Konsistenzgrenze, klar abgegrenzt zu anderen Root Entities (oder Aggregates).
Domain Service Ein Domain Service implementiert Geschäftslogik, die nicht zu einer Entität oder einem Value Object gehört. Weiter steuert der Domain Service den Ablauf eines Anwendungsfalls. Ein Domain Service ist zustandslos zu implementieren.
Factory Eine Factory ist für die Erstellung von Aggregates, Entitäten oder Value Objects verantwortlich. Die Factory kapselt die Erstellungslogik komplexer Domänenobjekte.
Repository Ein Repository ist verantwortlich für die Speicherung und das Abrufen von Aggregaten und Entitäten aus einer Datenquelle. Das Repository kapselt den Zugriff auf eine Datenbank oder auch andere technische Komponenten.


Infografik Mustersprache des taktischen Domain-driven Design

Infografik Mustersprache des taktischen Domain-driven Design

Mustersprache des taktischen Domain-driven Design (Abb. 2).

Ein Beispiel verdeutlicht den Unterschied zwischen einem Value Object und einer Entity: Eine Entity könnte ein bestimmtes Elektrofahrzeug sein. Entities sind also eindeutig und unverwechselbar. In der realen Welt zeigt sich das an der global eindeutigen Fahrgestellnummer (VIN). Der aktuelle Zustand eines E-Fahrzeugs wird zu einem bestimmten Zeitpunkt beispielsweise durch seinen Ladezustand beschrieben, ein Wert, der sich im Laufe der Nutzung des Fahrzeugs verändert. Der Ladezustand entspricht einem Value Object. Er verfügt über keine eigene Identität, sondern definiert sich ausschließlich durch seinen Wert.

Die Mustersprache der Building Blocks ist nicht vollständig. Sie benötigt weitere Elemente, die von den eingesetzten Architekturstilen und -mustern abhängen. REST als Architekturstil führt beispielsweise zwei Elemente in die Mustersprache ein: Controller und Resource. Bei der Integration von REST als Provider liegt der Fokus auf der Resource, die als Datentransferobjekt (DTO) über den API-Endpunkt bereitsteht. Der Controller fungiert als Schnittstelle zwischen der Anfrage des Konsumenten und der Fachlogik des Systems. Das heißt, der Controller nutzt den bereits eingeführten Domain Service und delegiert die Ausführung von Fachlogik an diesen.

Bei der Integration von REST als Consumer erhält die Mustersprache das Element Service Client, das dem Abrufen von Daten oder Ausführen von Funktionen über einen externen API-Endpunkt dient. Der Domain Service triggert dies als Teil der Fachlogik über den Service Client.

Der Stil Event-driven Architecture erweitert die Mustersprache um die Elemente Event Listener, Event Publisher und das Event selbst. Ein Event Listener hört auf Ereignisse und ruft den entsprechenden Domain Service auf, um die Ausführung der Geschäftslogik auszulösen. Der Event Publisher veröffentlicht eine Zustandsveränderung in der Fachlichkeit über ein Event. Der Domain Service triggert die Event-Veröffentlichung als Teil seiner Fachlogik und nutzt hierfür den Event Publisher.

Die in diesen Beispielen aufgeführten Begriffe sind im Vergleich zu den DDD Building Blocks nicht in der Literatur definiert und entstammen der Praxis. Abbildung 3 zeigt die Klassen der erweiterten Mustersprache.


Infografik Elemente der Mustersprache des taktischen Domain-driven Design

Infografik Elemente der Mustersprache des taktischen Domain-driven Design

Elemente der Mustersprache des taktischen Domain-driven Design (Abb. 3).

Architekturmuster kombinieren Regeln, Entwurfsmuster und Prinzipien. Muster wie Clean Architecture, die sich besonders für komplexe Systeme mit hohen Anforderungen an den Lebenszyklus eignen, bündeln mehrere Konzepte und beeinflussen daher die Mustersprache stärker als andere Muster. Ein Beispiel ist das Konzept Use Case in der Clean Architecture, das ein zentrales Element darstellt und die Mustersprache um die Elemente Use Case Input Port, Use Case Output Port und Use Case Interactor erweitert. Ein weiteres Beispiel ist die Anwendung des Dependency Inversion Principle (DIP) in der Clean Architecture, das zu dem Musterelement Mapper führt.

Nach dem Exkurs über die Mustersprachen stellt dieser Artikel verschiedene Architekturmuster vor, die sich in schichten- und domänenbasierende unterteilen.

Schichtenbasierende Architekturmuster sind datenzentrisch strukturiert. Je nach Muster ist dieser Aspekt mehr oder weniger ausgeprägt. Die Schichtung unterscheidet sich in technischer (horizontal geschnitten) und fachlicher (vertikal geschnitten) Hinsicht. Für die weitere Beschreibung eignet sich die Begriffswelt von Simon Brown mit „Package by …“ .

Package by Layer: Dieses Muster organisiert die Anwendung nach technischen Aspekten, zum Beispiel nach Controller, Service und Repository (Abbildung 4). Es kommt jedoch schnell an seine Grenzen: Mittlere und große Systeme mit komplizierter Fachlichkeit erfordern eine vertikale Schichtung anhand fachlicher Aspekte, andernfalls enden die Projekte erfahrungsgemäß in komplizierten Monolithen mit vielen Architekturverletzungen.

Vorteile:

  • Bekannt und verbreitet
  • Einfach zu verstehen und anzuwenden
  • In kleinen Projekten praktikabel

Nachteile:

  • Enge Kopplung zwischen Schichten, mit der Gefahr chaotischer Abhängigkeiten bei Wachstum des Systems
  • Fachlich zusammenhängende Funktionalitäten sind über viele Pakete verteilt
  • Schwer wartbar und erweiterbar bei mittleren bis großen Anwendungen


Infografik Das Architekturmuster Package by Layer

Infografik Das Architekturmuster Package by Layer

Das Architekturmuster Package by Layer (Abb. 4).

Package by Feature: Der Code organisiert sich vertikal anhand fachlicher Aspekte. Eine Schnitt-Heuristik, wie genau das Feature von den fachlichen Anforderungen abzuleiten ist, definiert das Architekturmuster nicht. Es definiert nur, dass dieser fachliche Schnitt zu erfolgen hat. Wird das taktische DDD angewendet, erfolgt der Schnitt entlang der Aggregates (siehe Abbildung 5).

Vorteile:

  • Fachlich kohäsiver Code ist lokal zusammengefasst, was zu hoher Wartbarkeit und Erweiterbarkeit führt.
  • Modularisierung ermöglicht die unabhängige Entwicklung fachlicher Module.
  • Fachliche Ende-zu-Ende-Komponenten sind lose gekoppelt.
  • Abhängigkeiten zwischen fachlichen Modulen müssen explizit gehandhabt werden, was die Robustheit der Architektur gegenüber ungewünschten Abhängigkeiten erhöht.
  • Fachlich komplexe, mittelgroße bis große Anwendungen lassen sich mit vertikalen Schichten besser beherrschen als mit Package by Layer und Package by Component.

Nachteile:

  • Abhängigkeiten zwischen fachlichen Modulen erfordern fortgeschrittene Kommunikationsmuster (zum Beispiel Events), was die architektonische Komplexität erhöht.
  • Vertikale Modularisierung muss gut durchdacht werden, um enge Kopplung zwischen Modulen zu vermeiden.


Infografik Architekturmuster Package by Feature

Infografik Architekturmuster Package by Feature

Das Architekturmuster Package by Feature (Abb. 5).

Package by Component: Das Muster strukturiert die Anwendung sowohl fachlich (vertikal) als auch technisch (horizontal), wobei sich ein fachliches Feature in eine Inbound-Komponente und eine Domain-Komponente aufteilt (siehe Abbildung 6). Die Domain-Komponente kapselt Geschäftslogik und die dazugehörige Persistenzschicht. Diese Unterteilung in fachliche Module ist ein entscheidender Unterschied zu Package by Layer.

Vorteile:

  • Gute Modularisierung durch fachliche Grenzen zwischen Komponenten
  • Hohe Wiederverwendbarkeit der Domain-Komponenten, durch unterschiedliche Inbound-Komponenten
  • Erleichterte Testbarkeit durch gesteigerte Modularisierung im Vergleich zu Package by Layer

Nachteile:

  • Enge Kopplung zwischen Inbound- und Domain-Schicht, mit dem Risiko indirekter Abhängigkeiten und Seiteneffekten bei Änderungen, insbesondere wenn die Anwendung wächst
  • Komponentenkommunikation schwer beherrschbar bei erhöhter fachlicher Komplexität
  • Schwerer erweiterbar für mittlere bis große Anwendungen mit höherer fachlicher Komplexität


Infografik Architekturmuster in Package by Component (Abb. 6).

Infografik Architekturmuster in Package by Component (Abb. 6).

Das Architekturmuster in Package by Component (Abb. 6).



Source link

Entwicklung & Code

VS Code deaktiviert IntelliCode zugunsten des kostenpflichtigen Copilot


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

Im Zuge der Veröffentlichung von VS Code 1.107 wurde bekannt, dass Microsoft die beliebte Erweiterung IntelliCode, die über 60 Millionen Downloads verzeichnete, deaktiviert hat: Die Extension ist nun veraltet (deprecated) und die grauen Inline-Vorschläge funktionieren nicht mehr.

Weiterlesen nach der Anzeige

Microsoft verweist in der gut versteckten Ankündigung von Mitte November auf die KI-Erweiterung von Copilot in VS Code, die allerdings nur ein Freivolumen von 2.000 Vorschlägen bietet – eine Grenze, die Entwicklerinnen und Entwickler schnell erreichen, da der Copilot bei jeder Eingabe einen Vorschlag macht. Ab dann benötigen Anwender eine kostenpflichtige Lizenz. Die Nutzung von IntelliCode erforderte ein lokales Modell, war dadurch aber unbegrenzt kostenfrei.

Noch kostenlos gibt es das klassische IntelliSense mit Language Server für die genutzte Sprache – allerdings ohne KI-Unterstützung. Von der Abschaltung betroffen sind die Extensions:

  • IntelliCode
  • IntelliCode Completions
  • IntelliCode for C# Dev Kit
  • IntelliCode API Usage Examples

In der Ankündigung zu VS Code 1.107 ist nichts von IntelliCode zu finden. Neu ist aber die experimentelle Unterstützung von TypeScript 7 mit dem neuen, in Go geschriebenen Compiler. Dieser lässt sich updaten mit:

npm install @typescript/native-preview

Weiterlesen nach der Anzeige

Der Aufruf erfolgt mit

npx tsgo

statt tsc. Die Konfiguration in VS Code erfolgt mit

{ "typescript.experimental.useTsgo": true }

Weitere Neuerungen im Editor betreffen Agenten, die sich nun über den Chat steuern lassen. Sie laufen auch weiter, wenn der Anwender den Chat geschlossen hat. Entwicklerinnen und Entwickler können Agenten ferner in andere Umgebungen verschieben, mit Kontext anreichern oder als Unteragenten einordnen. Der Blog spricht militärisch von einem Agent Head Quarter (HQ).

Lesen Sie auch


(who)



Source link

Weiterlesen

Entwicklung & Code

Rust Coreutils 0.5.0 erreicht 88 Prozent GNU-Kompatibilität


Das Projekt uutils hat Version 0.5.0 seiner Rust Coreutils veröffentlicht. Die in Rust geschriebene Neuimplementierung klassischer Unix-Kommandozeilenprogramme erreicht damit 87,75 Prozent Kompatibilität zur GNU-Test-Suite – ein Anstieg um knapp zwei Prozentpunkte gegenüber Version 0.4.0. Von insgesamt 645 Tests bestehen die Rust Coreutils nun 566, während 55 fehlschlagen, 23 übersprungen werden und einer zu einem Fehler führt.

Weiterlesen nach der Anzeige

Die Entwickler haben die Referenz-Test-Suite von GNU Coreutils 9.8 auf 9.9 aktualisiert, wodurch elf neue Tests hinzukamen. Trotz dieser zusätzlichen Prüfungen konnten 22 weitere Tests erfolgreich absolviert werden. Besonders hervorzuheben sind Verbesserungen bei den Utilities fold, cksum, install, numfmt und seq. Das Tool fold unterstützt nun kombinierende Unicode-Zeichen für korrekte Textumbrüche, während cksum mit hashsum zusammengeführt wurde und nun eine einheitliche Checksum-Funktion bietet.

Mit Version 0.5.0 erweitert das Projekt seinen Plattform-Support erheblich. OpenBSD wurde in die CI-Pipeline aufgenommen, die Redox-OS-Unterstützung reaktiviert und der Cygwin-Support in der uucore-Bibliothek verbessert. Als Folge hiervon können nun zehn zuvor übersprungene Tests ausgeführt werden. Die Rust Coreutils laufen damit offiziell auf Linux-Distributionen wie Ubuntu 25.10, FreeBSD, OpenBSD, Windows via Cygwin und dem experimentellen Betriebssystem Redox.

Canonical hatte bereits angekündigt, die Rust Coreutils in Ubuntu standardmäßig einzusetzen – primär aufgrund der Vorteile von Rust in puncto Speichersicherheit. Die Version 0.3.0 hatte bereits gezeigt, dass das sort-Tool in CPU-lastigen Szenarien bis zu 3,7-mal schneller arbeitet als sein GNU-Pendant – andere Tools wie expand (1,8×) oder nl (1,57×) zeigen ebenfalls deutliche Geschwindigkeitsgewinne als die GNU-Pendants. Bei IO-gebundenen Operationen fallen die Unterschiede geringer aus.

Trotz der Fortschritte gibt es weiterhin Herausforderungen. Von den 55 fehlgeschlagenen Tests betreffen einige kritische Edge-Cases bei Tools wie cksum (crc32b mit --raw-Flag), od (Floating-Point-Operationen) und chroot. Auf GitHub verzeichnet das Projekt rund 380 offene Issues, die sich mit verbliebenen Inkompatibilitäten befassen. Für Administratoren, die einen produktiven Einsatz erwägen, empfehlen sich umfangreiche Tests der eigenen Skripte – insbesondere solche mit GNU-spezifischen Flags oder ungewöhnlichen Optionskombinationen.

Weiterlesen nach der Anzeige

Die Sicherheitsvorteile von Rust kommen bei den Coreutils zum Tragen: Speicherfehler wie Buffer-Overflows und unsichere Path-Traversal-Operationen gehören der Vergangenheit an. Tools wie chmod nutzen bereits sichere Traversierungsmethoden. Allerdings warnen Kritiker vor neuen Fehlerklassen, die durch Rust-spezifische Ownership-Semantik entstehen könnten. Im Gegensatz zu den jahrzehntelang gehärteten GNU Coreutils ist die Rust-Variante noch vergleichsweise jung.

An Version 0.5.0 haben sechs neue Contributor mitgewirkt. Das Projekt ruft zu Übersetzungen via Weblate auf und bittet um Unterstützung über GitHub Sponsors. Die Maintainer-Basis umfasst etablierte Entwickler wie Sylvestre Ledru von Debian und Daniel Hofstetter, der in einem iX-Interview die langfristigen Ziele des Projekts erläuterte.

Für Distributionen stellt sich die Frage nach der Paketierung: Ubuntu 25.10 setzt die Rust Coreutils standardmäßig ein, Nutzer können per apt purge coreutils-from-uutils zu den GNU-Varianten zurückkehren. FreeBSD bietet einen Port über FreshPorts an. Die MIT-Lizenz der Rust Coreutils ist mit der GPL der GNU Coreutils kompatibel und erlaubt eine problemlose Integration in Distributionen.

Die Download-Binaries für Version 0.5.0 stehen auf der Projekt-Website und über die GitHub-Releases bereit. Anwender sollten vor einem produktiven Einsatz die Test-Coverage-Dokumentation konsultieren und kritische Workflows prüfen.


(fo)



Source link

Weiterlesen

Entwicklung & Code

Verbindungsabbrüche bei heise online durch Cookies – eine Spurensuche


close notice

This article is also available in
English.

It was translated with technical assistance and editorially reviewed before publication.

In der Webentwicklung schreiben wir nicht nur neue Software, sondern es erreichen uns natürlich auch Fehlermeldungen. Meistens können wir schnell helfen oder den Bugfix auf jeden Fall für einen der nächsten Sprints einplanen. Aber manche Fehler sind hartnäckiger und haben am Ende eine ganz simple Ursache. Um solch einen Fehler geht es heute.

Weiterlesen nach der Anzeige


Ich roll dann mal aus - Hilko Holweg

Ich roll dann mal aus - Hilko Holweg

Hilko Holweg ist Frontend-Developer bei Heise Medien, wo es ihm besonders die Web-Performance angetan hat. Neben dem Frontend interessiert er sich auch für vieles mehr, das mit Technik zu tun hat. So schrieb er beispielsweise für die c’t einen Artikel über einen digitalen Assistenten mit Offline-Spracherkennung auf Basis des Raspberry Pi.

Eine Zeit lang erreichten uns immer mal wieder Berichte, dass bei Usern die Verbindung zu www.heise.de mit der Meldung ERR_HTTP2_PROTOCOL_ERROR nicht zustande kam. Schnell kristallisierte sich heraus, dass die betroffenen User noch ein paar Gemeinsamkeiten hatten: Alle nutzten Chrome als Browser und waren regelmäßige Besucher unseres Angebots. Damit war der Fehler zwar schon etwas eingegrenzt, aber unser größtes Problem war: Wir selbst konnten den Fehler lange Zeit nicht nachstellen.

Die Überlegungen gingen dennoch weiter. Was sammeln User (leider heutzutage) zuhauf, wenn sie auf einem weitgehend werbefinanzierten Angebot wie heise online unterwegs sind? – Cookies. Ein Test mit betroffenen Usern sorgte dann immerhin für einen Workaround: Cookies löschen half.

Zunächst hatten wir die Cookie-Größe im Verdacht und testeten mit besonders großen Cookies, aber auch damit ließ sich das Problem für uns nicht reproduzieren. Doch dann meldete sich ein Kollege aus der Redaktion mit demselben Fehler – er bekam ihn sogar regelmäßig. Wir baten ihn um Hilfe bei der Lösung, und er gab Bescheid, sobald der Bug erneut auftrat. Endlich konnten wir das Problem direkt beobachten.

Mit tcpdump schnitten wir den Netzwerkverkehr zwischen uns und dem Browser auf dem Load-Balancer (BigIP) mit, der TLS und HTTP2 termininiert. Dabei stellte sich heraus, dass BigIP selbst die HTTP2-Verbindung wegen eines „Protokollfehlers“ beendete. Da heise online nicht einfach eine direkte Verbindung vom Browser des Users zu unserem Webserver hat, sondern noch diverse (Netzwerk-)Infrastruktur dazwischen liegt, war es für uns schon mal sehr hilfreich, den Punkt ausfindig zu machen, an dem die Verbindung bricht und welcher Teil in der Kette diesen Abbruch auslöst.

Weiterlesen nach der Anzeige

Mit den gewonnenen Erkenntnissen durchforsteten wir die Chrome-Bug-Reports. Bei einem Report war ein HTTP2-Protokoll-Mitschnitt angefügt, bei dem wir sehen konnten, dass Chrome jeden Cookie im HTTP2-Request mit einem separaten Set-Cookie-Header sendete. Das brachte uns auf die Idee, statt der Cookie-Größe einfach mit der schieren Anzahl zu experimentieren, und siehe da: Mit sehr vielen, kleinen Cookies ließ sich das Problem reproduzieren.

Ab hier wurde es dann einfach. Mithilfe unserer Admins fanden wir eine Einstellung in der BigIP, die die maximal zulässige Anzahl der Header setzte. Dieses Limit verschoben wir nun deutlich nach oben und schon war das Problem gelöst. Jedenfalls fürs Erste, denn natürlich ist das neue höhere Limit mit noch mehr Cookie-Headern ebenfalls wieder erreichbar, und der Fehler käme zurück.

Am Fehler sind aber noch ein paar Dinge interessant. In HTTP/1.x waren mehrere Cookie-Header noch unzulässig (siehe RFC 6265), in HTTP/2 hingegen kann der User Agent jedes Cookie als einzelnen Header senden (siehe RFC 7540) und genau das hat Chrome hier getan. Dieses Verhalten ist offensichtlich eine Optimierung, denn das Übertragen sich wiederholender Header lässt sich in HTTP/2 mit der HPACK-Header-Komprimierung (siehe RFC 7541) enorm optimieren. Das funktioniert aber nur für Header, die sich nicht ständig ändern. Ein großer Cookie-Header für alle Cookies müsste also immer wieder komplett neu übertragen werden, sobald sich auch nur ein einzelnes Cookie ändert.

Chrome zeigte leider in den Developer-Tools nichts davon an. Dort wird immer nur ein Cookie-Header gelistet, was die Fehlersuche nicht unbedingt erleichtert hat.

Ob das nun eine Problemlösung oder lediglich ein großes Pflaster ist, wird die Zeit zeigen. Die Ursachenforschung war aber definitiv mal wieder eine der interessanteren Recherchen im Developer-Alltag.


(rme)



Source link

Weiterlesen

Beliebt