Comment nous avons créé notre moteur de workflow

Chez DIRECTUM, nous développons le système DirectumRX ECM. L'élément central du module Workflow pour le système ECM est le moteur. Il est responsable de la modification de l'état de l'instance de processus (instance) au cours du cycle de vie. Avant de commencer à développer le module Workflow, vous devez décider: prenez un moteur prêt à l'emploi ou écrivez le vôtre. Au départ, nous avons opté pour la première option. Nous avons pris le moteur Windows Workflow Foundation (WF), et dans l'ensemble, il nous convenait. Mais au fil du temps, nous avons réalisé que nous avions besoin de notre propre moteur. Comment cela s'est produit et ce qui en est arrivé, je le dirai ci-dessous.

Ancien moteur


Pourquoi wf?


En 2013, alors qu'il était temps de développer un module Workflow pour DirectumRX, nous avons décidé de prendre un moteur prêt à l'emploi. Vue depuis Windows Workflow Foundation (WF), ActiveFlow, K2.NET, WorkflowEngine.NET, cDevWorkflow, NetBpm. Certains n'étaient pas satisfaits du coût, certains étaient bruts, certains, à ce moment-là, n'avaient pas été pris en charge depuis longtemps.
En conséquence, le choix s'est porté sur WF. Nous avons ensuite activement utilisé la pile Microsoft (WCF, WPF) et décidé qu'un autre W ne nous ferait pas de mal. Un autre avantage était notre statut de partenaire de développement d'applications Microsoft, qui a permis de développer des produits utilisant les technologies Microsoft. Eh bien, en général, les capacités du moteur nous convenaient et couvraient presque tous nos cas.

Quel est le problème avec WF?


Après 6 ans d'utilisation de WF, nous avons accumulé un certain nombre de problèmes et le coût de résolution de ces problèmes était trop élevé. Nous avons commencé à penser à développer notre propre moteur. Je vais vous parler de certains d'entre eux.

Diagnostics coûteux et corrections de bogues


Des années ont passé, le nombre d'installations de produits et la charge ont augmenté. Des bogues ont commencé à apparaître, dont le diagnostic et la correction ont demandé beaucoup de ressources. Cela a été facilité par un ensemble de raisons: le manque de compétences, les erreurs de conception lors de l'intégration du moteur précédent et les fonctionnalités de WF.
Nous avions suffisamment de compétences de base pour construire dans WF DirectumRX, le même niveau était suffisant pour faire face à de simples bugs. Pour les cas complexes, les compétences manquaient - l'analyse des journaux, l'analyse de l'état de l'instance, etc., étaient difficiles.
Il était possible d'envoyer une personne à des cours sur WF, mais on ne leur apprenait guère comment analyser l'état d'une instance et associer son changement à des journaux. Et franchement, personne n'avait un désir particulier de mettre à niveau leurs compétences avec une technologie pratiquement morte.
Une autre façon consiste à embaucher une personne possédant les compétences appropriées. Mais en trouver un à Izhevsk n'est pas une tâche si banale, et ce n'est pas le fait que son niveau soit suffisant pour résoudre nos problèmes.
En fait, nous sommes confrontés à un seuil d'entrée élevé pour prendre en charge WF. D'une manière ou d'une autre, je pense que nous réglerions ce problème, sinon pour un certain nombre d'autres raisons.
Un autre problème était que lors de la construction de diagrammes de processus, nous utilisons notre propre notation. Il est plus visuel et plus facile à développer. Par exemple, WF ne permet pas d'implémenter un graphique à part entière, vous ne pouvez pas dessiner de blocs sans issue, il existe des fonctionnalités de dessin de branches parallèles. Le retour sur investissement est la conversion de nos circuits en circuits WF, qui ne sont pas si simples et imposent un certain nombre de limitations. Lors du débogage, j'ai dû analyser l'état du circuit WF, à cause de cela, la visibilité a été perdue, j'ai dû comparer les blocs et les visages pour comprendre à quelle étape se trouvait l'instance.
image

Représentation du circuit dans DirectumRX
image

