기술 동향

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