Wie gehen Sie mit transitiven Abhängigkeitskonflikten um, die nur zur Laufzeit bekannt sind? [geschlossen]


9

Wie gehen Sie normalerweise mit transitiven Abhängigkeitsproblemen um, die zur Laufzeit in großen Softwareprojekten auftreten?

In den letzten drei Wochen habe ich versucht, eine Komponente einer großen Software innerhalb einer anderen Komponente der Software zu starten, diese stirbt jedoch zeitweise aufgrund von Problemen mit der transitiven Abhängigkeit, die nur zur Laufzeit bekannt sind.

Mit transitiven Abhängigkeitsproblemen meine ich, dass bestimmte Abhängigkeiten der Abhängigkeiten eines bestimmten Projekts zur Laufzeit mit anderen Abhängigkeiten kollidieren und Instabilität oder sofortigen Fehler verursachen.

Es werden Hunderte von Abhängigkeiten verwendet, und es gibt ungefähr 50 Teilprojekte, die mit dem Tool verknüpft sind und von anderen Teams isoliert bearbeitet werden, wobei alle Module tief verschachtelte Abhängigkeiten untereinander aufweisen. Niemand weiß, wofür alle Teilprojekte verwendet werden, angesichts des Umfangs und der Komplexität des Projekts.

Würden Sie in dieser Situation versuchen, eine visuelle Darstellung der DAG für jede der Abhängigkeiten der betroffenen Komponente zu erstellen und festzustellen, wo zur Laufzeit Kollisionen auftreten können? Ich habe keine Kontrolle darüber, wie Abhängigkeiten in anderen Unterprojekten verwaltet werden, und kann keinen Java-Code ändern, der von anderen Entwicklern geschrieben wurde

Die Lösungen, die ich mir ausgedacht habe, funktionieren nur ein oder zwei Stunden und funktionieren dann aufgrund von Änderungen an den vorgelagerten Komponenten nicht mehr. Ein Beispiel für eine vorgelagerte Komponente ist ein Artefakt, von dem das Projekt, an dem ich arbeite, abhängig ist und das zu einem früheren Zeitpunkt in der CI-Pipeline erstellt wurde.


Auf Wunsch anderer werde ich Informationen darüber aufnehmen, welche Technologie verwendet wird, auf die Gefahr hin, dass die Frage wegen zu vieler Informationen geschlossen wird oder der Körper zu lang wird:

  • Maven wird für das Abhängigkeitsmanagement verwendet. und
  • Die Feder wird als DI-Behälter verwendet.
  • Die meisten Abhängigkeitsprobleme betreffen überlappende Bean-Kontexte, da die Kontexte anderer Module zur Laufzeit geladen werden
  • Das Produkt funktioniert ordnungsgemäß, und es gibt eine Vielzahl von Komponententests und Integrationstests, um der funktionalen Korrektheit des Programms zu entgehen

Im Allgemeinen suche ich nach einem sprachunabhängigen Ansatz, um Möglichkeiten zur Lösung von Abhängigkeitskonflikten zu identifizieren, ohne alle möglichen Kombinationen der Abhängigkeiten eines bestimmten Projekts aufzuzählen.

Ich kann das Projekt nicht neu gestalten, zusätzliche Qualitätstore hinzufügen, auf Paradigmenwechsel im gesamten Unternehmen drängen oder die Sprache als Lösung wechseln.


Sprechen Sie über etwas wie den DI-Container, der nicht richtig eingerichtet ist, um zu wissen, dass IFooImpl für IFoo verwendet wird?
Andy

Nein, es ist eher so, als ob Paket (b) in Projekt (b) eine transitive Abhängigkeit von Paket (a) in Projekt (A) ist, aber (B) wird nur zur Laufzeit eingeführt. Die Software funktioniert nach allem, was ich sagen kann, aber ich muss nur eine Reihe von Abhängigkeiten festlegen, damit alle transitiven Abhängigkeiten ordnungsgemäß aufgelöst werden können. Es gibt eine Reihe von Abhängigkeiten, mit denen das Produkt ordnungsgemäß gestartet werden kann. Diese Konfigurationen sind jedoch sehr spröde. Als Junior-Entwickler. Ich habe keine Kontrolle darüber.
Tyler

