Pergunta

Durante a navegação o código de um de erlang aplicativo, me deparei com um interessante problema de design.Deixe-me descrever a situação, mas eu não posso postar qualquer código devido a PIA desculpe.

O código é estruturado como uma OTP aplicativo no qual dois gen_server os módulos são responsáveis pela atribuição de algum tipo de recursos.O aplicativo é executado perfeitamente por algum tempo, e realmente não tive grandes problemas.

A parte difícil começa quando o primeiro gen_server precisa verificar se o segundo tem recursos suficientes para a esquerda.Um call é emitido para o segundo gen_server que pedir, ela própria, uma biblioteca de utilitários que (em muito, muito especial caso) emitir um call para o primeiro gen_server.

Eu sou relativamente novo para erlang, mas eu acho que esta situação vai fazer com que as duas gen_server esperar um para o outro.

Este é provavelmente um problema de design, mas eu só queria saber se existe algum mecanismo especial construído em OTP, o que pode impedir esse tipo de "trava".

Qualquer ajuda seria apreciada.

EDITAR :Para resumos as respostas :Se você tem uma situação onde dois gen_servers call uns aos outros de forma cíclica é melhor gastar um pouco mais no design do aplicativo.

Obrigado pela sua ajuda :)

Foi útil?

Solução

Isso é chamado de um impasse e pode/deve ser evitado em um design de nível.Abaixo está uma possível solução e alguns subjetiva pontos que esperamos que ajuda a evitar fazer um engano.

Enquanto existem maneiras de contornar o problema, "espera" é exatamente o que o call está a fazer.

Uma possível alternativa seria gerar um processo de dentro de Uma que chama de B, mas não impede que Um de manipulação de chamada de B.Este processo iria responder diretamente para o chamador.

No servidor:

handle_call(do_spaghetti_call, From, State) ->
    spawn(fun() -> gen_server:reply(From, call_server_B(more_spaghetti)) end),
    {noreply, State};
handle_call(spaghetti_callback, _From, State) ->
    {reply, foobar, State}

No servidor B:

handle_call(more_spaghetti, _From, State) ->
    {reply, gen_server:call(server_a, spaghetti_callback), State}

Para mim, isso é muito complexo e superhard para raciocinar sobre.Eu acho que você poderia até chamá-lo de código espaguete, sem ofender ninguém.

Em outra nota, enquanto o acima pode resolver seu problema, você deve pensar muito sobre o que chamar como este realmente implica.Por exemplo, o que acontece se o servidor executa esta chamada muitas vezes?O que acontece se, em qualquer ponto, não há um limite de tempo?Como posso configurar os tempos de espera de modo a que façam sentido?(O mais íntimo de chamada deve ter um tempo mais curto do que o exterior de chamadas, etc.).

Eu gostaria de mudar o design, mesmo que seja doloroso, porque quando você permitir que isso existe e funciona em torno dele, o sistema torna-se muito difícil para raciocinar sobre.IMHO, a complexidade é a raiz de todo o mal e deve ser evitado a todo custo.

Outras dicas

É, sobretudo, um problema de design, onde você precisará certificar-se de que não há tempo de bloqueio de chamadas a partir de gen_server1.Isto pode ser facilmente feito pela geração de uma pequena diversão que cuida da sua chamada para gen_server2 e a entrega do resultado para gen_server1 ao feito.

Você teria que acompanhar o fato de que gen_server1 está aguardando por uma resposta de gen_server2.Algo como isto talvez:

handle_call(Msg, From, S) ->
  Self = self(),
  spawn(fun() ->
    Res = gen_server:call(gen_server2, Msg),
    gen_server:cast(Self, {reply,Res})
  end),
{noreply, S#state{ from = From }}.

handle_cast({reply, Res}, S = #state{ from = From }) ->
  gen_server:reply(From, Res),
  {noreply, S#state{ from = undefiend}.

Desta forma gen_server1 pode servir solicitações de gen_server2 sem deslocado.Você seria claro também precisa fazer a devida erro de propagação das pequenas processo, mas você entendeu a idéia geral.

Outra maneira de fazer, que eu acho que é melhor, é fazer com que esse (recurso) passando informações assíncrona.Cada servidor reage e faz o que é suposto fazer quando recebe um (assíncrona) my_resource_state mensagem de outro servidor.Ele também pode pedir ao servidor para enviar o seu estado de recurso com um send_me_your_resource_state mensagem assíncrona.Como essas mensagens são assíncronas, eles nunca irão bloquear e um servidor pode processar pedidos de outros, enquanto ele está aguardando por uma my_resource_state mensagem de outro servidor depois de perguntar a ele.

Outro benefício de ter a mensagem assíncrona é que os servidores podem enviar estas informações sem o pedido quando eles sentem que é necessário, por exemplo, "me ajude, eu estou correndo muito baixa!" ou "estou transbordando, quer um pouco?".

As duas respostas do @Lukas e @knutin, na verdade, fazer isso de forma assíncrona, mas eles fazem isso por uma postura um processo temporário, que pode, então, fazer síncrono chamadas sem bloquear os servidores.É mais fácil usar mensagens assíncronas em linha reta fora, e mais claro na intenção de bem.

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