2014-11-20 3 views
3

자동으로 감시 트리를 시작하면 교착 상태 문제가 발생합니다. 하나의 GenServer의 초기 상태는 트리에있는 다른 감독자의 하위 작업자입니다.Elixir - 다른 여러 GenServer pids의 초기 상태가있는 GenServer

감독자 및 근로자 : 그 근로자의 PID의 상태

defmodule Parallel.Worker.Supervisor do 
    import Supervisor.Spec 

    def start_link do 
    # Start up a worker for each core 
    schedulers = :erlang.system_info(:schedulers) 
    children = Enum.map(1..schedulers, 
     &(worker(Parallel.Worker.Server, [], id: "Parallel.Worker#{&1}"))) 

    opts = [strategy: :one_for_one, name: Parallel.Worker.Supervisor] 
    Supervisor.start_link(children, opts) 
    end 

    def workers do 
    Process.whereis(Parallel.Supervisor) 
     |> Supervisor.which_children 
     |> Enum.reduce [], fn 
     {_name, pid, :worker, _module}, acc -> [{make_ref, pid} | acc] 
     _, acc -> acc 
     end 
    end 

end 

GenServer :

defmodule Parallel.Process.Server do 
    use GenServer 

    def start_link do 
    GenServer.start_link(__MODULE__, workers: [Parallel.Worker.Supervisor.workers]) 
    end 
end 

마지막 줄에서 볼 수 있듯이, 내가 전화하고 "Parallel.Worker 여기 코드는 .Supervisor.workers "이것은 트리에서 초기화를 기다리는 것을 차단하는 것으로 보입니다.이 메소드는이 메소드가 반환 될 때까지 완료되지 않습니다. PID를 초기 GenServer 상태로 어떻게 감독 할 수 있습니까?

UPDATE :

나는 내가 더 배울 수 있도록 (이 소스에서 볼 수있는 좋은 제안이 비록) poolboy을 사용하지 싶어

. 저는 특별히 아무것도하지 않으려 고합니다. 직원은 인수가있는 전달 된 함수를 처리하기 만합니다. Heres the Worker GenServer :

defmodule Parallel.Worker do 
    use GenServer 
    require Logger 

    def start_link(state) do 
    GenServer.start_link(__MODULE__, state, []) 
    end 

    def init(state) do 
    {:ok, state} 
    end 

    # Using cast to be async as the job could take longer than the default 5 seconds, 
    # Don't want client blocked on waiting for job to complete 
    def handle_cast({:execute, fun, args, return_pid, job_ref}, state) do 
    Logger.debug fn()-> "#{inspect self}: Recevied job with args: #{inspect args} for job #{inspect job_ref} to return to #{inspect return_pid}" end 
    send(return_pid, {job_ref, apply(fun, args), self}) 
    {:noreply, state} 
    end 
end 
+1

아마도 작업자 pid를 명시 적으로 전달하지 않고 응용 프로그램을 다시 아키텍처해야합니다. 지금 여기서 뭘하려고하는거야? –

+1

아마도 Poolboy와 같은 풀링 라이브러리를 찾고 계십니까? 이 기사를 확인하십시오 : http://hashnuke.com/2013/10/03/managing-processes-with-poolboy-in-elixir.html –

답변

7

여기서 어떤 종류의 풀을 만들고 싶다고 가정하고 있습니까? 의견에서 언급했듯이, 당신은 풀장을 들여다보아야합니다. 연습을 위해 이것을 직접 구현하고 싶다면, 여전히 영감을 얻기 위해 풀 보이스 코드를 조사 할 가치가 있습니다.

기본적으로 풀 보이스 풀은 알려진 풀의 컬렉션을 관리하는 "풀 관리자"(gen_server)에 의해 관리됩니다. 이 풀 관리자 프로세스는 직원을 시작하고 감독하는 데 사용되는 simple_one_for_one 감독자를 내부적으로 시작합니다.

풀 관리자 프로세스 during initialization first starts the supervisor. 그런 다음 prepopulate/1에서 start supervised worker processes으로 전화합니다. 이 함수는 동적으로 N 작업자 via supervisor:start_child/2을 생성하며 풀 관리자는 작업자 pid의 목록을 내부적으로 보관할 수 있습니다.

이렇게하면 풀 관리자 프로세스가 초기화 중에 상위 수퍼바이저와 통화 할 필요가 없습니다 (교착 상태의 원인). 대신 관리자는 자식 자체를 만듭니다. 내부 감독자에게 의지하면 여전히 근로자가 감독 트리에 상주하게됩니다.

모든 것이 제대로 작동하는 데 필요한 기타 세부적인 사항이 있습니다. 풀 보이스 프로세스 (풀 관리자)는 종료를 트랩하고 모니터에 연결하며 작업자가 체크 아웃 될 때 작업자를 모니터링합니다. 이렇게하면 작업자 충돌을 올바르게 감지 할 수 있습니다. 추가 분석을 위해 코드를 읽는 것이 좋습니다.

내 생각에 OTP에 대한 더 나은 이해를 얻으려면 흥미로운 연습이 될 수 있습니다. 그러나 프로덕션 환경에서이 작업을 수행하는 경우 풀 보이스를 직접 사용하는 것이 좋습니다.

+1

설명을 주셔서 감사합니다. 정말 풀 보이스 코드를 이해하는 데 도움이되었습니다. OTP에 대해 더 잘 이해하기 위해 언급 한대로 학습 목적으로 사용하고 있습니다. – mgwidmann

+2

그래, 나는 작은 단계로가는 것이 좋습니다. 우선 관리자를 사용하지 않고 init/1에서 직접 worker를 생성하는 풀 관리자를 구현할 수 있습니다.일단 작동하면 감독을 처리하기 위해 simple_one_for_one을 도입 할 수 있습니다. 그런 다음 충돌이 발생했을 때이를 알기 위해 근로자를 모니터링 할 수 있습니다. 마지막으로, 요청에 따라 생성 된 프로세스와 클라이언트가 작업을 완료 한 후에 종료되는 프로세스에 대한 "주문형"작성을 추가 할 수 있습니다. – sasajuric

관련 문제