Représentation du circuit en WF
De plus, nous étions confrontés au fait que la documentation WF décrit mal le référentiel d'instances. Comme je l'ai écrit ci-dessus, cela est nécessaire lors de l'analyse d'un bogue afin de comprendre l'état de l'instance de processus. De plus, une partie des données est cryptée, ce qui interfère également avec l'analyse.

Postgres en tant que SGBD


Depuis de nombreuses années, il existe une tendance en Russie à la substitution des importations, et de plus en plus souvent l'une des exigences de la plate-forme est le support des systèmes de gestion de base de données open source (SGBD) ou SGBD de production nationale. Le plus souvent, c'est Postgres. Par défaut, WF ne prend en charge que MS SQL. Pour travailler avec d'autres bases de données, vous pouvez utiliser des fournisseurs tiers. Nous avons choisi dotConnect de DevArt.
Alors que la charge était légère, tout fonctionnait bien. Mais dès que nous avons conduit le système sous charge, des problèmes sont apparus. WF peut soudainement arrêter et arrêter les instances de traitement (transactions préparées terminées), ou tous les messages sont envoyés à la file d'attente empoisonnée MSMQ, etc. Nous avons réglé tous ces problèmes, mais nous y avons consacré beaucoup de temps. Rien ne garantit qu'un nouveau n'apparaîtra pas, dont la solution devra dépenser le même montant.

Care on .net core


Après l'annonce de .Net Core par Microsoft, nous avons décidé d'y aller progressivement afin de réaliser la multiplateforme de nos solutions. Microsoft a décidé de ne pas intégrer WF, ce qui nous a empêché de transférer le module Workflow vers .Net Core sous la forme dans laquelle il existait. Nous sommes conscients qu'il existe des ports WF non officiels sur .Net Core, et parmi eux, il y en a même de développeurs WF, mais tous ne sont pas 100% compatibles. Un autre facteur a été le refus de Microsoft de développer .Net. en faveur de .Net Core.

Nouveau moteur


Prenant tout ce tas de problèmes, d'options de solutions, la complexité du refactoring et des corrections, pesant tous les avantages et les inconvénients, nous avons décidé de passer à un nouveau moteur. Nous avons commencé par analyser celles existantes.

Le choix


Les principales exigences lors du choix d'un moteur étaient:
  • travailler sur .Net Core;
  • évolutivité
  • conversion des instances de processus existantes, avec la possibilité de poursuivre l'exécution après la conversion
  • coût raisonnable de l'analyse des problèmes existants
  • travailler avec différents SGBD

De plus, il était nécessaire que l'activité (activité) puisse exécuter le code d'application en C #, il était possible de déboguer des blocs, etc.
Dans le cadre de l'analyse des moteurs existants, nous avons examiné:
  1. Core wf
  2. Flowwright
  3. Flux de travail K2
  4. Noyau de workflow
  5. Zeebe
  6. Moteur de workflow
  7. Cadre de travail durable
  8. Camunda
  9. Activités à Orléans

Après avoir imposé toutes les exigences sur les solutions revues et ajouter le coût des solutions payantes, nous avons considéré que notre moteur n'est pas très cher, alors qu'il sera 100% adapté à nos demandes et qu'il sera facile à affiner.

Implémentation / Architecture


Dans l'implémentation précédente, le module WF était un service WCF auquel les bibliothèques WF étaient connectées. Il a pu créer des instances de processus, démarrer des processus, exécuter des blocs, y compris la logique métier (code écrit par les développeurs). Tout cela a été hébergé dans l'application IIS.
Dans la nouvelle implémentation, suivant la tendance de l'architecture de microservices, nous avons décidé de diviser immédiatement le service en deux: Workflow Process Service (WPS) et Workflow Block Service (WBS), qui pourraient être hébergés séparément. Un autre maillon de cette chaîne est le service d'application, qui implémente le système DirectumRX et la logique métier, et les clients travaillent avec lui.
WPS «marche» selon le schéma, WBS traite les blocs et exécute la logique métier à chaque étape. La commande pour démarrer le processus provient du serveur d'applications. L'interaction entre les services est réalisée à l'aide de RabbitMQ. Ci-dessous, je vais vous en dire plus sur chacun d'eux.