2
Komm schon, "Abhängigkeitsproblem" ist ein sehr allgemeiner Begriff, und jeder, der dies liest, stellt sich wahrscheinlich etwas anderes vor. Für reale Software ist dies technologieabhängig. Geben Sie daher ein reales Beispiel für eine bestimmte Technologie, an die Sie denken. Treten die Probleme auf, weil die benötigten Komponenten fehlen? Oder weil eine falsche Version bereitgestellt wird? Welche Art von Abhängigkeitsauflösungsmechanismus ist bereits vorhanden? Bitte klären Sie.
Doc Brown

1
In der .Net-Welt löse ich dieses Problem, indem ich die Pakete einfach in mein Hauptanwendungsprojekt installiere. Während kein Anwendungscode die Plugin-Bibliotheken direkt verwendet, stellt der .Net-Compiler sicher, dass sich die DLL während der Erstellung und damit der Bereitstellung an der richtigen Stelle befindet.
Andy

2
"Ich habe keine Kontrolle darüber, wie Abhängigkeiten verwaltet werden, und kann keinen Code ändern" - was können Sie tatsächlich ändern? Wenn Sie nichts ändern können, erscheint die Frage sinnlos.
Doc Brown

Antworten:


6

Möglicherweise können Sie Ihr Problem mit benutzerdefinierten Klassenladeprogrammen lösen, um jedem Modul in der Anwendung eine eigene Ausführungsumgebung zu geben.

Grundsätzlich würden Sie eine Kernumgebung definieren, die aus einem kleinen Kernel der Anwendung besteht, zusammen mit den allgemein vereinbarten Abhängigkeiten und allen Schnittstellen, die für die Kommunikation der Module untereinander erforderlich sind. Anschließend erhält jedes geladene Modul einen eigenen Klassenladeprogramm, das auf (1) Klassen aus der Laufzeitumgebung, (2) Klassen aus dem Anwendungskern und (3) Klassen aus diesem Modul und seinen direkten Abhängigkeiten zugreifen kann. Die gesamte Kommunikation zwischen Modulen erfolgt über im Kern definierte Schnittstellen, sodass kein Modul direkt von einem anderen abhängt.

Diese Technik wird in Anwendungsservern verwendet, um Anwendungen Abhängigkeiten zu ermöglichen, die andernfalls in Konflikt geraten würden, sodass Sie eine funktionierende Implementierung der Technik sehen können, z. B. in Tomcat (und wenn Sie Glück haben, können Sie möglicherweise Tomcat verwenden Implementierung mit wenig Änderung).


1
Es ist mir nicht gestattet, das Projekt neu zu gestalten, um das Abhängigkeitsproblem zu lösen. Ein Wechsel zu einer Mikrokernel-ähnlichen Architektur wäre allerdings ziemlich cool.
Tyler

2
@ Tyler: Trotzdem denke ich, dass dies eine gute, generische Antwort auf den generischen Teil der Frage ist.
Doc Brown

1
Das klingt ein bisschen nach Osgi.
SpaceTrucker

4

Ich habe noch nie mit Maven oder Spring gearbeitet, aber um Ihnen eine allgemeine Antwort auf eine allgemeine Frage zu geben: Wenn es um Abhängigkeitsprobleme geht, sollte Ihr System so ausgelegt sein, dass sie zum frühestmöglichen Zeitpunkt erkannt werden und wenn diese Probleme erkannt werden sollten sie sofort signalisiert werden, einschließlich ihrer möglichen Ursache.

Idealerweise liegt dieser Zeitpunkt nicht zur "Produktionslaufzeit". Der beste Zeitpunkt ist "Bauzeit", aber das scheint in Ihrem Fall unmöglich zu sein. Die zweitbeste Option sind also Laufzeit-Tests. Sie haben uns gesagt, dass es "Integrationstests" gibt, aber da sie die Abhängigkeitsprobleme nicht erkennen, scheinen sie unvollständig zu sein oder sie testen Abhängigkeitskollisionen im Allgemeinen nicht. Hier sollten Sie versuchen, das Problem zu lösen.

Um die Tests effektiver zu gestalten, müssen Ihre Komponenten außerdem "schnell ausfallen", wenn ein Abhängigkeitsproblem auftritt. Das heißt, sobald eine Abhängigkeit aufgelöst und eine Komponente geladen ist, sollten potenzielle Abhängigkeitskollisionen sofort überprüft und signalisiert werden. Wenn das System zum ersten Mal eine Stunde lang ausgeführt wird, bevor eine Kollision erkannt wird, ist es sehr schwierig, die Ursache für das Problem zu ermitteln. Ich weiß nicht, ob Maven / Spring Ihnen bereits eine integrierte "Fail Fast" -Strategie bietet, aber wenn dies nicht der Fall ist, versuchen Sie, in diese Richtung zu denken.

