2016-11-24 2 views
5

Je voudrais utiliser le superviseur OTP d'erlang dans une application distribuée que je construis. Mais j'ai du mal à comprendre comment un superviseur de ce type peut surveiller un processus s'exécutant sur un nœud distant. Contrairement à la fonction start_link d'erlang, start_child n'a pas de paramètres pour spécifier le nœud sur lequel l'enfant sera engendré.Un superviseur OTP peut-il surveiller un processus sur un nœud distant?

Est-il possible pour un superviseur OTP de surveiller un enfant distant, et si non, comment puis-je y parvenir en erlang?

+1

Je pense que la méthode recommandée est d'avoir un superviseur sur chaque nœud. – Dogbert

+0

@Dogbert En effet. En fait, pas seulement un superviseur, mais généralement une réplique complète de tout système distribué afin que les demandes de travail puissent être inter-nœuds sans nécessiter de changements drastiques au code. – zxq9

Répondre

3

supervisor:start_child/2 peut être utilisé entre les nœuds.

La raison de votre confusion est juste une confusion au sujet du contexte d'exécution (ce qui est un peu difficile à garder parfois). Il y a trois processus impliqués dans rogue OTP:

  • Le Requestor
  • Le superviseur
  • Le processus Engendré

Le contexte du demandeur est celui dans lequel supervisor:start_child/2 est appelé - pas le contexte du superviseur lui-même. Vous fournirez normalement une interface de superviseur en exportant une fonction qui enveloppe l'appel à supervisor:spawn_child/2:

do_some_crashable_work(Data) -> 
    supervisor:start_child(sooper_dooper_sup, [Data]). 

qui pourrait être défini et exporté à partir du module superviseur, être défini en interne dans une sorte de « gestionnaire » du procédé selon la "service manager/supervisor/workers" idiom, ou peu importe. Dans tous les cas, cependant, un processus autre que le superviseur effectue cet appel.

Maintenant, regardez à nouveau attentivement les documents Erlang pour supervisor:start_child/2 (here, et an R19.1 doc mirror car parfois erlang.org a du mal pour une raison quelconque). Notez que le type sup_ref() peut être un nom enregistré, un pid(), un {global, Name} ou un {Name, Node}. Le demandeur peut se trouver sur n'importe quel nœud appelant un superviseur sur un autre nœud lorsqu'il appelle avec un pid(), un {global, Name} ou un tuple {Name, Node}. Cependant, le superviseur ne se contente pas de lancer les choses au hasard. Il a un child_spec() il part de, et la spécification indique au superviseur ce qu'il faut appeler pour démarrer ce nouveau processus. Ce premier appel dans le module enfant est effectué dans le contexte du superviseur et est une fonction personnalisée. Bien que nous l'appelions généralement quelque chose comme start_link/N, il peut faire ce que nous voulons dans le cadre du démarrage, y compris déclarer un nœud spécifique sur lequel se reproduire. Alors maintenant, nous finissons avec quelque chose comme ceci:

%% Usually defined in the requestor or supervisor module 
do_some_crashable_work(SupNode, WorkerNode, Data) -> 
    supervisor:start_child({sooper_dooper_sup, SupNode}, [WorkerNode, Data]). 

Avec une spécification de l'enfant de quelque chose comme:

%% Usually in the supervisor code 
SooperWorker = {sooper_worker, 
       {sooper_worker, start_link, []}, 
       temporary, 
       brutal_kill, 
       worker, 
       [sooper_worker]}, 

qui indique que le premier appel serait sooper_worker:start_link/2:

%% The exported start_link function in the worker module 
%% Called in the context of the supervisor 
start_link(Node, Data) -> 
    Pid = proc_lib:spawn_link(Node, ?MODULE, init, [self(), Data]). 

%% The first thing the newly spawned process will execute 
%% in its own context, assuming here it is going to be a gen_server. 
init(Parent, Data) -> 
    Debug = sys:debug_options([]), 
    {ok, State} = initialize_some_state(Data) 
    gen_server:enter_loop(Parent, Debug, State). 

Vous vous demandez peut-être à quoi servait tout ce qui se passait avec proc_lib.Il s'avère que tout en appelant un spawn de n'importe où dans un système multi-nœuds pour initier un spawn n'importe où ailleurs dans un système multi-nœuds est possible, ce n'est pas une façon très utile de faire des affaires, et donc le gen_* comportements et même proc_lib:start_link/N n'a pas une méthode de déclaration du noeud sur lequel générer un nouveau processus.

Ce que vous voulez idéalement, ce sont des nœuds qui savent s'initialiser et rejoindre le cluster une fois qu'ils sont en cours d'exécution. Quels que soient les services fournis par votre système, ils sont généralement mieux répliqués sur les autres nœuds du cluster, et vous n'avez qu'à écrire un moyen de sélectionner un nœud, ce qui vous permet d'exécuter entièrement le processus de démarrage. tous les cas. Dans ce cas, tout ce que fait votre code manager/superviseur/worker ordinaire ne doit pas changer - les choses arrivent, et peu importe que le PID du demandeur se trouve sur un autre nœud, même si ce PID est l'adresse à quels résultats doivent être retournés. En d'autres termes, nous ne voulons pas vraiment créer des nœuds arbitraires, ce que nous voulons vraiment faire, c'est passer à un niveau supérieur et demander qu'un travail soit effectué par un autre nœud et pas vraiment à propos de comment cela se passe. Rappelez-vous que pour générer une fonction particulière basée sur un appel {M,F,A}, le nœud que vous appelez doit avoir accès au module cible et à la fonction - s'il a déjà une copie du code, pourquoi n'est-il pas un doublon du nœud appelant?

Espérons que cette réponse a expliqué plus que confus.