Pergunta

Eu estou tendo um tempo difícil de moldagem minha cabeça todo o caminho correto para fazer chamadas contra um gen_server instância criado dinamicamente por um supervisor, com um simple_one_for_one criança estratégia.Eu estou tentando criar controles de acesso de dados como gen_servers.Cada entidade terá o seu próprio supervisor, e que o supervisor irá criar gen_server instâncias, conforme necessário, para realmente realizar as operações CRUD no banco de dados.Eu entendo o processo de definição de processos subordinados, bem como o processo para criá-los conforme necessário.

Inicialmente, meu plano era para abstrair o filho processo de criação em funções personalizadas no gen_server módulo que criou uma criança, disparou a operação solicitada (por exemplo,localizar, armazenar, excluir) em que a criança, através de gen_server:call(), e , em seguida, retornar o resultado da operação de volta para o processo de chamada.A menos que eu esteja enganado, porém, que irá bloquear quaisquer outros processos de tentar usar essas funções até que a chamada retorna.Isso é, definitivamente, não é o que eu tenho em mente.

Eu posso ser preso em OO modo (minha formação é em Java), mas parece que deve haver uma maneira simples de permitir que uma função em um módulo para obter uma referência para um processo filho e, em seguida, fazer chamadas contra o processo, sem fuga de internos da criança.Em outras palavras, eu não quero ter que ligar para a create_child() método em uma entidade supervisor e, em seguida, ter o meu código de aplicativo de fazer gen_server:chamadas contra essa criança PID (i.e. gen_sever:call(Pid, {find_by_id, Id})).Eu gostaria de, em vez disso, gostaria de ser capaz de chamar uma função mais como Child:find_by_id(Id).

Foi útil?

Solução

quando você criar, modificar ou excluir um registro, você não precisa esperar por uma resposta.Você pode usar um gen_server:elenco para isso, mas você não precisa de um gen_server para isso, como eu disse no meu primeiro comentário, uma simples chamada para uma função de interface executado no processo do cliente, irá poupar tempo.

Se você quiser ler, 2 casos:

  • você pode fazer outra coisa enquanto espera a resposta e, em seguida, um gen_server chamada é ok, mas um simples processo de expansão de espera para a resposta e enviá-lo de volta para o cliente irá fornecer o mesmo serviço.

  • você não pode fazer nada antes de obter a resposta, então não há nenhum problema de bloqueio, e eu penso que é realmente preferível usar como menos de código possível, então, novamente, uma simples chamada de função será o suficiente.

gen_server destina-se a ser persistente e reagir a mensagens.Eu não vejo no seu exemplo, a necessidade de ser persistente.

-module(access).
-export([add/2,get/1]).
-record(foo, {bar, baz}).

add(A,B) ->
  F = fun() ->
    mnesia:write(#foo{bar=A,baz=B})
  end,
  spawn(mnesia,activity,[transaction, F]). %% the function return immediately,
                                           %% but you will not know if the transaction failed

get(Bar) ->
  F = fun() ->
    case mnesia:read({foo, Bar}) of
      [#foo{baz=Baz}] -> Baz;
      [] -> undefined
    end
  end,
  Pid = self(),
  Ref = make_ref(),
  Get = fun() ->
    R = mnesia:activity(transaction, F),
    Pid ! {Ref,baz,R}
  end,
  spawn(Get),
  Ref. %% the function return immediately a ref, and will send later the message {Ref,baz,Baz}.

Outras dicas

Uma resposta completa é altamente dependente da aplicação, por exemplo, um gen_server pode ser suficiente, ou talvez você realmente precisa de um pool de conexões de banco de dados em vez disso.Mas uma coisa que você deve estar ciente de que uma gen_server pode retornar de um handle_call de retorno de chamada antes de ele realmente tem uma resposta pronta para o cliente, retornando {noreply, NewState} e, mais tarde, uma vez que tem um cliente de resposta pronta, chamando gen_server:reply/2 para enviar de volta ao cliente.Isto permite que a gen_server para o serviço de chamadas a partir de outros clientes, sem bloqueio na primeira chamada.Note que isto requer que o gen_server tem uma forma de enviar um pedido para o banco de dados sem ter de bloco esperando por uma resposta;isso é muitas vezes realizada por ter o banco de dados de enviar uma resposta que chega a gen_server:handle_info/2 a chamada de retorno, passando informações suficientes que o gen_server pode-se associar o banco de dados de resposta com a correta solicitação do cliente.Note-se também que gen_server:call/2,3 tem um tempo limite padrão de 5 segundos, então você vai precisar para lidar com o que se pensa que a duração das chamadas de banco de dados exceder o padrão.

Se o problema que você vê é o que você está escapando de que a implementação interna do seu banco de dados-o processo é uma gen_server, você pode implementar a api, que leva o pid como argumento bem.

-module(user).

-behaviour(gen_server).

-export([find_by_id/2]).

find_by_id(Pid, Id) ->
    gen_server:call(Pid, {find_by_id, Id}).

%% Lots of code omitted

handle_call({find_by_id, Id}, From, State) ->
    ok.

%% Lots more code omitted.

Desta forma, você não dizer aos clientes o que a implementação está no fato de um gen_server (embora alguém possa usar gen_server:chamar assim).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top