2009-08-17 18 views
7

Transmití la mayor parte de mi aplicación a comportamientos OTP, pero estoy atascado. No puedo entender cómo hacer recepciones selectivas usando gen_server. Si ninguna de las cláusulas de la función de devolución de llamada concuerda con un mensaje, en lugar de volver a colocar el mensaje en el buzón, se produce un error.¿Cómo se hace select select en gen_servers?

Ahora, donde sea que vaya, la gente elogia selectivamente. Donde sea que vaya, la gente alaba a OTP. ¿Puede ser cierto que no puedes tener ambas cosas a la vez? ¿No parece esto una deficiencia importante y corregible?

¿Cómo lo manejan los programadores de erlang?

EDITAR (respondiendo al comentario de la zeta):

He aquí un ejemplo en el que me gustaría ver una lista de números enteros impreso en el orden establecido:

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

Por supuesto, en mi verdadera aplicación, hay retrasos de temporizador y los mensajes que deben procesarse en orden se entrelazan con otros mensajes. En particular, envío solicitudes http, a veces muchas a la vez, a veces una a la vez con un intervalo entre ellas. En cualquier caso, necesito recogerlos en orden.

+0

¿Qué te gustaría lograr? – Zed

Respuesta

4

Gen_server probablemente no sea la mejor opción para esto. Una cosa que puede hacer es recibir todos los mensajes en una lista de buffers, e implementar reciben el mismo selectiva:

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 para completar, puede volver a enviar todos los mensajes no deseados (moviéndolo así al final de su buzón de correo). Esto tiene muchos problemas (su gen_server funcionará en los mensajes de aceleración de un lado a otro, utiliza detalles de implementación), por lo que NO debe usarlo NUNCA :) handle_cast (Msg, Estado) -> self()! {'$ gen_cast', Msg}, {noreply, State}. – Zed

+0

Sí, lo consideré antes. Muy corto. ;) Su primera idea es buena. El caso habitual es que necesito recoger 8 elementos antes de procesarlos. Normalmente habrá 200 lotes de estos en un trabajo. Estoy tratando de calcular si su método aquí es más eficiente que etiquetar cada resultado con un número y ordenar cada lote una vez que se han recibido 8 elementos. – mwt

3

Tal vez usted realmente desea utilizar gen_fsm. Ese comportamiento generalmente se selecciona para los protocolos, donde el protocolo tiene ciertos estados y necesita manejar las solicitudes de manera diferente según el estado en el que se encuentre actualmente.

Pero volviendo a gen_server, utilizamos gen_server por sus características. Se suscribe a los eventos del sistema para la carga de código y te da la devolución de llamada code_change. Causa sasl-reports en bloqueos. Obtiene una estructura bien conocida que ayuda al mantenimiento del código. Lo más importante, implementa un protocolo bien diseñado para llamadas sincrónicas.

Es difícil determinar si los comportamientos de OTP son adecuados para usted en este caso.


Dado el comentario, parece que gen_server es incorrecto para usted. Cuando uso el comportamiento gen_server, lo veo como un jefe en una empresa. Todo el mundo quiere hablar con el jefe, por lo que es mejor para la empresa que el jefe pueda delegar puestos de trabajo de manera rápida y eficiente para que no permita que las personas se sienten a esperar en lugar de trabajar.

El gen_server puede delegar utilizando el parámetro 'De'. Para hacerlo, devuelva {no_reply, State} y pase el parámetro From al delegado. El delegado usa gen_server:reply/2 para contestar la llamada original.

Este método de usar gen_server podría ser para usted. Comience un proceso "simple" en el que use el extremo de recepción para hacer una recepción selectiva. Si este es un trabajo verdaderamente independiente, gen_server puede ignorarlo (disparar y olvidar). Si quiere saber si está terminado, puede gen_server:cast/2 recibir un mensaje del proceso "simple" iniciado.

Si desea bloquear el gen_server mientras realiza la recepción selectiva, entonces podría ser una idea comenzar un proceso también y esperar a que muera antes de regresar. La recepción selectiva es una búsqueda lineal O (n) en los mensajes en el orden en que llegaron. Por lo tanto, cada recepción selectiva escaneará todos los mensajes en cola que podrían ser altos para un gen_server popular.

No se supone que ninguna compañía tenga jefes trabajando allí. No se supone que ninguna aplicación erlang tenga gen_servers.

+0

El gen_fsm es una idea interesante: he estado buscando una excusa para usarlo. A primera vista, no creo que funcione para mí, a menos que pueda "parametrizar" los estados de alguna manera. El caso típico es de 8 estados, pero hay casos extremos con cientos. No sé si gen_fsm fue pensado para eso. Realmente, realmente quiero las características OTP, por lo que manejaré la recepción selectiva de alguna otra manera dado que gen_servers aparentemente no puede hacerlo de forma natural. – mwt

2

El hecho de que no pueda usar gen_server para uno de sus módulos no significa que no esté utilizando OTP. Todos los módulos de devolución de llamada implementan el bloque de recepción por usted, lo que le impide usar recepciones selectivas. No hay ninguna razón por la que no pueda implementar su propio servicio que maneja las recepciones selectivas. Y hacerlo no significa que no lo haya hecho de la manera OTP.

Todavía puede hacer que su servicio sea administrado por un supervisor con todos los beneficios que ofrece.

4

No, gen_server no está diseñado para poder manejar recepciones selectivas, cada solicitud se procesa cuando llega. Este es realmente un problema difícil ya que Erlang requiere que todos los patrones sean conocidos en tiempo de compilación, no existe un "objeto de patrón".

Estoy de acuerdo en que gen_fsm probablemente no sea para usted, ya que no acepta que lleguen diferentes mensajes en cualquier orden antes de que se produzca una explosión en el número de estados. Esta fue una de las razones por las que agregamos la recepción selectiva, le permite ignorar de forma segura mensajes poco interesantes, dejándolos para más adelante.

¿En qué OTP está interesado especialmente?

+0

Si te refieres a las características de OTP, bueno, me gustan las cosas de depuración, como sys: get_status. Eso ya ha sido realmente útil. Y los informes de fallas que provienen de sasl han sido invaluables. Y quiero que los procesos sean supervisados, aunque tal vez sea igual de fácil con los procesos normales. Y me gustó bastante la idea de llamar a mis procesos con gen_server api. Lo más importante es que este es mi primer gran proyecto de erlang y está en una fecha límite y OTP parece ser el correcto. Al no tener tiempo para explorar cada grieta, quiero asegurarme de que tenga todas las ventajas. – mwt

+0

Es decir, quiero adaptarme al marco de aplicaciones tanto como sea posible para poder entender cuáles son las ventajas. Además, aún no llegué a la versión de actualización de la versión, y espero que haya una ventaja al usar gen_servers para eso. – mwt