2009-08-14 6 views
5

MODIFICA:Come parametrizzare un modulo gen_server?

Non sto cercando di utilizzare i parametri come un modo generale per costruire i programmi di Erlang - sto ancora imparando i principi del design tradizionale. Non sto nemmeno cercando di emulare OOP. Il mio unico punto qui è rendere le mie chiamate gen_server coerenti tra le istanze del server. Questo sembra più come sistemare un'astrazione rotta per me. Posso immaginare un mondo in cui la lingua o l'OTP rendessero conveniente l'uso di qualsiasi API di gen_server, ed è un mondo in cui voglio vivere.

Grazie a Zed per aver dimostrato che il mio obiettivo primario è possibile.


Qualcuno può trovare un modo per utilizzare i moduli parametrizzati su gen_servers? Nel seguente esempio, supponiamo che test_child sia un gen_server con un parametro. Quando provo ad avviarlo, tutto quello che ottiene è:

42> {test_child, "hello"}:start_link(). 
** exception exit: undef 
    in function test_child:init/1 
     called as test_child:init([]) 
    in call from gen_server:init_it/6 
    in call from proc_lib:init_p_do_apply/3 

In definitiva, sto cercando di capire un modo per utilizzare più istanze denominate di un gen_server. Per quanto ne so, non appena inizi a farlo, non puoi più usare la tua API carina e devi lanciare messaggi alle tue istanze con gen_server: call e gen_server: cast. Se potessi dire agli esempi i loro nomi, questo problema potrebbe essere alleviato.

+0

Vuoi più server di gen utilizzando lo stesso modulo di callback, ognuno con un nome diverso? O più gen_server con lo stesso nome? – Jacob

+0

Più server di gen utilizzando lo stesso modulo di callback. Puoi passare a start_link un nome per registrare "istanze" differenti con. Quando hai una singola istanza, sembra che sia tipico dargli lo stesso nome del modulo, e quindi la tua API pubblica funziona come: some_module: some_function(). ... ma sembra essere solo una comodità. Se registri il gen_server con un nome diverso, non funziona più. Mi piacerebbe finire con qualcosa di simile: 1> Mod: some_function(). 2> Mod1: some_function(). ... dove ogni variabile fa riferimento a diverse istanze del modulo gen_server – mwt

+0

Oppure, in alternativa, mi piacerebbe sapere perché non dovrei preoccuparmene. Ogni intro di gen_server che ho visto imposta un'API piuttosto che usare solo giga/chiamate gen_server.Essendo nuovo di Erlang, mi aspettavo di poter clonare facilmente molti processi e sono sorpreso che la funzionalità API si interrompa non appena si rinomina il proprio gen_server. – mwt

risposta

-4
-module(zed, [Name]). 
-behavior(gen_server). 

-export([start_link/0, init/1, handle_cast/2]). 
-export([increment/0]). 

increment() -> 
    gen_server:cast(Name, increment). 

start_link() -> 
    gen_server:start_link({local, Name}, {?MODULE, Name}, [], []). 

init([]) -> 
    {ok, 0}. 

handle_cast(increment, Counter) -> 
    NewCounter = Counter + 1, 
    io:format("~p~n", [NewCounter]), 
    {noreply, NewCounter}. 

Questo modulo sta lavorando bene per me:

Eshell V5.7.2 (abort with ^G) 
1> S1 = zed:new(s1). 
{zed,s1} 
2> S1:start_link(). 
{ok,<0.36.0>} 
3> S1:increment(). 
1 
ok 
4> S1:increment(). 
2 
ok 
+0

Grazie. Ho intenzione di aggiungere questo al mio progetto e vedere se i benefici superano le obiezioni sollevate in questa discussione. – mwt

+1

Immagino che i voti negativi su questa risposta dovrebbero chiarire le cose :) – gleber

+0

Immagino che abbiamo già capito che tu e archaelus non siete a favore dei moduli parametrizzati, ma grazie per avere il conto :) – Zed

3

Penso che non si dovrebbe usare questa funzione in questo modo. Sembra che tu stia cercando un'interfaccia simile a OO per i tuoi gen_server. Per questo scopo stai utilizzando nomi registrati localmente: aggiungi un sacco di stato condiviso nel tuo programma, ovvero The Bad Thing. Solo i server cruciali e centrali devono essere registrati con register BIF - lasciare che tutti gli altri siano senza nome e gestiti da qualche tipo di gestore su di essi (che probabilmente dovrebbe essere registrato con un nome).

+0

Qual è la differenza tra il "gestore su di essi" e il registro dei processi integrato in termini di funzionalità? – Zed

+1

La differenza è che il gestore è un processo specializzato, che fa una cosa alla volta. Mentre il registro dei processi integrato è un registro generale per i server centrali, a cui si dovrebbe accedere globalmente (su un nodo). La possibilità di conflitti di nomi è molto più alta quando si utilizza il registro dei processi. Inoltre, il registro di processo AFAIK non è destinato a memorizzare un numero elevato di elementi, vale a dire è destinato a un numero limitato di processi registrati – gleber

+0

Sono d'accordo con te e vedo similitudini con i framework di dipendenza delle dipendenze nel mondo OO. L'iniezione di dipendenza viene utilizzata piuttosto che avere metodi di fabbrica statici globali, aumenta l'isolamento e la modularità e semplifica i test. Anche i server cruciali e centrali non dovrebbero essere referenziati da nomi atomici codificati, invece quegli atomi dovrebbero essere passati al gen server come parametro. I moduli parametrizzati sarebbero una volta l'approccio se non avessero problemi. Questo è il motivo per cui uso sempre lo stato esplicito ordinario. I nomi hard coding sono per i comportamenti dei supervisori. – Christian

