2009-08-17 4 views
7

Ich portierte die meisten meiner App auf OTP-Verhalten, aber ich stecke fest. Ich kann nicht herausfinden, wie man mit einem gen_server selektiv empfängt. Wenn keine der Callback-Funktionsklauseln mit einer Nachricht übereinstimmt, wird die Nachricht nicht zurück in das Postfach geschrieben, sondern es wird ausgegeben.Wie erhalten Sie selektives Empfangen in gen_servers?

Jetzt, überall, wo ich gehe, Leute lauschen selektiv empfängt. Überall, wo ich hingehe, loben die Leute OTP. Kann es wirklich wahr sein, dass du beides nicht gleichzeitig haben kannst? Scheint das nicht wie ein großer, korrigierbarer Mangel?

Wie gehen Erlang Programmierer damit um?

EDIT (Reaktion auf zed Kommentar):

ein Beispiel Hier ist, wo Ich mag würde eine Liste von ganzen Zahlen in sortierter Reihenfolge gedruckt, um zu sehen:

-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}. 

Natürlich in meiner realen Anwendung, es gibt Timer-Verzögerungen und die Nachrichten, die in der Reihenfolge verarbeitet werden müssen, werden mit anderen Nachrichten verschachtelt. Insbesondere sende ich HTTP-Anfragen ab, manchmal auf einmal, manchmal einzeln nacheinander mit einem Intervall dazwischen. In jedem Fall muss ich sie in der Reihenfolge sammeln.

+0

Was möchten Sie erreichen? – Zed

Antwort

4

Gen_server ist wahrscheinlich nicht die beste Wahl dafür. Eine Sache, die Sie tun können ist, alle Nachrichten in eine Pufferliste zu empfangen und implementieren die selektive erhalten selbst:

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

Nur der Vollständigkeit halber können Sie alle unerwünschten Nachrichten an sich selbst senden (und damit an das Ende Ihrer Mailbox verschieben). Dies hat viele Probleme (Ihr gen_server arbeitet mit Vollgas thowing Nachrichten hin und her; es verwendet Implementierungsspezifika), also sollten Sie es nie verwenden :) handle_cast (Msg, State) -> self()! {'$ gen_cast', Msg}, {noreply, Status}. – Zed

+0

Ja, ich habe das früher berücksichtigt. Sehr kurz. ;) Deine erste Idee ist eine gute Idee. Der übliche Fall ist, dass ich 8 Gegenstände sammeln muss, bevor ich sie verarbeite. Normalerweise werden 200 Chargen in einem Job enthalten sein. Ich versuche zu berechnen, ob Ihre Methode hier effizienter ist, als jedes Ergebnis mit einer Zahl zu versehen und jede Charge zu sortieren, sobald 8 Artikel eingegangen sind. – mwt

3

Vielleicht wirklich gen_fsm Sie verwenden möchten. Dieses Verhalten wird normalerweise als Frontend für Protokolle gewählt, bei denen das Protokoll bestimmte Zustände hat und unterschiedlich behandelt werden muss.

Aber zurück zu gen_server, benutzen wir gen_server für seine Funktionen. Er abonniert Systemereignisse für das Laden von Code und gibt Ihnen den Rückruf code_change. Es verursacht sasl-Berichte über Abstürze. Sie erhalten eine gut bekannte Struktur, die die Codepflege unterstützt. Am wichtigsten ist, dass es ein gut gestaltetes Protokoll für synchrone Anrufe implementiert.

Es ist schwer zu sagen, ob OTP-Verhalten in diesem Fall für Sie geeignet ist.


Angesichts der Kommentar klingt es wie ein Gen_server ist falsch für Sie. Wenn ich das Verhalten von gen_server verwende, denke ich daran, dass es sich um einen Chef in einer Firma handelt. Jeder möchte mit dem Chef sprechen, deshalb liegt es im Interesse des Unternehmens, den Chef in die Lage zu versetzen, Aufträge schnell und effizient zu delegieren, damit er nicht warten lässt, anstatt zu arbeiten.

Der gen_server kann über den Parameter 'From' delegieren. Geben Sie dazu {no_reply, State} zurück und übergeben Sie den Parameter Von an den Delegaten. Der Delegat verwendet gen_server:reply/2, um den ursprünglichen Anruf zu beantworten.

