Skip to Content
Mélodium 0.10.3 is now available!
DocsExemples10. Calcul distribué

Calcul distribué

Source: tutorial/10_distributed_computation (répertoire) See in Playground

Cet exemple clôt le parcours du tutoriel. Tous les exemples précédents s’exécutaient dans un seul processus ; distribute est le mécanisme qui répartit un calcul sur plusieurs moteurs, celui sur lequel s’appuient les exemples vitrine pour passer à l’échelle. Un client local envoie un flux de nombres à un second moteur Mélodium, exécuté séparément ; ce moteur double chaque nombre et le renvoie.

Contrairement à chaque autre étape du tutoriel, cet exemple est un seul fichier .mel autonome, distributed_computation.mel, exécuté directement avec melodium run. Il n’y a pas de Compo.toml.

Note

Nécessite deux terminaux, chacun exécutant son propre moteur Mélodium, partageant le même groupe de distribution (une variable d’environnement MELODIUM_GROUP_ID) et une paire de clés d’authentification correspondantes.

Exécution

Les deux terminaux doivent exporter la même variable MELODIUM_GROUP_ID. Les deux clés sont inversées entre eux : la clé d’envoi d’un côté correspond à la clé de réception de l’autre. Les UUID ci-dessous sont des valeurs d’exemple pour un appairage local uniquement, n’importe quelle paire correspondante fonctionne tant que les deux terminaux sont d’accord.

Terminal 1, le moteur à l’écoute :

cd tutorial/10_distributed_computation export MELODIUM_GROUP_ID=10101010-1010-1010-1010-101010101010 melodium dist --localhost --port 6789 \ --recv-key 11111111-1111-1111-1111-111111111111 \ --send-key 22222222-2222-2222-2222-222222222222

Terminal 2, ce script, avec les clés inversées :

cd tutorial/10_distributed_computation export MELODIUM_GROUP_ID=10101010-1010-1010-1010-101010101010 melodium run distributed_computation.mel --port 6789 \ --send_key 11111111-1111-1111-1111-111111111111 \ --recv_key 22222222-2222-2222-2222-222222222222
info: distrib: connected to remote engine info: doubled: 6 info: doubled: 6 info: doubled: 6 info: doubled: 6 info: doubled: 6

--localhost utilise un certificat embarqué prévu pour les tests locaux. Le moteur à l’écoute doit être démarré en premier : main n’envoie des données qu’une fois la connexion confirmée comme prête, mais cette tentative de connexion ne peut aboutir que si le moteur à l’écoute est déjà en train de l’accepter.

Optionnel : ajoutez --api-report et un jeton d’API (MELODIUM_API_TOKEN) pour voir la trace complète de cette exécution sur Cadence.CI.

Fonctionnement

Un seul modèle DistributionEngine identifie le traitement distant à exécuter et sa version, pas une ressource réseau :

model Doubler() : DistributionEngine { treatment = "distributed_computation::double" version = "0.1.0" }

C’est différent de modèles comme SqlPool ou HttpServer : la cible réseau elle-même est fournie séparément, via une valeur Access transmise à start. double, le traitement exécuté sur le moteur distant, est défini dans le même fichier :

treatment double() input n: Stream<i64> output n: Stream<i64> { doubled: add<i64>() Self.n -> doubled.a Self.n -> doubled.b doubled.sum -> Self.n }

Le flux de données global traverse le réseau dans les deux sens :

Se connecter au moteur distant

work/access::|new_access construit une valeur Access (IP, port, et les deux clés d’authentification) entièrement à partir de paramètres, sans aucun service cloud, juste un second processus Mélodium accessible sur le réseau. start ouvre ensuite la connexion :

treatment main( const port: u16 = 6789, const send_key: string, const recv_key: string, const amount: u128 = 5, const value: i64 = 3 ) model distributor: Doubler() { startup() accessBlock: emit<Access>(value=|new_access([|from_ipv4(|localhost_ipv4())], port, send_key, recv_key)) startup.trigger -> accessBlock.trigger distribStart: start[distributor=distributor](params=|map([])) accessBlock.emit -> distribStart.access distribErr: logError(label="distrib") distribFailed: logErrorMessage(label="distrib", message="could not connect to the remote engine") distribStart.error -> distribErr.message distribStart.failed -> distribFailed.trigger logReady: logInfoMessage(label="distrib", message="connected to remote engine") distribStart.ready -> logReady.trigger run[distributor=distributor](amount=amount, value=value) distribStart.ready -> run.trigger }

L’ordre des paramètres de |new_access est (ip, port, remote_key, self_key) : remote_key est l’identité présentée vers l’extérieur, la send_key locale, et self_key est ce qui est vérifié contre ce qui revient, la recv_key locale. Ce n’est qu’une fois distribStart.ready déclenché que run construit et envoie effectivement des données : rien ne devance la mise en place de la connexion. Avec les valeurs par défaut (--amount 5 --value 3), le client envoie cinq copies de 3 et journalise doubled: 6 cinq fois.

Envoyer et recevoir à travers le réseau

dispatchDouble illustre la forme générale pour “exécuter ceci comme un traitement local, mais à distance” : distribute alloue un distribution_id pour un échange, puis sendStream et recvStream, portant le même name, transportent les données réelles dans les deux sens :

treatment run[distributor: DistributionEngine](const amount: u128, const value: i64) input trigger: Block<void> { length: emit<u128>(value=amount) numbers: generate<i64>(data=value) Self.trigger -> length.trigger,emit -> numbers.length,stream -> dispatch.n dispatch: dispatchDouble[distributor=distributor]() logResult: logInfos(label="doubled") asText: toString<i64>() dispatch.n -> asText.value,into -> logResult.messages } treatment dispatchDouble[distributor: DistributionEngine]() input n: Stream<i64> output n: Stream<i64> { startTrigger: trigger<i64>() dist: distribute[distributor=distributor]() Self.n -> startTrigger.stream,start -> dist.trigger send: sendStream<i64>[distributor=distributor](name="n") recv: recvStream<i64>[distributor=distributor](name="n") dist.distribution_id -> send.distribution_id dist.distribution_id -> recv.distribution_id Self.n -> send.data recv.data -> Self.n }

C’est la poignée de main en trois étapes pour un appel distant : allouer un identifiant, envoyer l’entrée, recevoir la sortie, le tout étiqueté par le nom du port ("n" ici) afin que plusieurs flux puissent traverser la même connexion sans ambiguïté. Vu depuis ses propres entrées et sorties, dispatchDouble fait apparaître l’appel au traitement distant double exactement comme une connexion à un traitement local.

Dépendances

Il n’y a pas de Compo.toml pour cet exemple : en tant que script autonome, distributed_computation.mel déclare ses dépendances directement dans son propre en-tête :

#!/usr/bin/env melodium #! name = distributed_computation #! version = 0.1.0 #! require = std:0.10.* net:0.10.* work:0.10.* distrib:0.10.*
  • std : flux de base, journalisation, structures de données
  • net : utilitaires d’adresses IP
  • work : description de l’accès réseau au moteur distant
  • distrib : exécution de traitements sur un moteur distant et câblage de leurs flux