Um Probleme in der Produktion zu vermeiden, sollte Ihr System so konzipiert sein, dass es nicht vollständig stirbt, wenn eine Abhängigkeitskollision mit einer weniger wichtigen Unterkomponente auftritt. Der Fehler sollte natürlich nicht maskiert werden, aber das System sollte das Laden dieser Komponente idealerweise ablehnen, das Problem protokollieren und / oder signalisieren und in einem stabilen Zustand bleiben. Wenn die Komponente zuerst geladen wird, wird das System instabil und Sie erkennen das Problem erst danach. Dann müssen Sie das System wahrscheinlich herunterfahren und vollständig neu starten, was Sie wahrscheinlich besser vermeiden möchten.

Wenn Ihr System beispielsweise ein Webshop ist und Ihr Submodul für "Newsletter-Abonnement" einige Stunden lang nicht geladen werden kann, ist dies möglicherweise leichter zu tolerieren, als wenn der gesamte Shop nicht mehr funktioniert.

Schließlich ist dies in Ihrem Fall möglicherweise keine Option. Wenn Komponenten jedoch besser isoliert voneinander ausgeführt werden können und Abhängigkeitskollisionen vermieden werden können, ist dies möglicherweise ein erwägenswerter Ansatz.


2

Denken Sie daran, dass ich hier nur laut nachdenke. Abhängig von den Anforderungen / zeitlichen Einschränkungen könnte ich Folgendes berücksichtigen:

1) Erstellen Sie eine Liste der Schnittstellen und ihrer Implementierungen (ggf. mithilfe von Reflection).

2) Erstellen Sie eine Art Wrapper um Ihren DI. Wenn ein DI aufgefordert wird, eine konkrete Implementierung bereitzustellen, prüfen Sie, ob bereits eine DI-Regel vorhanden ist. Wenn eine Regel vorhanden ist, geben Sie einfach zurück, was Ihr DI bereitstellt. Andernfalls, wenn es eine einzelne Implementierung einer Schnittstelle gibt und Sie ein Instanzprotokoll erstellen und eine Instanz zurückgeben können. Andernfalls protokollieren und fehlschlagen.

Hängt davon ab, wie involviert Sie sein möchten, aber irgendwann möchten Sie nur noch eine vollständige Liste der Anforderungen haben - diese Lösung generiert schließlich diese Liste.

Dies ist jedoch wohl eine Menge Arbeit und nicht zuverlässig - was ist, wenn Sie einen seltsamen Zustand haben, der eine bestimmte Abhängigkeit einmal in einem blauen Mond erfordert?

Was meinen Sie mit "Änderungen an vorgelagerten Komponenten"?


Entschuldigung, ich wollte nicht zu viele Hintergrundinformationen bereitstellen. Wenn ich die Betriebsumgebung beschreibe, werden Fragen normalerweise abgelehnt und zu Tode geflammt. Ich werde die ursprüngliche Frage mit einem Definitionsabschnitt aktualisieren.
Tyler

2

Wenn ich Ihnen richtig folge, besteht das Problem darin, dass der Code, den Sie verwenden müssen, Laufzeitabhängigkeiten von Modulen aufweist, die in Spring XML ausgedrückt werden, aber Maven wird nicht darüber informiert. Wenn Sie sie verwenden, befinden sich die benötigten Dinge (die transitiven Abhängigkeiten) nicht in Ihrem Klassenpfad und sie tun nicht das, was sie sollten.

Ich vermute, wenn Sie Ihre Kollegen fragen: "Wie bringe ich das zum Laufen?", Sagen sie: "Offensichtlich benötigen Sie diese Liste mit 35 Modulen und Versionen . Wie können Sie das nicht wissen?"

Vermutlich werden nach Abschluss des Integrationstests die erforderlichen Module als Testabhängigkeiten deklariert. Die systematische Lösung besteht daher darin, Inspektionen von Integrationstest-Poms einzurichten, um sicherzustellen, dass nur Testtools von Drittanbietern als Testabhängigkeiten deklariert sind. Dies könnte sogar durch das Maven Enforcer Plugin automatisiert werden .

Anscheinend können Sie das nicht tun, also finden Sie hier wahrscheinlich Ihre beste Option .


Genau das passiert, soweit ich das beurteilen kann. Bisher jongliere ich mit 30 Abhängigkeiten und drehe die Bereiche, um Kollisionen zu vermeiden, aber hier ist Peer Review erforderlich.
Tyler
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.