ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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

    댓글

Elixian