10

Ci sono due parti per questa risposta. Il primo è che probabilmente non si desidera utilizzare i moduli parametrizzati finché non si è abbastanza esperti di Erlang. Tutto quello che ti danno è un modo diverso per passare argomenti in giro.

-module(test_module, [Param1]). 

some_method() -> Param1. 

è equivalente a

-module(test_non_paramatized_module). 

some_method(Param1) -> Param1. 

Il primo non comprarti molto a tutti, e molto poco codice Erlang esistente utilizza quello stile.

È più normale passare l'argomento del nome (presupponendo che si stia creando un numero di simili gen_server registrati con nomi diversi) alla funzione start_link.

start_link(Name) -> gen_server:start_link({local, Name}, ?MODULE, [Name], []). 

La seconda parte della risposta è che gen_server è compatibile con i moduli paramatized:

-module(some_module, [Param1, Param2]). 

start_link() -> 
    PModule = ?MODULE:new(Param1, Param2), 
    gen_server:start_link(PModule, [], []). 

Param1 e Param2 sarà quindi disponibile in tutte le funzioni gen_server callback.

Come cita Zed, come start_link appartiene ad un modulo paramatized, si avrebbe bisogno di fare quanto segue al fine di chiamarlo:

Instance = some_module:new(Param1, Param2), 
Instance:start_link(). 

Trovo che questo sia un particolare brutto stile - il codice che chiama some_module:new/n deve conoscere il numero e l'ordine dei parametri del modulo. Anche il codice che chiama some_module:new/n non può vivere nello stesso some_module. Ciò a sua volta rende più difficile l'aggiornamento a caldo se il numero o l'ordine dei parametri del modulo cambiano. Dovresti coordinare il caricamento di due moduli invece di uno (some_module e il suo modulo interfaccia/costruttore) anche se potresti trovare un modo per eseguire l'aggiornamento del codice some_module. In una nota minore, questo stile rende un po 'più difficile l'uso della base di codice per gli usi di some_module:start_link.


Il metodo consigliato per passare parametri a gen_servers è esplicitamente tramite gen_server:start_link/3,4 argomenti della funzione e memorizzarli nel valore dello stato si torna dalla ?MODULE:init/1 callack.

-module(good_style). 

-record(state, {param1, param2}). 

start_link(Param1, Param2) -> 
    gen_server:start_link(?MODULE, [Param1, Param2], []). 

init([Param1, Param2]) -> 
    {ok, #state{param1=Param1,param2=Param2}}. 

Utilizzando questo stile significa che non sarete catturati dalle varie parti del OTP che ancora non supporta completamente i moduli paramatized (una caratteristica nuova e ancora in fase sperimentale). Inoltre, il valore dello stato può essere modificato mentre l'istanza gen_server è in esecuzione, ma i parametri del modulo non possono.

Questo stile supporta anche l'aggiornamento a caldo tramite il meccanismo di modifica del codice. Quando viene chiamata la funzione code_change/3, è possibile restituire un nuovo valore di stato. Non esiste un modo corrispondente per restituire una nuova istanza del modulo parametrizzata al codice gen_server.

+0

Non vedo il punto della tua seconda parte. Non ci sono funzioni "statiche" nei moduli parametrizzati (mi mancano anche loro). Non sarai in grado di chiamare start_link() senza una chiamata precedente a some_module: new/2. – Zed

+0

Ovviamente potresti avere un modulo separato, come some_module_factory, che ha il tuo start_link() che restituisce un nuovo paramodato some_module, ma poi siamo troppo in profondità nella codifica di stile ... – Zed

+2

Suppongo che potresti creare un modulo di interfaccia (che era parametizzato) che comunicava con un gen_server non parametrizzato tramite gen_server: call/2. Ciò eviterebbe il problema dell'aggiornamento con gen_server e significherebbe che il riferimento del server viene passato in modo diverso (come un modulo parametrizzato piuttosto che un parametro di funzione). Rimarrebbe comunque il problema dell'aggiornamento dei due moduli di coordinamento e non posso raccomandarlo come un buon stile. – archaelus

10

Voglio solo dire due cose:

  • archaelus spiega in modo corretto. Come lui dice, l'ultimo modo in cui mostra è il modo consigliato di farlo e fa quello che ti aspetti.

  • mai, MAI, MAI , MAI di utilizzare il modulo che stavi cercando! È un residuo dei vecchi tempi che non ha mai significato ciò che intendevi e ora è fortemente deprecato.

+0

@mwt, per favore considera i punti di Robert. È uno dei creatori di Erlang ed è sicuramente quello che ogni Erlang-newbie dovrebbe ascoltare! – gleber

+0

Bene, grazie per avermelo detto. Presterò particolare attenzione ai commenti che farà.:) Per quanto riguarda il primo punto, l'ordine dei commenti potrebbe avere delle cose confuse. Non riesco a capire se si riferisca all'ultima o all'ultima modifica di Archaelus a questo punto, ma penso che sia il penultimo. Se è così, questo è simile all'esempio di Zed. Per quanto riguarda il punto due, non avrei mai veramente usato lo stile. – mwt

+0

Mi riferivo all'ultima modalità mostrata, quella che non utilizzava i moduli parametrizzati, modulo good_style. Archaelus inoltre commenta che mentre usa i moduli parametrizzati funziona perfettamente. Beh, anche se non hai mai inteso usare lo stile nel tuo esempio che è lo stile che hai usato, devi presumere che è quello che intendi. – rvirding