2009-08-17 8 views
7

Ho portato la maggior parte della mia app ai comportamenti OTP, ma sono bloccato. Non riesco a capire come fare ricevere selettivi usando un server gen. Se nessuna delle clausole della funzione di callback corrisponde a un messaggio, anziché reinserire il messaggio nella casella di posta, viene eliminato l'errore!Come si fa a ricevere selettivi in ​​gen_servers?

Ora, ovunque vado, la gente riceve una lode. Ovunque vada, la gente loda OTP. È vero che non puoi averli entrambi contemporaneamente? Non sembra una carenza importante e correggibile?

Come gestiscono i programmatori di erlang?

EDIT (rispondendo al commento di zed):

Ecco un esempio in cui mi piacerebbe vedere una lista di interi stampato in modo ordinato:

-module(sel_recv). 
-behaviour(gen_server). 

-export([start_link/0]). 

-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 
    terminate/2, code_change/3]). 

-export([test/0]). 

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

test() -> 
    gen_server:cast(?MODULE, test). 

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

handle_call(_Request, _From, State) -> 
    Reply = ok, 
    {reply, Reply, State}. 

handle_cast(test, _State) -> 
    lists:map(fun(N) -> 
         gen_server:cast(?MODULE, {result, N}) 
       end, [9,8,7,6,5,4,3,2,1]), 
    {noreply, [1,2,4,5,6,7,8,9]}; 
handle_cast({result, N}, [N|R]) -> 
    io:format("result: " ++ integer_to_list(N) ++ "~n"), 
    {noreply, R}. 

handle_info(_Info, State) -> 
    {noreply, State}. 

terminate(_Reason, _State) -> 
    ok. 

code_change(_OldVsn, State, _Extra) -> 
    {ok, State}. 

Naturalmente, nella mia vera app, ci sono ritardi del timer e i messaggi che devono essere elaborati in ordine sono intercalati con altri messaggi. In particolare, invio richieste HTTP, a volte molte contemporaneamente, a volte una alla volta con un intervallo tra di esse. In ogni caso, ho bisogno di raccoglierli in ordine.

+0

Cosa vorresti raggiungere? – Zed

risposta

4

Gen_server probabilmente non è la scelta migliore per questo. Una cosa che puoi fare è quello di ricevere tutti i messaggi in un elenco di buffer, e attuare il selettivo di ricezione da soli:

handle_cast(test, _State) -> 
    ... 
    {noreply, {[1,2,4,5,6,7,8,9], []}}; 

handle_cast({result, N}, {Wait, Buff}) -> 
    {noreply, handle_results(Wait, [N|Buff])}. 

handle_results([], Buff) -> 
    {[], Buff}; 

handle_results([W|WTail] = Wait, Buff) -> 
    case lists:member(W, Buff) of 
     true -> 
      io:format("result: " ++ integer_to_list(W) ++ "~n"), 
      handle_results(WTail, Buff -- [W]); 
     false -> 
      {Wait, Buff} 
    end. 
+0

Solo per motivi di completezza, è possibile inviare nuovamente tutti i messaggi indesiderati a se stessi (spostandolo così alla fine della propria casella di posta). Questo ha molti problemi (il tuo gen_server funzionerà a pieno regime mandando messaggi avanti e indietro, usa specifiche di implementazione), quindi NON dovresti usarlo MAI :) handle_cast (Msg, Stato) -> self()! {'$ gen_cast', Msg}, {noreply, Stato}. – Zed

+0

Sì, l'ho considerato prima. Molto brevemente. ;) La tua prima idea è buona. Il solito caso è che ho bisogno di raccogliere 8 oggetti prima di elaborarli. In genere ci saranno 200 lotti di questi in un lavoro. Sto cercando di calcolare se il tuo metodo qui è più efficiente di codificare ogni risultato con un numero e ordinare ogni lotto una volta che sono stati ricevuti 8 elementi. – mwt

3

Forse davvero si vuole utilizzare gen_fsm. Questo comportamento viene solitamente scelto come front-end per i protocolli, dove il protocollo ha determinati stati e deve gestire le richieste in modo diverso a seconda dello stato corrente.

Ma torniamo a gen_server, usiamo gen_server per le sue funzionalità. Si iscrive agli eventi di sistema per il caricamento del codice e ti dà il callback code_change. Fa sasl-report su arresti anomali. Si ottiene una struttura ben nota che aiuta la manutenzione del codice. Soprattutto, implementa un protocollo ben progettato per le chiamate sincrone.

