기술 동향
Process Register
gozalex
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