-
Process Register기술 동향 2021. 4. 5. 00:04
defmodule Segway.Context.Registry do use GenServer # API @doc """ ## start_link ## Examples iex> {:ok, pid} = GenServer.start_link(__MODULE__, "room1") {:ok, #PID<0.107.0>} """ def start_link do # We register our registry (yeah, I know), with a simple name, # just so we can reference it in the other functions. GenServer.start_link(__MODULE__, nil, name: :registry) end @doc """ ## Examples iex> Segway.Context.Registry.whereis_name("room1") :undefined """ def whereis_name(room_name) do GenServer.call(:registry, {:whereis_name, room_name}) end @doc """ ## Examples iex> Segway.Context.Registry.register_name("room1", pid) :yes iex> Segway.Context.Registry.register_name("room1", pid) :no iex> Segway.Context.Registry.whereis_name("room1") #PID<0.107.0> """ def register_name(room_name, pid) do GenServer.call(:registry, {:register_name, room_name, pid}) end @doc """ ## Examples iex> Segway.Context.Registry.unregister_name("room1") :ok iex> Segway.Context.Registry.whereis_name("room1") :undefined """ def unregister_name(room_name) do GenServer.cast(:registry, {:unregister_name, room_name}) end def send(room_name, message) do # If we try to send a message to a process # that is not registered, we return a tuple in the format # {:badarg, {process_name, error_message}}. # Otherwise, we just forward the message to the pid of this room. case whereis_name(room_name) do :undefined -> {:badarg, {room_name, message}} pid -> Kernel.send(pid, message) pid end end defp via_tuple(room_name) do # And the tuple always follow the same format: # {:via, module_name, term} {:via, Chat.Registry, {:chat_room, room_name}} end # SERVER def init(_) do {:ok, Map.new} end def handle_call({:whereis_name, room_name}, _from, state) do {:reply, Map.get(state, room_name, :undefined), state} end def handle_call({:register_name, room_name, pid}, _from, state) do # Registering a name is just a matter of putting it in our Map. # Our response tuple include a `:no` or `:yes` indicating if # the process was included or if it was already present. case Map.get(state, room_name) do nil -> {:reply, :yes, Map.put(state, room_name, pid)} _ -> {:reply, :no, state} end end def handle_cast({:unregister_name, room_name}, state) do # And unregistering is as simple as deleting an entry from our Map {:noreply, Map.delete(state, room_name)} end end
an atom - the GenServer is registered locally with the given name using Process.register/2.
{:global, term} - the GenServer is registered globally with the given term using the functions in the :global module.
{:via, module, term} - the GenServer is registered with the given mechanism and name.
(The :via option expects a module that exports register_name/2, unregister_name/1, whereis_name/1 and send/2.)
via tuple (w/ Module that I want to use in order to register processes) 을 사용하여 다음과 같이 name option에 사용할 수 있다.
Process.register/2 는 locally 사용하는 경우 --> :atom만 name(key for pids)으로 사용할 수 있지만
:via or :global 은 remote node에서도 사용할 수 있다. --> any type(key for pids)으로 사용할 수 있다.
defmodule Chat.Server do use GenServer # API def start_link(name) do # Instead of passing an atom to the `name` option, we send # a tuple. Here we extract this tuple to a private method # called `via_tuple` that can be reused for every function GenServer.start_link(__MODULE__, [], name: via_tuple(name)) end def add_message(room_name, message) do # And the `GenServer` callbacks will accept this tuple the same way it # accepts a `pid` or an atom. GenServer.cast(via_tuple(room_name), {:add_message, message}) end def get_messages(room_name) do GenServer.call(via_tuple(room_name), :get_messages) end defp via_tuple(room_name) do # And the tuple always follow the same format: # {:via, module_name, term} {:via, Segway.Context.Registry, {:chat_room, room_name}} end # SERVER (no changes required here) # ... end
'기술 동향' 카테고리의 다른 글
GenServer를 어떻게 종료할 것인가? (1) 2021.08.25 Elixir Process (0) 2021.06.28 Ecto Composable Query (0) 2020.11.16 Ecto.Multi (0) 2020.10.06 History of Erlang (0) 2020.06.25