Wir denken über die Entwicklung einer unternehmenskritischen Anwendung in Java EE nach, und eine Sache, die mich wirklich beeindruckt hat, ist die fehlende Sitzungsisolierung in der Plattform. Lassen Sie mich das Szenario erklären.
Wir haben eine native Windows-Anwendung (eine komplette ERP-Lösung), die von spärlichen Mitwirkenden etwa 2k LoC und 50 Bug-Fixes pro Monat erhält. Es unterstützt auch Skripting, so dass der Kunde seine eigene Logik hinzufügen kann und wir haben keine Ahnung, was diese Logik macht. Anstatt einen Threadpool zu verwenden, verfügt jeder Serverknoten über einen Broker und einen Prozesspool. Der Broker empfängt eine Clientanforderung, reiht sie ein, bis eine gepoolte Instanz frei ist, sendet eine Anforderung an diese Instanz, gibt eine Antwort an den Client aus und gibt die Instanz zurück an den Prozesspool.
Diese Architektur ist robust, da es bei so vielen spärlichen Beiträgen und benutzerdefiniertem Skripting nicht selten ist, dass eine implementierte Version schwerwiegende Fehler wie eine Endlosschleife, eine lang wartende pessimistische Sperre, eine Speicherbeschädigung oder Speicherverlust aufweist. Wir haben ein Speicherlimit, ein Timeout für Anfragen und einen einfachen Watchdog implementiert. Wenn ein Prozess nicht korrekt und pünktlich antwortet, tötet der Broker ihn einfach, sodass der Watchdog eine andere Instanz erkennt und startet. Wenn ein Prozess abstürzt, bevor er mit der Beantwortung einer Anfrage begonnen hat, sendet der Broker dieselbe Anfrage an eine andere gepoolte Instanz, und der Benutzer weiß nichts über einen Fehler auf der Serverseite (außer in den Administratorprotokollen). Das ist nett, weil einige Instanzen langsam durch gefälschten Code zerstört werden, da sie an Anfragen arbeiten. Da die meisten Sitzungsdaten auf dem Client oder (in seltenen Fällen) auf einem freigegebenen Speicher gespeichert sind, scheint es perfekt zu funktionieren.
In Anbetracht der Umstellung auf Java EE konnte ich auf der Spezifikation oder auf den gängigen Anwendungsservern wie Glassfish und JBoss nichts Vergleichbares finden. Ja, ich weiß, dass die meisten Cluster-Implementierungen ein transparentes Fail-Over mit Sitzungsreplikation durchführen, aber wir haben kleine Unternehmen, die unser System in einem einfachen 2-Knoten-Cluster verwenden (und wir haben auch Abenteurer, die das System auf einem 1-Knoten-Server verwenden) . Mit einem Threadpool verstehe ich, dass ein fehlerhafter Thread einen gesamten Knoten herunterfahren kann, da der Server ihn nicht erkennen und nicht gefahrlos beenden kann. Das Herunterfahren eines ganzen Knotens ist viel schlimmer als das Töten eines einzelnen Prozesses - wir haben Bereitstellungen, bei denen jeder Knoten über etwa 100 gepoolte Prozessinstanzen verfügt.
Ich weiß, dass IBM und SAP dieses Problem kennen, basierend auf
. Aber basierend auf aktuellen JSRs, Foren und Open-Source-Tools gibt es nicht viel Aktivität in der Community.
Jetzt kommen die Fragen!
Wenn Sie ein ähnliches Szenario haben und benutze Java EE, wie hast du das gelöst?
Weißt du etwas über ein bevorstehendes? Open-Source-Produkt oder Änderung in Java EE-Spezifikation, die dies ansprechen kann Problem?
Hat .NET das gleiche Problem? Kann Sie erklären oder zitieren Referenzen?
Kennst du etwas modernes und offene Plattform, die dies ansprechen kann Ausgabe und ist es wert, die Aufgabe zu tun ERP-Geschäftslogik?
Bitte, ich muss Sie bitten, nicht mehr über Tests oder andere QA-Investitionen zu berichten, weil wir unsere Kunden nicht dazu zwingen können, dies auf ihren eigenen Skripts zu machen. Wir haben auch Fälle, in denen dringende Bug-Fixes die QA umgehen müssen. Während wir den Kunden zwingen, dies zu akzeptieren, können wir ihn nicht dazu bringen, zu akzeptieren, dass ein fehlerhafter Softwareteil eine Reihe von nicht verwandten Funktionen beeinflussen kann. Dieses Problem betrifft robuste Architekturen und nicht den Entwicklungsprozess.
Danke für Ihre Aufmerksamkeit!
Was Sie herausgefunden haben, ist ein grundsätzliches Problem bei der Verwendung von Java und "feindlichen" Anwendungen.
Es ist ein grundlegendes Problem, nicht nur auf der Ebene von Java EE, sondern auf der JVM-Kernebene. Die typischen verfügbaren JVMs haben alle möglichen Probleme beim Laden von "unsicherem Code". Aus Speicherlecks, Class Loader-Lecks, Ressourcenerschöpfung und unreinen Thread-Kills ist die typische JVM einfach nicht robust genug, um sich schlecht verhaltenden Code in einer gemeinsamen Umgebung gut zu handhaben.
Ein einfaches Beispiel ist die Speicherauslastung des Java Heaps. Als eine Grundregel erkennt NOBODY (und niemand, ich meine speziell die Core-Java-Bibliothek und fast jede andere 3rd-Party-Bibliothek) OutOfMemory-Ausnahmen. Es gibt die wenigen, die das tun, aber auch sie können wenig dagegen tun. Ein typischer Code behandelt die Ausnahmen, die sie "erwarten", aber andere fallen durch. Laufzeitausnahmen (von denen OOM eins ist) werden glücklicherweise durch den Aufrufstapel bis ganz nach oben aufsteigen, wobei ein Wrack unkontrollierten kritischen Pfadcodes zurückbleibt, der alle möglichen Dinge in einem unbekannten Zustand zurücklässt.
Dinge wie Konstruktoren oder statische Initialisierer, die "nicht fehlschlagen lassen" können, die nicht initialisierte Klassenmitglieder zurücklassen, die "niemals null" sind. Diese beschädigten Klassen wissen einfach nicht, dass sie beschädigt sind. Niemand weiß, dass sie beschädigt sind, und es gibt keine Möglichkeit, sie zu säubern. Ein Heap, der auf OOM trifft, ist ein unsicheres Image und muss ziemlich neu gestartet werden (es sei denn, Sie haben den gesamten Code selbst geschrieben oder auditiert, was Sie natürlich nicht tun - wer würde das tun?).
>Nun kann es durchaus herstellerspezifische JVMs geben, die sich besser benehmen und Ihnen bessere Kontrolle geben. Diejenigen, die auf der Sun / Oracle JVM basieren (d. H. Die meisten von ihnen) nicht.
Es ist also nicht unbedingt ein Java EE-Problem, es ist ein JVM-Problem.
Das Hosten von feindlichem Code in der JVM ist eine schlechte Idee. Die einzige Möglichkeit ist, wenn Sie eine Skriptsprache hosten und die Skriptsprache eine Art Ressourcensteuerung implementiert. Das könnte gemacht werden, und Sie können die bestehenden als einen Anfang (JavaScript, Groovy, JPython, JRuby) zwicken. Die Tatsache, dass diese Sprachen den Benutzern direkten Zugriff auf Java-Bibliotheken gewähren, macht sie potenziell gefährlich, so dass Sie dies möglicherweise auch auf Aspekte beschränken müssen, die von Skripthandlern umschlossen sind. An diesem Punkt wird jedoch die Frage "Warum Java überhaupt verwenden" eingeblendet.
Sie werden feststellen, dass Google App Engine keines dieser Elemente verwendet. Es spoolt eine separate JVM für jede Anwendung, die ausgeführt wird, aber selbst dann wird stark eingeschränkt, was in diesen JVMs getan werden kann, insbesondere durch das vorhandene Java-Sicherheitsmodell. Der Unterschied besteht darin, dass diese Instanzen dazu tendieren, "langlebig" zu sein, um die Verarbeitungskosten beim Starten und Herunterfahren nicht zu ertragen. Ich sollte sagen, sie sollten lange gelebt werden, und diejenigen, die nicht sind, entstehen diese Kosten.
Sie können mehrere Instanzen der JVM selbst erstellen, ihnen ein wenig Infrastruktur zur Verfügung stellen, um Anfragen nach Logik zu bearbeiten, ihnen eine benutzerdefinierte Klassenladerlogik geben, um vor Klassenladeprogrammlecks zu schützen, und Sie können die Instanzen minimal ausschalten (sie Es ist einfach ein Prozess, wenn Sie wollen. Das kann funktionieren und funktioniert wahrscheinlich "ok", abhängig von der Granularität der Aufrufe und der "Start" -Zeit für Ihre Logik. Die Startzeit wird minimal das Laden der Klassen für die Logik von Lauf zu Lauf sein, das allein kann dies eine schlechte Idee machen. Und es wird sicherlich nicht "Java EE" sein. Java EE ist nicht dafür eingerichtet. Aber Sie wissen nicht, welche Java EE-Funktionen Sie gerade betrachten.
Effektiv, das ist was Apache und "mod_php" tut. Mehrere Instanzen, als Prozesse, die Anfragen einzeln bearbeiten, mit schlechtem Verhalten, sobald sie wie nötig abgetötet werden. Aus diesem Grund ist PHP im Shared-Hosting-Geschäft üblich. In dieser Struktur ist es grundsätzlich "sicher".
Ich glaube, dass Ihr Szenario höchst untypisch ist, daher ist es unwahrscheinlich, dass es einen vorgefertigten Rahmen / Plattform gibt, der diesem Bedarf gerecht wird. Java EE geht davon aus, dass der Request Processing Code vom selben Team geschrieben wird wie der Rest der App, daher muss er nicht so oft isoliert, überwacht und zurückgesetzt werden, und Bugfixes würden in allen Teilen der App gleich gehandhabt werden System. Diese Annahme vereinfacht die Entwicklung, den Einsatz, das Testen usw. für die meisten Projekte erheblich und zwingt sie nicht, für etwas zu bezahlen, was sie nicht brauchen. Und ja, es ist nicht für jeden geeignet. Wenn Sie etwas grundlegend anderes wollen, müssen Sie wahrscheinlich selbst eine gewisse Menge an Failover-Logik implementieren. Java EE stellt die grundlegenden Bausteine dafür zur Verfügung.
Ich glaube (obwohl es keine konkrete Erfahrung hat, dies zu beweisen), dass .NET oder andere Plattformen im Grunde auf ähnlichen Annahmen beruhen.
Wir hatten einen ähnlichen - wenn auch nicht so schwerwiegenden - Port einer wirklich enormen Perl-Site für Java. Nach dem Empfang einer HTTP-Anfrage instanziieren wir eine Klasse und rufen ihre processRequest-Methode auf. umgeben von Try-Catch und Zeitmessung. Das Hinzufügen eines Timers und eines Threads würde ausreichen, um den Thread zu beenden. Das ist wahrscheinlich im wirklichen Leben ausreichend.
Ein Java-EE-Server wie Glassfish ist ein OSGi-Container, den Sie möglicherweise isolierender einsetzen.
Sie können auch eine Reihe von (Web- oder lokalen) Anwendungen ausführen, auf denen Sie Ihre Anfrage über eine zentrale Web-Anwendung versenden. Diese Anwendungen sind dann isoliert.
Noch isolierter sind serialisierte Sitzungen und Betriebssystemprozesse, die eine neue JVM starten.