Pourquoi les flux dans Orleans ?
Il existe déjà un large éventail de technologies permettant de créer des systèmes de traitement de flux. Il s’agit notamment de systèmes permettant de stocker durablement des données de flux (p. ex., Event Hubs et Kafka) et de systèmes permettant d’exprimer des opérations de calcul sur des données de flux (p. ex., Azure Stream Analytics, Apache Storm et Apache Spark Streaming). Ce sont d’excellents systèmes qui vous permettent de créer des pipelines de traitement de flux de données efficaces.
Limitations des systèmes existants
Toutefois, ces systèmes ne conviennent pas pour un calcul affiné de forme libre sur les données de flux. Les systèmes de calcul de streaming mentionnés ci-dessus vous permettent de spécifier un graphique unifié de flux de données des opérations appliquées de la même façon à tous les éléments de flux. Il s’agit d’un modèle puissant quand les données sont uniformes et que vous souhaitez exprimer le même ensemble d’opérations de transformation, de filtrage ou d’agrégation sur ces données. Toutefois, il existe d’autres cas d’usage où vous devez exprimer des opérations fondamentalement différentes sur des éléments de données différents. Dans certains d’entre eux, dans le cadre de ce traitement, vous devez parfois effectuer un appel externe, comme par exemple appeler une API REST arbitraire. Les moteurs de traitement de flux de données unifiés ne prennent pas en charge ces scénarios, les prennent en charge de manière limitée et contrainte, ou manquent d’efficacité dans leur prise en charge. Cela tient au fait qu’ils sont intrinsèquement optimisés pour un grand volume d’éléments similaires, et généralement limités en termes d’expressivité et de traitement. Les flux Orleans ciblent ces autres scénarios.
Motivation
Tout a commencé avec les demandes des utilisateurs d’Orleans de prise en charge du renvoi d’une séquence d’éléments à partir d’un appel de méthode de grain. Comme vous pouvez l’imaginer, ce n’était que la partie émergée de l’iceberg. Ils avaient besoin de beaucoup plus que cela.
Dans un scénario typique pour les flux Orleans, vous avez par exemple des flux par utilisateur et vous souhaitez effectuer un traitement différent pour chaque utilisateur, dans le contexte d’un utilisateur individuel. Nous pouvons avoir des millions d’utilisateurs. Certains d’entre eux sont intéressés par la météo et peuvent s’abonner aux alertes météo pour un emplacement particulier, tandis que d’autres s’intéressent à des événements sportifs ; d’autres encore suivent le statut d’un vol particulier. Le traitement de ces événements nécessite une logique différente, mais vous ne voulez pas exécuter deux instances indépendantes de traitement de flux. Certains utilisateurs s’intéressent uniquement à une action boursière particulière et seulement si une certaine condition externe s’applique, une condition qui ne fait pas nécessairement partie des données de flux (et qui doit donc être vérifiée dynamiquement au moment de l’exécution dans le cadre du traitement).
Les utilisateurs modifient sans cesse leurs intérêts et leurs abonnements à des flux d’événements spécifiques évoluent dynamiquement, de sorte que la topologie de streaming change dynamiquement et rapidement. En plus de cela, la logique de traitement par utilisateur évolue et change également dynamiquement, en fonction de l’état utilisateur et des événements externes. Les événements externes peuvent modifier la logique de traitement pour un utilisateur particulier. Par exemple, dans un système de détection de tricheries à un jeu, quand une nouvelle façon de tricher est découverte, la logique de traitement doit être mise à jour avec la nouvelle règle pour détecter cette nouvelle violation. Cela doit être fait bien sûr sans interrompre le pipeline de traitement en cours. Les moteurs de traitement de flux de données en bloc n’ont pas été conçus pour prendre en charge de tels scénarios.
Il va presque sans dire qu’un tel système doit s’exécuter sur plusieurs ordinateurs connectés au réseau, et non pas sur un seul nœud. Par conséquent, la logique de traitement doit être distribuée de manière évolutive et élastique sur un cluster de serveurs.
Nouvelles exigences
Nous avons identifié 4 exigences de base pour notre système de traitement de flux qui lui permettront de cibler les scénarios ci-dessus.
- Logique flexible de traitement de flux
- Prise en charge des topologies hautement dynamiques
- Précision affinée des flux
- Distribution
Logique flexible de traitement de flux
Nous voulons que le système prenne en charge différentes façons d’exprimer la logique de traitement des flux. Les systèmes existants mentionnés ci-dessus nécessitent que le développeur écrive un graphique de calcul de flux de données déclaratif, généralement en suivant un style de programmation fonctionnel. Cela limite l’expressivité et la flexibilité de la logique de traitement. Les flux Orleans sont indifférents à la façon dont la logique de traitement est exprimée. Elle peut être exprimée sous la forme d’un flux de données (par exemple, en utilisant les extensions réactives (Rx) dans .NET), en tant que programme fonctionnel, en tant que requête déclarative ou dans une logique impérative générale. La logique peut être avec état ou sans état, peut avoir des effets secondaires ou non, et peut déclencher des actions externes. Tout le pouvoir revient au développeur.
Prise en charge des topologies dynamiques
Nous voulons que le système autorise l’évolution dynamique des topologies. Les systèmes existants mentionnés ci-dessus sont généralement limités à des topologies statiques qui sont établies de façon fixe au moment du déploiement et qui ne peuvent pas évoluer au moment de l’exécution. Dans l’exemple suivant d’une expression de flux de données, tout est agréable et simple jusqu’à ce que vous deviez effectuer des modifications.
Stream.GroupBy(x=> x.key).Extract(x=>x.field).Select(x=>x+2).AverageWindow(x, 5sec).Where(x=>x > 0.8) *
Modifiez la condition de seuil dans le filtre Where, ajoutez une instruction Select ou ajoutez une autre branche dans le graphique de flux de données et produisez un nouveau flux de sortie. Dans les systèmes existants, cela n’est pas possible sans supprimer l’intégralité de la topologie et redémarrer le flux de données à partir de zéro. En pratique, ces systèmes marquent d’un point de contrôle le calcul existant et peuvent redémarrer à partir du dernier point de contrôle. Toutefois, un tel redémarrage introduit une interruption et s’avère coûteux pour un service en ligne qui produit des résultats en temps réel. Un tel redémarrage devient particulièrement inadapté dans le cas de l’exécution d’un grand nombre d’expressions avec des paramètres similaires mais différents (par utilisateur, par appareil, etc.) et qui changent continuellement.
Nous voulons que le système permette de faire évoluer le graphique de traitement de flux au moment de l’exécution, en ajoutant de nouveaux liens ou nœuds au graphique de calcul, ou en modifiant la logique de traitement dans les nœuds de calcul.
Précision affinée des flux
Dans les systèmes existants, la plus petite unité d’abstraction correspond généralement à l’ensemble du flux (topologie). Toutefois, un grand nombre de nos scénarios cibles nécessitent un nœud/lien individuel dans la topologie pour constituer une entité logique en soi. De cette façon, chaque entité peut être éventuellement gérée indépendamment. Par exemple, dans la topologie de flux volumineux comprenant plusieurs liens, différents liens peuvent avoir différentes caractéristiques et peuvent être implémentés sur différents transports physiques. Certains liens peuvent passer par des sockets TCP, tandis que d’autres par des files d’attente fiables. Différents liens peuvent avoir différentes garanties de remise. Différents nœuds peuvent avoir différentes stratégies de point de contrôle, et leur logique de traitement peut être exprimée dans différents modèles ou même différents langages. Une telle flexibilité n’est généralement pas possible dans les systèmes existants.
L’argument de flexibilité et d’unité d’abstraction est similaire à une comparaison des architectures orientées services (SoA) et des acteurs. Les systèmes d’acteurs favorisent une plus grande flexibilité, car chaque acteur constitue essentiellement un « petit service » géré indépendamment. De façon similaire, nous voulons que le système de flux permette un contrôle aussi affiné.
Distribution
Bien entendu, notre système doit avoir toutes les propriétés d’un « bon système distribué ». Cela inclut :
- Scalabilité : prend en charge un grand nombre de flux et d’éléments de calcul.
- Élasticité : permet d’ajouter/supprimer des ressources pour croître/diminuer en fonction de la charge.
- Fiabilité : être résilient aux échecs
- Efficacité : utiliser efficacement les ressources sous-jacentes
- Réactivité : activer des scénarios en quasi-temps réel.
Telles étaient les exigences que nous avions à l’esprit pour créer le streaming Orleans.
Clarification : Orleans ne prend actuellement pas directement en charge l’écriture d’expressions de flux de données déclaratives comme dans l’exemple ci-dessus. Les API de streaming Orleans actuelles sont des composants de plus bas niveau, comme cela est décrit ici. Fournir des expressions de flux de données déclaratives est notre objectif futur.