2016-12-02 13 views
1

Привет, я хочу реализовать распределенные кэши в качестве упражнения. Модуль кеша основан на gen_server. Кэши запускаются модулем CacheSupervisor. Сначала я попытался запустить все это на одном узле, который работал хорошо. Теперь я пытаюсь распространять свои кеши на двух узлах, которые живут в двух открытых консольных окнах на моем ноутбуке.Как распределить контролируемых сотрудников gen_server?

PS:

Во время написания этого вопроса, я понял, что я забыл подключить третье окно к другим узлам. Я исправил его, но у меня все еще есть оригинальная ошибка.

консоль:

Node consoles

После подключения моих узлов я называю CacheSupervisor.start_link() в третьем окне, это приводит к сообщению об ошибке follwing.

Ошибка:

** (EXIT from #PID<0.112.0>) shutdown: failed to start child: :de 
    ** (EXIT) an exception was raised: 
     ** (ArgumentError) argument error 
      erlang.erl:2619: :erlang.spawn(:[email protected]_XPS, {:ok, #PID<0.128.0>}) 
      (stdlib) supervisor.erl:365: :supervisor.do_start_child/2 
      (stdlib) supervisor.erl:348: :supervisor.start_children/3 
      (stdlib) supervisor.erl:314: :supervisor.init_children/2 
      (stdlib) gen_server.erl:328: :gen_server.init_it/6 
      (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 

Я предполагаю, что ошибка указывает на то, что :gen_server.start_link(..) внутри start_link(name) моего модуля Cache решает {:ok, #PID<0.128.0>} который, кажется, неправильно, но я не имею ни малейшего представления, куда поставить Node.spawn() еще

Модуль Cache:

defmodule Cache do 
use GenServer 
def handle_cast({:put, url, page}, {pages, size}) do 
    new_pages = Dict.put(pages, url, page) 
    new_size = size + byte_size(page) 
    {:noreply, {new_pages, new_size}} 
end 

def handle_call({:get, url}, _from, {pages, size}) do 
    {:reply, pages[url], {pages, size}} 
end 

def handle_call({:size}, _from, {pages, size}) do 
    {:reply, size, {pages, size}} 
end 
def start_link(name) do 
    IO.puts(elem(name,0)) 
    Node.spawn(String.to_atom(elem(name, 0)), :gen_server.start_link({:local,elem(name, 1)}, __MODULE__, {HashDict.new, 0}, [])) 
end 

def put(name, url, page) do 
    :gen_server.cast(name, {:put, url, page}) 
end 

def get(name, url) do 
    :gen_server.call(name, {:get, url}) 
end 

def size(name) do 
    :gen_server.call(name, {:size}) 
end 

end 

Модуль CacheSupervisor:

defmodule CacheSupervisor do 
    use Supervisor 
def init(_args) do 

    workers = Enum.map([{"[email protected]_XPS", :de},{"[email protected]_XPS", :edu}, {"[email protected]_XPS", :com} ,{"[email protected]_XPS", :it}, {"[email protected]_XPS", :rest}], 
    fn(n)-> worker(Cache, [n], id: elem(n, 1)) end) 
    supervise(workers, strategy: :one_for_one) 
end 

def start_link() do 
    :supervisor.start_link(__MODULE__, []) 
end 

end 

ответ

1
:erlang.spawn(:[email protected]_XPS, {:ok, #PID<0.128.0>}) 

:erlang.spawn/2 та же функция, как Node.spawn/2. Функция ожидает имя узла (которое вы предоставили) и функцию. Ваш вызов GenServer.start_link возвратил {: ok, Pid}, как и следовало ожидать. Поскольку кортеж нельзя рассматривать как функция Node.spawn/2 сбоев.

Я бы не рекомендовал процесс нереста на отдельных узлах, подобных этому. Если удаленный узел опустится, вы потеряете не только этот узел в своем кластере, но и вам придется иметь дело с последствиями всех ваших порожденных процессов. Это приведет к тому, что приложение будет более хрупким, чем в противном случае. Если вы хотите, чтобы кэш GenServers работал на нескольких узлах, я предлагаю запустить приложение, которое вы создаете на нескольких узлах, и иметь экземпляр вашего CacheSupervisor на каждом узле. Затем каждый CacheSupervisor запускает собственный GenServers под ним. Это более устойчиво, потому что, если узел опускается, оставшиеся узлы не будут затронуты. Конечно, логика приложения должна учитывать это, потеря узла может означать потерю данных кеша или клиентских подключений. Смотрите этот ответ для более подробной информации: How does an Erlang gen_server start_link a gen_server on another node?

Если вы действительно хотите, чтобы процесс мицелия на удаленном узле, как это вы могли бы сделать это:

:erlang.spawn_link(:[email protected]_XPS, fun() -> 
    {:ok, #PID<0.128.0>} = :gen_server.start_link({:local,elem(name, 1)}, __MODULE__, {HashDict.new, 0}, []) 
    receive 
    % Block forever 
    :exit -> :ok 
    end 
end) 

Обратите внимание, что вы должны использовать spawn_link, как и руководители ожидают быть связанными с их детьми. Если супервизор не связан, он не узнает, когда ребенок сработает и не сможет перезапустить процесс.

+0

Спасибо, ваш ответ был полезен. Я решил пересмотреть программную логику. Теперь каждый рабочий получает свой собственный руководитель –