Wps


Workflow Process Service est un microservice qui est responsable du démarrage des processus et du contournement du diagramme de processus.
Le référentiel de services contient des diagrammes de processus avec prise en charge du contrôle de version et de l'état sérialisé des instances de processus. Vous pouvez utiliser MS SQL et Postgres comme stockage.
Le service est capable de traiter les messages reçus d'autres services via RabbitMQ. Essentiellement, les messages sont une API de service. Types de messages que le service peut recevoir:
  • StartProcess - créez une nouvelle instance de processus et lancez une analyse dessus;
  • CompleteBlock - achèvement du bloc, après ce message, le service déplace l'instance de processus plus loin dans le schéma;
  • Suspend / ResumeProcess - suspendre l'exécution d'une instance de processus, par exemple, en raison d'une erreur lors du traitement d'un bloc, et reprendre l'exécution une fois l'erreur corrigée;
  • Abort / RestartProcess - arrêtez l'exécution de l'instance de processus et redémarrez-la;
  • DeleteProcess - supprime une instance de processus.

Le schéma se compose de blocs et de connexions entre eux (faces). Chaque face a un identifiant, le "résultat d'exécution". Il existe 5 types de blocs:
  • StartBlock
  • Bloquer
  • OrBlock;
  • AndBlock;
  • FinishBlock.

image

Vue du schéma WPS
Lorsqu'un message arrive au début du processus, le service crée une instance et commence à "marcher" selon le schéma. La classe responsable de la «marche» selon le schéma, nous appelons en plaisantant le «Stepator». Un circuit commence toujours par un StartBlock. Ensuite, le strider prend tous les visages sortants et les active. Chaque bloc fonctionne sur le principe du bloc «ET», c'est-à-dire tous les visages entrants doivent être actifs pour que le bloc puisse être activé. L'algorithme décide alors quels blocs peuvent être activés et envoie un message WBS pour activer ces blocs. WBS traite le bloc et renvoie le résultat du WPS. En fonction du résultat de l'exécution, le strider sélectionne les faces appropriées sortant du bloc pour l'activation, et le processus se poursuit.
Pendant le développement, nous sommes tombés sur des situations intéressantes liées aux connexions cycliques entre les blocs, ce qui a ajouté de la logique au moment de décider quel bloc activer / arrêter.
Le service est autonome, c'est-à-dire passez-lui simplement le schéma au format Json, écrivez votre propre gestionnaire de blocs et vous pourrez échanger des messages.

Wbs


Workflow Block Service est un service qui traite les diagrammes de blocs. Le service connaît l'essence de la logique métier, telle que tâche, tâche, etc. Ces entités peuvent être ajoutées à l'environnement de développement DirectumRX Development Studio (DDS). Par exemple, nos blocs ont un événement pour démarrer le bloc. Le code de ce gestionnaire d'événements est écrit par le développeur dans DDS et WBS exécute ce code. En fait, c'est notre implémentation du gestionnaire de blocs; vous pouvez le remplacer par le vôtre.
Le service stocke l'état des blocs. En plus des propriétés de base (Id, State), le bloc peut contenir d'autres informations nécessaires à l'exécution / terminaison / suspension du bloc.
Les blocs peuvent être dans un état:
  • Terminé - passe dans cet état après l'achèvement réussi des travaux sur le bloc;
  • En attente - est dans un état d'attente lorsqu'un travail est effectué dans le bloc, par exemple, une sorte de réponse est requise de la part de l'utilisateur;
  • Abandonné - passe dans cet état lorsque le processus est arrêté;
  • Suspendu - passe dans cet état lorsque le processus s'arrête lorsqu'une erreur se produit.

Lorsqu'un message arrive pour exécuter le bloc, le bloc est exécuté et WBS envoie un message avec le résultat du bloc.

Évolutivité


