xcraft-core-horde
v3.8.1
Published
Xcraft Horde master
Readme
📘 xcraft-core-horde
Aperçu
Le module xcraft-core-horde est le composant Xcraft chargé de la gestion et de la coordination d'une horde : un ensemble de processus applicatifs (« esclaves » ou slaves) qui collaborent au sein d'une même architecture distribuée. Il permet de démarrer de nouveaux processus, de se connecter à des processus existants, de router les commandes et les événements entre eux, et de surveiller la santé de chaque connexion.
Une horde est un nœud serveur où des services sont déployés. Une horde peut posséder des sous-hordes, formant ainsi un graphe de serveurs à travers lequel commandes et événements circulent selon des règles de routage précises. Un « client » qui se connecte à un serveur principal ne fait en réalité que déclarer ce serveur comme une sous-horde dans sa propre configuration de topologie.
Sommaire
- Aperçu
- Structure du module
- Fonctionnement global
- Exemples d'utilisation
- Interactions avec d'autres modules
- Configuration avancée
- Détails des sources
- Licence
Structure du module
Le module s'organise autour de deux classes principales définies dans lib/index.js :
Slave— Représente une instance d'application connectée à la horde, qu'elle soit démarrée localement (processus daemon) ou simplement connectée à un bus distant déjà actif. Chaque esclave possède sa propre clé de routage et son propreBusClient.Horde— Orchestre l'ensemble des esclaves : cycle de vie, topologie, diffusion des messages et surveillance de la latence des connexions. Le module exporte une instance singleton de cette classe.
En complément, le module expose :
horde.js— Un ensemble de commandes Xcraft (horde.load,horde.reload,horde.slave.add,horde.slave.remove) exposées sur le bus.lib/offlineChecker.js— Un utilitaire (OfflineChecker) permettant à un acteur Goblin de surveiller la connectivité d'une horde spécifique.config.js— La définition des options configurables viaxcraft-core-etc.
Fonctionnement global
Concept de Horde et de Tribus
Chaque application Xcraft peut être divisée en tribus (tribes) : des instances distinctes du même code, chacune avec sa propre configuration de bus (ports différents). La tribu principale porte le numéro 0.
La clé de routage (routingKey) d'un esclave suit le format :
{hordeId}pour la tribu principale,{hordeId}-{tribe}pour les tribus numérotées.
Le module permet de :
- démarrer de nouveaux processus esclaves via xcraft-core-daemon ;
- se connecter à des esclaves déjà en cours d'exécution via leur configuration de bus ;
- diffuser des messages entre esclaves (broadcast, fwcast, unicast) ;
- surveiller en continu l'état de santé et la latence de chaque connexion.
Communication entre esclaves
La communication repose sur xcraft-core-bus et xcraft-core-transport. Trois modes de transmission sont disponibles :
- Broadcast — Diffuse un message à tous les esclaves, à l'exception de l'émetteur. Un routage par ligne (
Router.extractLineId) restreint la diffusion aux seuls esclaves concernés lorsque le topic référence une ligne spécifique. - Fwcast (forward cast) — Transmet un message à un esclave précis, identifié par sa clé de routage.
- Unicast — Envoie un message à un esclave précis via le routeur axon associé à un
orcName.
Chaque esclave connecté s'abonne à l'ensemble des topics (['*::*']) via son BusClient, sauf s'il fonctionne en mode noForwarding. Le gestionnaire catchAll reçu sur ce BusClient agit comme un proxy : il réachemine les messages de commande (.finished, .error) et les événements orcishés (.orcished) vers la horde locale via fwcast ou unicast, puis retombe sur un broadcast si aucune des deux tentatives n'a abouti.
- Le mode
noForwardingsignale que l'esclave ne doit pas agir comme proxy automatique : les informations de forwarding sont alors ajoutées au message pour permettre un routage explicite côté horde parente. - Le mode
passiverestreint la transmission aux seuls événements de commandes, aux événements.orcishedet aux appels RPC (_xcraftRPC), en ignorant les autres événements tant que l'esclave n'est pas connecté.
Surveillance et résilience
Chaque esclave connecté est surveillé par un intervalle d'une seconde qui mesure la latence via performance.now() et busClient.events.lastPerf(). Un événement greathall::<perf> est émis avec le payload suivant :
{
horde: string, // Identifiant de la horde
delta: number, // Latence en millisecondes
lag: boolean, // Présence de latence
overlay: boolean, // Affichage de l'overlay demandé
noSocket: boolean, // Connexion perdue
reason: string // Raison de l'erreur de connexion
}Seuils appliqués :
- < 1000 ms — Fonctionnement normal ; l'événement
lag: falsen'est émis qu'une seule fois, au moment du retour à la normale. - 1000 – 10 000 ms — Latence signalée, sans overlay.
- ≥ 10 000 ms — Latence critique ; l'overlay est activé si
connection.useOverlayest vrai. - ≥
maxLagDeltaTime(20 000 ms par défaut, réglable viasetMaxLagDeltaTime) — Le socket push de l'esclave est détruit pour préparer une reconnexion, sauf en environnementNODE_ENV=developmentou si l'option de topologieoptimistLagest active.
Si l'esclave n'a plus de socket (lastPerf < 0), la dernière latence connue est conservée (prevPerf) et reason reprend la dernière erreur de connexion (lastErrorReason) du BusClient.
Chargement de la topologie
Lors de l'appel à autoload, le module parcourt la liste des hordes déclarées dans la configuration et, pour chacune :
- si la topologie définit des
tribespour cette horde, il utilise_loadTribesafin de se connecter à toutes les tribus de l'application courante (à l'exception de la tribu courante) — l'horde qui gère sa propre distribution de tribus devient alors le « tribe dispatcher » (isTribeDispatcher) ; - sinon, il utilise
_loadSingleafin de démarrer ou de se connecter à la tribu principale (0), puis aux tribus supplémentaires éventuellement déclarées dans la configuration de bus de l'esclave.
La topologie peut être exprimée sous forme de chaîne JSON ou d'objet (contrainte imposée par Inquirer, qui ne gère que des chaînes) ; elle peut également être surchargée au démarrage via l'argument --topology grâce à modules.mergeOverloads de xcraft-core-utils.
Exemples d'utilisation
Chargement automatique des hordes
const horde = require('xcraft-core-horde');
// resp est l'objet de réponse Xcraft disponible dans une quête ou une commande
await horde.autoload(resp);Ajout d'un esclave via le bus
// Depuis une quête Goblin, ajoute un esclave pour l'application 'myApp'
const {slaveId} = yield this.quest.cmd('horde.slave.add', {appId: 'myApp'});
// Puis, pour le retirer :
yield this.quest.cmd('horde.slave.remove', {pid: slaveId});Note :
Horde.add()est également utilisable directement en interne (c'est ce que faitautoload), mais depuis une commande du bus il est préférable de passer parhorde.slave.add/horde.slave.removecomme ci-dessus.
Envoi de messages entre esclaves
const horde = require('xcraft-core-horde');
// Diffuser à tous les esclaves (sauf l'émetteur)
horde.broadcast('sourceSlaveId', 'mon.topic', {data: 'Hello world'});
// Transmettre à un esclave spécifique via sa clé de routage
horde.fwcast('myApp-0', 'mon.topic', {data: 'Message ciblé'});
// Envoyer via orcName
horde.unicast('mon.topic', {data: 'Message pour un orc'}, 'monOrcName');Rechargement des hordes via le bus
// Depuis une quête Goblin
yield this.quest.cmd('horde.reload');Surveillance de la connectivité d'une horde depuis un acteur
const OfflineChecker = require('xcraft-core-horde/lib/offlineChecker.js');
// Dans le constructeur/init d'un acteur Goblin
const checker = new OfflineChecker(quest, 'myApp', async (isConnected) => {
if (isConnected) {
quest.log.info('myApp est de nouveau en ligne');
} else {
quest.log.warn('myApp est hors ligne');
}
});Interactions avec d'autres modules
- xcraft-core-bus — Notification des changements de registre de commandes, de token et de reconnexion ; obtention du token courant.
- xcraft-core-busclient — Création des
BusClientpour la connexion aux bus esclaves ; accès au client global (getGlobal()) pour l'émission d'événements de diffusion et de performance. - xcraft-core-transport — Résolution des routeurs axon pour le mode unicast et extraction des identifiants de ligne pour le broadcast ciblé.
- xcraft-core-etc — Chargement de la configuration du module ainsi que de la configuration de bus (
xcraft-core-bus) de chaque esclave. - xcraft-core-daemon — Démarrage des processus esclaves en tant que daemons détachés.
- xcraft-core-host — Accès aux informations de l'application hôte (
appId,appArgs,variantId,appData,appCompany,appConfigPath,projectPath). - xcraft-core-utils — Fusion de la topologie fournie en ligne de commande (
modules.mergeOverloads). - xcraft-server — Initialisation de l'environnement (
init-env.js#initEtc) pour chaque esclave démarré.
Configuration avancée
Le fichier config.js définit les options suivantes, exploitées par xcraft-core-etc :
| Option | Description | Type | Valeur par défaut |
| ----------------------- | --------------------------------------------------------------------- | --------------- | ----------------- |
| hordes | Liste des hordes à charger automatiquement | Array | [] |
| topology | Configuration JSON de la topologie des hordes (hôtes, ports, tribus…) | String/Object | '' |
| autoload | Charge automatiquement la topologie au démarrage | Boolean | true |
| connection.useOverlay | Active l'affichage d'une superposition UI en cas de déconnexion | Boolean | true |
Variables d'environnement
| Variable | Description | Exemple | Valeur par défaut |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------- |
| NODE_ENV | Environnement d'exécution ; en development, la destruction automatique du socket en cas de lag important est désactivée | development | — |
| GOBLINS_APP | Identifiant temporaire de l'application Goblins positionné le temps de résoudre la configuration de bus d'un esclave (Slave.busConfig) | myApp@variant | — |
Détails des sources
horde.js
Fichier de commandes Xcraft exposées sur le bus, toutes déclarées en mode parallel :
horde.load— Appellehorde.autoload(resp). Émethorde.load.{id}.finisheden cas de succès, ouhorde.load.{id}.error(aveccode,message,stack) en cas d'échec.horde.reload— Appelle successivementhorde.unload(resp)puishorde.autoload(resp). Émethorde.reload.{id}.finishedouhorde.reload.{id}.error.horde.slave.add— Ajoute un esclave pour l'appIdfourni dans les données du message. Retourne{slaveId}viahorde.slave.add.{id}.finished.horde.slave.remove— Supprime l'esclave identifié par le paramètre requispid(slaveId). Émethorde.slave.remove.{id}.finished(sans payload) ouhorde.slave.remove.{id}.error.
lib/index.js
Contient l'implémentation principale du module : les classes Slave et Horde, ainsi que l'instance singleton exportée.
Classe Slave
Hérite d'EventEmitter. Représente un esclave, qu'il s'agisse d'un processus démarré localement via xcraft-core-daemon ou d'une simple connexion vers un serveur déjà actif.
Propriétés
id— PID du processus daemon, ou UUID généré (_name) si l'esclave n'a pas de daemon associé.horde— Identifiant de la horde (hordeId).routingKey— Clé de routage :{hordeId}ou{hordeId}-{tribe}.commands— Registre des commandes disponibles sur cet esclave.busClient— InstanceBusClientutilisée pour la communication.isDaemon—truesi l'esclave a été démarré via un daemon local.isConnected— État courant de la connexion au bus.isPassive—truesi l'esclave fonctionne en mode passif.noForwarding—truesi l'esclave ne doit pas réacheminer automatiquement les messages.tribe— Numéro de la tribu.totalTribes(setter uniquement) — Nombre total de tribus de la horde.lastErrorReason— Dernière raison d'erreur rapportée par leBusClient.
Méthodes publiques
connect(busConfig)— Connecte l'esclave à un bus existant. Instancie leBusClient(abonné à['*::*']sauf en modenoForwarding), relaie les événementscommands.registry,token.changed,orcname.changed,reconnectetreconnect attempt, puis installe un gestionnairecatchAllqui agit comme proxy de réacheminement (fwcast/unicast avec repli sur broadcast) pour tous les messages reçus, à l'exception des topicsgreathall::*et des messages déjà diffusés (_xcraftBroadcasted).start()— Démarre un nouveau processus esclave via xcraft-core-daemon, avec les arguments--appet--tribe(et--total-tribessi plusieurs tribus sont configurées). Tente de lire le fichier de configuration du bus toutes les 5 secondes, jusqu'à 10 tentatives, avant d'appelerconnect. Lève une erreur si la tribu n'est pas définie.stop(shutdown)— Arrête l'esclave proprement : retire tous les listeners, envoie la commandeshutdownau bus distant sishutdownest vrai et que l'esclave n'est pas en modenoForwarding, puis arrête le daemon local le cas échéant.busConfig(pid)— Construit la configuration de bus pour un PID donné, en initialisant temporairementGOBLINS_APPet en appelantinitEtcde xcraft-server avec le bon chemin d'application/variante.
Événements émis
commands.registry— Le registre de commandes de l'esclave a été mis à jour.token.changed— Le token d'authentification duBusClienta changé.orcname.changed— L'orcNameassocié a changé.reconnect— Reconnexion réussie au bus distant.reconnect attempt— Une tentative de reconnexion est en cours.
Classe Horde
Gère l'ensemble des esclaves via une Map privée (_slaves), les intervalles de surveillance de latence (_deltaInterval) et les promesses de connexion passive (#connects).
Propriétés
routingKey— Clé de routage de la horde courante (dépend de la tribu locale).commands— Registre complet de tous les esclaves, y compris ceux en modenoForwarding.public— Registre des seuls esclaves qui ne sont pas en modenoForwarding.config— Configuration chargée depuis xcraft-core-etc.isTribeDispatcher—truesi cette horde a la charge de distribuer les tribus de son propreappId.busClient— Objet exposantcommand.send(routingKey, cmd, msg), qui enrichit le message (ARP,route, prioriténice,router) avant de le transmettre à l'esclave correspondant.
Méthodes publiques
setMaxLagDeltaTime(delta=20000)— Définit le seuil de latence (en ms) au-delà duquel le socket push d'un esclave est détruit.autoload(resp)— Charge toutes les hordes déclarées dans la configuration, en tenant compte de leur topologie (tribus ou mode simple). Ne fait rien si aucune horde n'est configurée.waitAutoload(timeout=5000)— Attend la résolution des connexions passives en attente, avec un intervalle de vérification de 200 ms. Retourne dès qu'une erreur de connexion est détectée sur un esclave, afin d'éviter une attente inutile lorsque le serveur distant est inaccessible.add(slave, horde, busConfig)— Ajoute un esclave à la horde. Si unebusConfigest fournie (ou trouvée dans la topologie), connecte l'esclave existant et met en place la surveillance de latence ; sinon, démarre un nouveau processus viaslave.start(). Notifie le registre de commandes viaxBus.notifyCmdsRegistry().remove(id, resp)— Retire un esclave : nettoie son intervalle de surveillance, retire ses listeners et appelleslave.stop(false).broadcast(hordeId, topic, msg)— Diffuse un message à tous les esclaves sauf celui d'origine (hordeId), en tenant compte d'un éventuel filtrage par ligne (Router.extractLineId) et en ignorant les esclaves passifs non connectés.fwcast(routingKey, topic, msg)— Transmet un message à l'esclave correspondant à la clé de routage donnée. Retournetrueen cas de succès,falsesinon.unicast(topic, msg, orcName?)— Envoie un message via le routeur axon associé à l'orcName(déduit demsg.orcNamesi non fourni). Retournetrueen cas de succès.stop(all)— Arrête tous les esclaves (envoi deshutdownsiallest vrai ou si l'esclave est un daemon local) et nettoie tous les intervalles de surveillance.unload(resp)— Décharge tous les esclaves en appelantremovepour chacun d'eux.getSlaves()— Retourne la liste des clés de routage de tous les esclaves actifs.getTribe(routingKey)— Retourne le numéro de tribu associé à une clé de routage, ou-1si non trouvée.getSlave(routingKey)— Retourne l'instanceSlaveassociée à une clé de routage, ou-1si non trouvée.isNoForwarding(hordeId)— Indique si l'esclave associé à la horde donnée fonctionne en modenoForwarding.hasSyncing(hordeId)— Indique si la synchronisation est activée pour la horde donnée (absence de l'optionnoSyncdans la topologie).
Le module exporte une instance singleton de Horde (module.exports = new Horde()) ainsi que la classe elle-même (module.exports.Horde = Horde), permettant d'instancier une horde alternative si nécessaire.
lib/offlineChecker.js
Utilitaire permettant à un acteur Goblin de surveiller l'état de connectivité d'une horde spécifique, sans avoir à gérer directement l'abonnement à l'événement de performance.
Classe OfflineChecker
Constructeur
constructor(quest, hordeId, callback)— Initialise l'état de connexion courant à partir dexHorde.getSlave(hordeId), puis souscrit (viaquest.sub.localetquest.goblin.deferpour la désinscription automatique) à l'événementgreathall::<perf>. Lecallback(asynchrone) reçoit un booléen —truesi la horde vient de se reconnecter,falsesi elle vient de se déconnecter — et n'est invoqué que lors d'une transition d'état, jamais de façon répétée.
La détection s'appuie sur le champ noSocket du payload : si noSocket vaut true, la horde est considérée hors ligne. Les événements marqués syncing, ou concernant une autre horde que celle surveillée, sont ignorés.
Licence
Ce module est distribué sous licence MIT.
Ce contenu a été généré par IA
