Bei DIRECTUM entwickeln wir das DirectumRX ECM-System. Das Kernelement des Workflow-Moduls für das ECM-System ist die Engine. Er ist dafür verantwortlich, den Status der Prozessinstanz (Instanz) während des Lebenszyklus zu ändern. Bevor Sie mit der Entwicklung des Workflow-Moduls beginnen, sollten Sie sich entscheiden: Nehmen Sie eine vorgefertigte Engine oder schreiben Sie Ihre eigene. Anfangs haben wir uns für die erste Option entschieden. Wir haben die Windows Workflow Foundation (WF) -Engine verwendet und insgesamt hat sie uns gepasst. Aber im Laufe der Zeit wurde uns klar, dass wir unseren eigenen Motor brauchten. Wie das passiert ist und was daraus geworden ist, werde ich weiter unten erzählen.
Alter Motor
Warum wf?
Bereits 2013, als es Zeit war, ein Workflow-Modul für DirectumRX zu entwickeln, entschieden wir uns für eine vorgefertigte Engine. Betrachtet von Windows Workflow Foundation (WF), ActiveFlow, K2.NET, WorkflowEngine.NET, cDevWorkflow, NetBpm. Einige waren mit den Kosten nicht zufrieden, andere waren roh, andere waren zu diesem Zeitpunkt schon lange nicht mehr unterstützt worden.
Infolgedessen fiel die Wahl auf WF. Wir haben dann aktiv den Microsoft-Stack (WCF, WPF) verwendet und entschieden, dass ein anderes W uns nicht verletzen würde. Ein weiterer Vorteil war unser Status als Microsoft Gold Application Development Partner, der es ermöglichte, Produkte mit Microsoft-Technologien zu entwickeln. Im Allgemeinen passten die Fähigkeiten des Motors zu uns und deckten fast alle unsere Fälle ab.
Was ist los mit WF?
Nach 6 Jahren mit WF haben wir eine Reihe von Problemen angehäuft, und die Kosten für die Lösung dieser Probleme waren zu hoch. Wir begannen darüber nachzudenken, unseren eigenen Motor zu entwickeln. Ich werde Ihnen von einigen erzählen.
Teure Diagnose und Fehlerbehebungen
Jahre vergingen, die Anzahl der Produktinstallationen und die Last nahmen zu. Es traten Fehler auf, deren Diagnose und Korrektur viele Ressourcen in Anspruch nahm. Dies wurde durch eine Reihe von Gründen erleichtert: mangelnde Kompetenzen, Konstruktionsfehler beim Einbetten der vorherigen Engine und Funktionen von WF.
Wir hatten genug Grundkompetenzen, um WF DirectumRX einzubauen. Das gleiche Niveau reichte aus, um einfache Fehler zu beheben. In komplexen Fällen reichten die Kompetenzen nicht aus - die Analyse von Protokollen, die Analyse des Status der Instanz usw. waren schwierig.
Es war möglich, eine Person zu Kursen über WF zu schicken, aber sie lernen kaum, wie man den Status einer Instanz analysiert und ihre Änderung mit Protokollen verknüpft. Und ehrlich gesagt hatte niemand den besonderen Wunsch, seine Fähigkeiten mit praktisch toter Technologie zu verbessern.
Eine andere Möglichkeit besteht darin, eine Person mit den entsprechenden Kompetenzen einzustellen. Aber einen in Ischewsk zu finden, ist keine so triviale Aufgabe und nicht die Tatsache, dass sein Niveau ausreicht, um unsere Probleme zu lösen.
Tatsächlich stehen wir vor einer hohen Eintrittsschwelle zur Unterstützung von WF. Ich denke, wir würden uns auf die eine oder andere Weise mit diesem Problem befassen, wenn nicht aus einer Reihe anderer Gründe.
Ein weiteres Problem war, dass wir beim Erstellen von Prozessdiagrammen unsere eigene Notation verwenden. Es ist visueller und einfacher zu entwickeln. Beispielsweise erlaubt WF nicht, ein vollwertiges Diagramm zu implementieren. Sie können keine Sackgassenblöcke zeichnen. Es gibt Funktionen zum Zeichnen paralleler Zweige. Die Amortisation hierfür ist die Umwandlung unserer Schaltungen in WF-Schaltungen, die nicht so einfach sind und eine Reihe von Einschränkungen auferlegen. Beim Debuggen musste ich den Status der WF-Schaltung analysieren. Aus diesem Grund ging die Sichtbarkeit verloren. Ich musste Blöcke und Gesichter miteinander vergleichen, um zu verstehen, in welchem Schritt sich die Instanz befand.
Darstellung der Schaltung in DirectumRXDarstellung der Schaltung in WFWir waren auch mit der Tatsache konfrontiert, dass die WF-Dokumentation das Instanz-Repository schlecht beschreibt. Wie ich oben geschrieben habe, ist dies bei der Analyse eines Fehlers erforderlich, um den Status der Prozessinstanz zu verstehen. Außerdem wird ein Teil der Daten verschlüsselt, was auch die Analyse stört.
Postgres als DBMS
In Russland gibt es seit vielen Jahren einen Trend zur Importsubstitution, und eine der Anforderungen an die Plattform ist immer häufiger die Unterstützung von Open-Source-Datenbankverwaltungssystemen (DBMS) oder inländischem DBMS. Meistens ist es Postgres. Standardmäßig unterstützt WF nur MS SQL. Um mit anderen Datenbanken zu arbeiten, können Sie Drittanbieter verwenden. Wir haben dotConnect von DevArt ausgewählt.
Während die Ladung leicht war, funktionierte alles gut. Sobald wir das System unter Last fuhren, traten Probleme auf. WF konnte plötzlich die Verarbeitung von Instanzen stoppen und stoppen (vorbereitete Transaktionen wurden beendet), oder alle Nachrichten gingen an die MSMQ Poisoned Queue usw. Wir haben uns mit all diesen Problemen befasst, aber viel Zeit damit verbracht. Es gab keine Garantie dafür, dass keine neue erscheinen würde, deren Lösung den gleichen Betrag ausgeben müsste.
Pflege auf .net Kern
Nachdem Microsoft .Net Core angekündigt hatte, beschlossen wir, schrittweise darauf zuzugreifen, um eine plattformübergreifende Lösung für unsere Lösungen zu erreichen. Microsoft hat beschlossen, WF nicht an Bord zu nehmen, was uns die Übertragung des Workflow-Moduls auf .Net Core in der Form versperrte, in der es vorhanden war. Wir sind uns bewusst, dass es inoffizielle WF-Ports auf .Net Core gibt, darunter auch von WF-Entwicklern, aber nicht alle sind zu 100% kompatibel. Ein weiterer Faktor war die Weigerung von Microsoft, .Net zu entwickeln. zugunsten von .Net Core.
Neuer Motor
Angesichts all dieser Probleme, Lösungsoptionen, der Komplexität von Refactoring und Korrekturen und der Abwägung aller Vor- und Nachteile haben wir uns entschlossen, auf eine neue Engine umzusteigen. Wir haben zunächst vorhandene analysiert.
Die Wahl
Die Hauptanforderungen bei der Auswahl eines Motors waren:
- Arbeit an .Net Core;
- Skalierbarkeit
- Konvertierung vorhandener Prozessinstanzen mit der Möglichkeit, die Ausführung nach der Konvertierung fortzusetzen
- angemessene Kosten für die Analyse bestehender Probleme
- arbeiten mit verschiedenen DBMS
Darüber hinaus war es erforderlich, dass die Aktivität (Aktivität) Anwendungscode in C # ausführen konnte, dass Blöcke debuggt werden konnten usw.
Im Rahmen der Analyse bestehender Motoren haben wir Folgendes untersucht:
- Kern wf
- Flowwright
- K2-Workflow
- Workflow-Kern
- Zeebe
- Workflow-Engine
- Dauerhaftes Aufgaben-Framework
- Camunda
- Orleans Aktivitäten
Nachdem wir alle Anforderungen an die überprüften Lösungen gestellt und die Kosten für bezahlte Lösungen addiert hatten, waren wir der Ansicht, dass unser Motor nicht sehr teuer ist, zu 100% für unsere Anforderungen geeignet und leicht zu verfeinern ist.
Implementierung / Architektur
In der vorherigen Implementierung war das WF-Modul ein WCF-Dienst, mit dem WF-Bibliotheken verbunden waren. Er war in der Lage, Prozessinstanzen zu erstellen, Prozesse zu starten, Blöcke auszuführen, einschließlich Geschäftslogik (von Entwicklern geschriebener Code). All dies wurde in der IIS-Anwendung gehostet.
In der neuen Implementierung haben wir uns entsprechend dem Trend der Microservice-Architektur entschlossen, den Service sofort in zwei Teile zu unterteilen: Workflow Process Service (WPS) und Workflow Block Service (WBS), die separat gehostet werden können. Ein weiteres Glied in dieser Kette ist der Anwendungsdienst, der das DirectumRX-System und die Geschäftslogik implementiert und mit dem Clients arbeiten.
WPS „geht“ gemäß dem Schema, WBS verarbeitet Blöcke und führt bei jedem Schritt Geschäftslogik aus. Der Befehl zum Starten des Prozesses kommt vom Anwendungsserver. Die Interaktion zwischen den Diensten erfolgt mit RabbitMQ. Im Folgenden werde ich Ihnen mehr über jeden von ihnen erzählen.
Wps
Der Workflow Process Service ist ein Microservice, der für das Starten von Prozessen und das Umgehen des Prozessdiagramms verantwortlich ist.
Das Service-Repository enthält Prozessdiagramme mit Unterstützung für die Versionierung und den serialisierten Status von Prozessinstanzen. Sie können MS SQL und Postgres als Speicher verwenden.
Der Dienst kann Nachrichten verarbeiten, die von anderen Diensten über RabbitMQ empfangen wurden. Nachrichten sind im Wesentlichen eine Service-API. Arten von Nachrichten, die der Dienst empfangen kann:
- StartProcess - Erstellen Sie eine neue Prozessinstanz und starten Sie einen Crawl darauf.
- CompleteBlock - Abschluss des Blocks. Nach dieser Nachricht verschiebt der Dienst die Prozessinstanz weiter entlang des Schemas.
- Suspend / ResumeProcess - Unterbricht die Ausführung einer Prozessinstanz, z. B. aufgrund eines Fehlers bei der Verarbeitung eines Blocks, und setzt die Ausführung fort, nachdem der Fehler behoben wurde.
- Abort / RestartProcess - Stoppen Sie die Ausführung der Prozessinstanz und starten Sie sie erneut.
- DeleteProcess - Löscht eine Prozessinstanz.
Das Schema besteht aus Blöcken und Verbindungen zwischen ihnen (Flächen). Jedes Gesicht hat eine Kennung, das sogenannte "Ausführungsergebnis". Es gibt 5 Arten von Blöcken:
- StartBlock
- Blockieren
- OrBlock;
- AndBlock;
- FinishBlock.
WPS-SchemaansichtWenn eine Nachricht zu Beginn des Prozesses eintrifft, erstellt der Dienst eine Instanz und beginnt, gemäß dem Schema zu "laufen". Die Klasse, die nach dem Schema für das „Gehen“ verantwortlich ist, nennen wir scherzhaft den „Stepator“. Eine Schaltung beginnt immer mit einem StartBlock. Dann nimmt der Strider alle ausgehenden Gesichter und aktiviert sie. Jeder Block arbeitet nach dem Prinzip des "UND" -Blocks, d.h. Alle eingehenden Gesichter müssen aktiv sein, damit der Block aktiviert werden kann. Der Algorithmus entscheidet dann, welche Blöcke aktiviert werden können, und sendet eine PSP-Nachricht, um diese Blöcke zu aktivieren. WBS verarbeitet den Block und gibt das Ergebnis des WPS zurück. Abhängig vom Ergebnis der Ausführung wählt der Strider die entsprechenden Flächen aus, die zur Aktivierung aus dem Block kommen, und der Prozess wird fortgesetzt.
Während der Entwicklung stießen wir auf interessante Situationen im Zusammenhang mit zyklischen Verbindungen zwischen Blöcken, die bei der Entscheidung, welcher Block aktiviert / gestoppt werden soll, eine zusätzliche Logik aufwiesen.
Der Dienst ist autonom, d.h. Übergeben Sie ihm einfach das Schema im Json-Format, schreiben Sie Ihren eigenen Block-Handler und Sie können Nachrichten austauschen.
Wbs
Der Workflow-Blockdienst ist ein Dienst, der Blockdiagramme verarbeitet. Der Dienst kennt die Essenz der Geschäftslogik wie Aufgabe, Aufgabe usw. Diese Entitäten können der DDS-Entwicklungsumgebung (DirectumRX Development Studio) hinzugefügt werden. Zum Beispiel haben unsere Blöcke ein Ereignis, um den Block zu starten. Der Code für diesen Ereignishandler wird vom Entwickler in DDS geschrieben, und WBS führt diesen Code aus. Tatsächlich ist dies unsere Implementierung des Block-Handlers, den Sie durch Ihren eigenen ersetzen können.
Der Dienst speichert den Status der Blöcke. Zusätzlich zu den grundlegenden Eigenschaften (ID, Status) kann der Block andere Informationen enthalten, die für die Ausführung / Beendigung / Suspendierung des Blocks erforderlich sind.
Blöcke können sich in einem Zustand befinden:
- Abgeschlossen - geht nach erfolgreichem Abschluss der Arbeiten am Block in diesen Zustand über;
- Ausstehend - befindet sich in einem Wartezustand, wenn einige Arbeiten innerhalb des Blocks ausgeführt werden. Beispielsweise ist vom Benutzer eine Antwort erforderlich.
- Abgebrochen - wechselt in diesen Zustand, wenn der Prozess gestoppt wird.
- Angehalten - wechselt in diesen Zustand, wenn der Prozess angehalten wird, wenn ein Fehler auftritt.
Wenn eine Nachricht zur Ausführung des Blocks eintrifft, wird der Block ausgeführt, und der PSP sendet eine Nachricht mit dem Ergebnis des Blocks.
Skalierbarkeit
WPS und WBS können in mehreren Instanzen bereitgestellt werden. Zu einem bestimmten Zeitpunkt kann nur ein WPS-Dienst eine Prozessinstanz verarbeiten. Gleiches gilt für die Verarbeitung von Blöcken - eine Prozessinstanz kann jeweils nur einen Block verarbeiten. Dies wird durch Sperren unterstützt, die während der Verarbeitung auf den Prozess gesetzt werden. Wenn sich mehrere Nachrichten in der Warteschlange befinden, um einen Prozess / Blöcke in einem Prozess zu verarbeiten, wird die Nachricht für einige Zeit verschoben. Gleichzeitig kann jeder Dienst gleichzeitig an mehreren Prozessinstanzen arbeiten.
Eine Situation kann auftreten, wenn mehrere Nachrichten in einem Prozess nach dem anderen eingehen, um Blöcke (parallele Verzweigungen) zu verarbeiten. Um die Anzahl der Situationen zu verringern, in denen Sie Nachrichten verschieben müssen, nimmt WBS mehrere Nachrichten gleichzeitig auf und führt sie nacheinander aus, wobei das Senden an die Warteschlange zur erneuten Ausführung aufgrund der Blockierung des Prozesses umgangen wird.
Konvertierung
Nach dem Übergang zu einer neuen Engine stellte sich die Frage, was mit vorhandenen Prozessinstanzen zu tun ist. Die bevorzugte Option war ihre Umrüstung, so dass sie weiter an dem neuen Motor arbeiteten. Die Vorteile liegen auf der Hand: Wir unterstützen nur einen Motor, die Probleme bei der Unterstützung des alten Motors verschwinden (siehe oben). Es bestand jedoch das Risiko, dass wir nicht vollständig herausfinden können, wie wir die benötigten Daten aus serialisierten Prozessinstanzen abrufen können. Es gab auch einen Fallback: Bestehende Instanzen auf der alten Engine finalisieren und neue auf einer neuen Engine starten. Die Nachteile dieser Option ergeben sich aus den Vorteilen der vorherigen Option. Außerdem sind zusätzliche Ressourcen erforderlich, um beide Motoren zu verdrehen.
Für die Konvertierung mussten wir den alten Status des Prozesses im WF-Format übernehmen und die Status von Prozessen und Blöcken generieren. Wir haben ein Dienstprogramm geschrieben, das den serialisierten Status einer Prozessinstanz in der Datenbank übernommen, eine Liste der aktiven Blöcke und Ausführungsergebnisse für Gesichter daraus gezogen und den Prozess virtuell ausgeführt hat. Als Ergebnis haben wir den Status der Instanz zum Zeitpunkt der Konvertierung erhalten.
Es traten Schwierigkeiten auf, Prozessprozessinstanzdaten in WF ordnungsgemäß zu deserialisieren. Der Status der Prozessinstanz (Instanz) von WF wird in der Datenbank als xaml gespeichert. Wir konnten keine klare Beschreibung der Struktur dieses XAML finden, wir mussten den ganzen Weg empirisch gehen. Analysierte Daten manuell und holte die benötigten Informationen heraus. Im Rahmen dieser Aufgabe haben wir eine weitere Option ausgearbeitet: Verwenden von WF-Tools, um den Status der Instanz zu deserialisieren und Informationen von Objekten abzurufen. Aufgrund der Tatsache, dass die Struktur solcher Objekte sehr komplex war, haben wir diese Idee aufgegeben und uns für das „manuelle“ Parsen von xaml entschieden.
Infolgedessen war die Konvertierung erfolgreich und alle Prozessinstanzen wurden von der neuen Engine verarbeitet.
Fazit
Was hat uns die Workflow-Engine gegeben? Tatsächlich haben wir es geschafft, alle am Anfang des Artikels angesprochenen Probleme zu überwinden:
- Die Engine ist in .NET Core geschrieben.
- Es handelt sich um einen von IIS unabhängigen Self-Host-Dienst.
- Als Testoperation verwenden wir die neue Engine aktiv im Unternehmenssystem und haben es geschafft, sicherzustellen, dass die Analyse von Fehlern viel weniger Zeit in Anspruch nimmt.
- Nach vorläufigen Daten wurden Lasttests auf Postgres durchgeführt. Das WPS + WBS-Bundle kann die Last von 5000 gleichzeitigen Benutzern problemlos bewältigen.
- und natürlich ist es wie jede interessante Arbeit eine interessante Erfahrung.
Als Bonus haben wir einen klaren und unterstützten Code erhalten, den wir an uns selbst anpassen können.
Die Kosten des Motors erwiesen sich als vergleichbar mit denen, die wir für den Kauf / die Anpassung eines Drittanbieterprodukts ausgeben müssten. Im Moment halten wir die Entscheidung, einen eigenen Motor zu entwickeln, für gerechtfertigt.
Wir warten auch auf Lasttests für mehr als 10.000 gleichzeitige Benutzer. Vielleicht ist eine Optimierung erforderlich, oder vielleicht hebt sie ab? ;-);
Wir haben kürzlich DirectumRX 3.2 veröffentlicht, das den neuen Workflow enthält. Mal sehen, wie sich der Motor den Kunden zeigt.