È difficile dire se i comportamenti OTP siano corretti per te in questo caso.


Dato il commento, sembra che un server gen_ sia sbagliato per te. Quando utilizzo il comportamento di gen_server, lo considero un capo di un'azienda. Tutti vogliono parlare con il capo, quindi è nell'interesse della compagnia rendere il capo in grado di delegare posti di lavoro in modo rapido ed efficiente, così da non lasciare che la gente sieda in attesa invece di lavorare.

gen_server può delegare utilizzando il parametro "Da". Per fare ciò, restituire {no_reply, State} e passare il parametro Da al delegato. Il delegato utilizza gen_server:reply/2 per rispondere alla chiamata originale.

Questo metodo di utilizzo di gen_server potrebbe essere per voi. Avvia un processo "semplice" che usi ricevi-end per effettuare la ricezione selettiva. Se questo è un lavoro veramente indipendente, il gen_server può ignorarlo (fuoco e dimentica). Se vuole sapere se è finito, è possibile rintracciare il messaggio gen_server:cast/2 dal processo "semplice" avviato.

Se si desidera bloccare il server gen mentre si eseguono i ricevimenti selettivi, potrebbe essere un'idea avviare un processo e attendere che muoia prima di tornare. La ricezione selettiva è una ricerca O (n) lineare sui messaggi nell'ordine in cui sono arrivati. Quindi ogni ricezione selettiva analizzerà tutti i messaggi in coda che potrebbero essere alti per un famoso server gen.

Nessuna azienda dovrebbe avere solo capi che lavorano lì. Nessuna applicazione erlang dovrebbe avere solo gen_server.

+0

gen_fsm è un'idea interessante - ho cercato una scusa per usarlo. A prima vista, non penso che funzionerà per me, a meno che non sia in grado di "parametrizzare" gli stati in qualche modo. Il caso tipico è 8 stati, ma ci sono casi limite con centinaia. Non so se gen_fsm fosse pensato per quello. Io davvero, davvero voglio le funzionalità di OTP, quindi gestirò la ricezione selettiva in qualche altro modo dato che apparentemente gen_servers non possono farlo naturalmente. – mwt

2

Solo perché non è possibile utilizzare gen_server per uno dei moduli non significa che non si stia utilizzando OTP. Tutti i moduli di callback implementano il blocco di ricezione per te che ti impedisce di utilizzare i ricevimenti selettivi. Non c'è motivo per cui non sia possibile implementare il proprio servizio che gestisce i ricevimenti selettivi. E farlo non significa che non l'hai fatto in modo OTP.

È ancora possibile gestire il servizio da un supervisore con tutti i vantaggi che offre.

4

No, gen_server non è progettato per essere in grado di gestire i ricevimenti selettivi, ogni richiesta viene elaborata al suo arrivo. Questo è in realtà un problema difficile poiché Erlang richiede che tutti i pattern siano noti al momento della compilazione, non esiste un "oggetto modello".

Sono d'accordo sul fatto che gen_fsm non è probabilmente nemmeno per te, in quanto non è possibile che messaggi diversi arrivino in qualsiasi ordine prima che si verifichi un'esplosione nel numero di stati. Questo è stato uno dei motivi per cui abbiamo aggiunto ricezioni selettive, che ti consente di ignorare tranquillamente i messaggi non interessanti, lasciandoli per dopo.

A quale OTP siete particolarmente interessati?

+0

Se intendi quali caratteristiche di OTP, beh, mi piace il materiale di debug, come sys: get_status. È già stato molto utile. E i rapporti sugli arresti anomali provenienti da sasl sono stati inestimabili. E voglio che i processi siano supervisionati, anche se forse è altrettanto facile con i processi ordinari? E mi piaceva un po 'l'idea di chiamare i miei processi con l'API gen_server. La cosa più importante è solo che questo è il mio primo grande progetto di erlang ed è su una scadenza e OTP sembra il modo giusto. Non avendo il tempo di esplorare ogni fessura, voglio assicurarmi di avere tutti i vantaggi. – mwt

+0

Vale a dire, voglio conformarmi il più possibile al framework applicativo in modo da capire quali sono i vantaggi. Inoltre, non ho ancora avuto a che fare con la gestione dell'upgrade, e mi aspetto che ci sia un vantaggio nell'usare gen_servers per questo. – mwt

Problemi correlati