WPS et WBS peuvent être déployés dans plusieurs instances. À un moment donné, un seul service WPS peut traiter une instance de processus. Il en va de même pour le traitement des blocs - une instance de processus ne peut traiter qu'un seul bloc à la fois. Ceci est aidé par des verrous qui sont mis sur le processus pendant le traitement. S'il y a plusieurs messages dans la file d'attente pour le traitement d'un processus / blocs dans un processus, le message est différé pendant un certain temps. En même temps, chaque service peut effectuer simultanément des travaux sur plusieurs instances de processus.
Une situation peut survenir lorsque plusieurs messages arrivent dans un processus après l'autre pour traiter des blocs (branches parallèles). Pour réduire le nombre de situations où vous devez reporter des messages, WBS prend plusieurs messages à la fois et les exécute les uns après les autres, en contournant l'envoi à la file d'attente pour une exécution répétée en raison du blocage du processus.

Conversion


Après la transition vers un nouveau moteur, la question s'est posée, que faire des instances de processus existantes? L'option privilégiée était leur conversion, afin qu'ils continuent à travailler sur le nouveau moteur. Les avantages sont évidents: nous ne prenons en charge qu'un seul moteur, les problèmes de prise en charge de l'ancien moteur disparaissent (voir ci-dessus). Mais il y avait des risques que nous ne puissions pas entièrement comprendre comment obtenir les données dont nous avons besoin à partir d'instances de processus sérialisées. Il y avait aussi un repli: donner aux instances existantes la possibilité de finaliser sur l'ancien moteur et d'en lancer de nouvelles sur un nouveau. Les inconvénients de cette option proviennent des avantages de la précédente, plus des ressources supplémentaires sont nécessaires pour faire tourner les deux moteurs.
Pour la conversion, nous devions prendre l'ancien état du processus au format WF et générer les états des processus et des blocs. Nous avons écrit un utilitaire qui a pris l'état sérialisé d'une instance de processus dans la base de données, en a extrait une liste de blocs actifs, les résultats d'exécution pour les visages et a virtuellement exécuté le processus. Par conséquent, nous avons obtenu l'état de l'instance au moment de la conversion.
Des difficultés sont apparues sur la façon de désérialiser correctement les données d'instance de processus dans WF. L'état de l'instance de processus (instance) de WF est stocké dans la base de données en tant que xaml. Nous n'avons pas pu trouver une description claire de la structure de ce xaml, nous avons dû aller jusqu'au bout empiriquement. Analysé les données manuellement et extrait les informations dont nous avions besoin. Dans le cadre de cette tâche, nous avons élaboré une autre option: utiliser des outils WF pour désérialiser l'état de l'instance et essayer d'obtenir des informations à partir d'objets. Mais du fait que la structure de tels objets était très complexe, nous avons abandonné cette idée et avons opté pour une analyse «manuelle» de xaml.
En conséquence, la conversion a réussi et toutes les instances de processus ont commencé à être traitées par le nouveau moteur.

Conclusion


Alors qu'est-ce que le moteur Workflow nous a apporté? En fait, nous avons réussi à vaincre tous les problèmes évoqués au début de l'article:
  • Le moteur est écrit en .NET Core;
  • il s'agit d'un service auto-hébergé indépendant d'IIS;
  • en tant qu'opération de test, nous utilisons activement le nouveau moteur dans le système d'entreprise et avons réussi à faire en sorte que l'analyse des bogues prenne beaucoup moins de temps;
  • effectué des tests de charge sur Postgres, selon les données préliminaires, le bundle WPS + WBS supporte sans problème la charge de 5000 utilisateurs simultanés;
  • et bien sûr, comme tout travail intéressant, c'est une expérience intéressante.

En prime, nous avons obtenu un code clair et pris en charge que nous pouvons adapter à nous-mêmes.
Le coût du moteur s'est avéré comparable à ce que nous aurions à dépenser pour l'achat / l'adaptation d'un produit tiers. Pour le moment, nous pensons que la décision de développer votre propre moteur s'est avérée justifiée.
Nous attendons également des tests de charge pour plus de 10 000 utilisateurs simultanés. Peut-être qu'une certaine optimisation sera nécessaire, ou peut-être qu'elle décollera? ;-)
Nous avons récemment publié DirectumRX 3.2, qui comprenait le nouveau Workflow. Voyons comment le moteur se montrera aux clients.

Source: https://habr.com/ru/post/fr468803/


All Articles