Diese Methode der Verwendung eines gen_server könnte möglicherweise für Sie sein. Starten Sie einen "einfachen" Prozess, den Sie mit receive-end verwenden, um selektiv zu empfangen. Wenn dies ein wirklich unabhängiger Job ist, kann der gen_server ihn ignorieren (fire and forget). Wenn es wissen will, ob es fertig ist, kann man eine gen_server:cast/2 Nachricht von dem gestarteten "normalen" Prozess zurückgeben.

Wenn Sie den gen_server blockieren wollen, während Sie die selektive empfängt, dann könnte es eine Idee sein, einen Prozess zu starten und zu warten, bis er stirbt, bevor er zurückkehrt. Selektiver Empfang ist eine O (n) lineare Suche nach den Nachrichten in der Reihenfolge, in der sie angekommen sind. So wird jeder selektive Empfang alle Nachrichten durchsuchen, die für einen populären gen_server hoch sein könnten.

Keine Firma soll nur Chefs haben, die dort arbeiten. Keine erlang-Anwendung soll nur gen_servers haben.

+0

Das gen_fsm ist eine interessante Idee - ich habe nach einer Ausrede gesucht, um es zu benutzen. Auf den ersten Blick glaube ich nicht, dass es für mich funktioniert, wenn ich die Zustände nicht irgendwie "parameterisieren" könnte. Der typische Fall ist 8 Zustände, aber es gibt Randfälle mit Hunderten. Ich weiß nicht, ob gen_fsm dafür gedacht war. Ich wirklich, wirklich wollen die OTP-Funktionen, so werde ich das selektive Empfangen auf eine andere Weise behandelt, da gen_servers es offenbar nicht natürlich tun können. – mwt

2

Nur weil Sie gen_server nicht für eines Ihrer Module verwenden können, heißt das nicht, dass Sie OTP nicht verwenden. Alle Callback-Module implementieren den Empfangsblock für Sie, der Sie daran hindert, selektiv zu empfangen. Es gibt keinen Grund, warum Sie Ihren eigenen Dienst nicht implementieren können, der selektiv empfängt. Und das bedeutet nicht, dass Sie es nicht auf OTP-Art gemacht haben.

Sie können Ihren Service immer noch von einem Supervisor mit allen Vorteilen verwalten lassen.

4

Nein, gen_server ist nicht dafür ausgelegt, selektiv empfangen zu können. Jede Anfrage wird so verarbeitet, wie sie ankommt. Dies ist tatsächlich ein schwieriges Problem, da Erlang verlangt, dass alle Muster zur Kompilierungszeit bekannt sind, es gibt kein "Musterobjekt".

Ich stimme zu, dass gen_fsm wahrscheinlich auch nicht für Sie ist, da es nicht mehrere Nachrichten in beliebiger Reihenfolge ankommen kann, bevor Sie eine Explosion in der Anzahl der Staaten bekommen würden. Dies war einer der Gründe, warum wir selektives Empfangen hinzugefügt haben, es erlaubt Ihnen, uninteressante Nachrichten sicher zu ignorieren und sie für später zu verlassen.

Für welche OTP interessieren Sie sich besonders?

+0

Wenn Sie meinen, welche OTP-Funktionen, gut, ich mag die Debugging-Sachen, wie sys: get_status. Das war schon sehr praktisch. Und die Absturzberichte, die von sasl kommen, waren von unschätzbarem Wert. Und ich möchte, dass die Prozesse überwacht werden, obwohl das bei normalen Prozessen genauso einfach ist. Und irgendwie gefiel mir die Idee, meine Prozesse mit dem gen_server api aufzurufen. Die größte Sache ist nur, dass dies mein erstes großes Erlang-Projekt ist, und es ist an einem Termin und OTP scheint wie der richtige Weg. Da ich nicht die Zeit habe, jeden Winkel zu erkunden, möchte ich sicherstellen, dass ich alle Vorteile habe. – mwt

+0

Das heißt, ich möchte mich so weit wie möglich an den Anwendungsrahmen anpassen, damit ich die Vorteile verstehen kann. Außerdem habe ich noch nicht mit der Versionsaktualisierung zu tun, und ich erwarte, dass es einen Vorteil für die Verwendung von gen_servers gibt. – mwt