Elixir
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     (およびもちろんもちろんErlang
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     )のプロセスは 、 一意のプロセス識別子 -pidを使用して識別されます。 
      
        
        
        
      
     これらを使用してプロセスとやり取りします。 メッセージはpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ように送信され、仮想マシン自体がこれらのメッセージを正しいプロセスに配信します。 
      
        
        
        
      
     ただし、場合によっては、 pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    に対する過度の信頼が重大な問題につながる可能性があります。 
      
        
        
        
      
     たとえば、死んだプロセスのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    保存したり、 pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    作成を抽象化するSupervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を使用したりできます。したがって、どのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を持っているかさえわかりません( per :また、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    では、別のpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスを再起動できます。これについては一切知りません)。 
      
        
        
        
      
     簡単なアプリケーションを作成して、直面する可能性のある問題と、これらの問題をどのように解決するかを見てみましょう。 
レジストリなしで始める
 最初の例では、簡単なチャットを作成します。  mix
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロジェクトを作成することから始めましょう。 
$ mix new chat
 この記事のすべての例で使用する、絶対に標準的なGenServer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    作成しましょう。 
 # ./lib/chat/server.ex defmodule Chat.Server do use GenServer # API def start_link do GenServer.start_link(__MODULE__, []) end def add_message(pid, message) do GenServer.cast(pid, {:add_message, message}) end def get_messages(pid) do GenServer.call(pid, :get_messages) end # SERVER def init(messages) do {:ok, messages} end def handle_cast({:add_message, new_message}, messages) do {:noreply, [new_message | messages]} end def handle_call(:get_messages, _from, messages) do {:reply, messages, messages} end end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      そのようなコードがなじみのない、または理解できないと思われる場合は、OTPに関する優れたパラグラフが記載されているElixir
の作業開始をお読みください。
  mix
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    環境でiex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    セッションをiex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    し、サーバーでの作業を試みましょう。 
 $ iex -S mix iex> {:ok, pid} = Chat.Server.start_link {:ok, #PID<0.107.0>} iex> Chat.Server.add_message(pid, "foo") :ok iex> Chat.Server.add_message(pid, "bar") :ok iex> Chat.Server.get_messages(pid) ["bar", "foo"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      このステップのコードはこのコミットにあります。
 この段階では、すべてが素晴らしく、素晴らしいだけです。  pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を取得し、送信する各メッセージ( add_message/2
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    およびget_messages/1
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     )に対してこのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を渡します。すべてが予想get_messages/1
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    機能し、退屈さえします。 
      
        
        
        
      
     しかし、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を追加しようとすると楽しみが始まります... 
 とてもいい:私はSupervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    です! 
       そのため、何らかの理由でChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    死に絶えています。 私たちは空のコールドiex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    セッションにiex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ており、新しいプロセスを開始し、そのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を取得し、この新しいpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    メッセージを書き込む以外に選択肢はありません。  Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    作成しましょう-このような些細なことを心配する必要はありません! 
 # ./lib/chat/supervisor.ex defmodule Chat.Supervisor do use Supervisor def start_link do Supervisor.start_link(__MODULE__, []) end def init(_) do children = [ worker(Chat.Server, []) ] supervise(children, strategy: :one_for_one) end end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       さて、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    作成Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    非常に簡単です。 しかし、サーバーの動作モデルが変わらない場合、問題が発生します。 結局のところ、 Chat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスを自分で開始するのではなく、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    がこれを行ってくれます。 したがって、 pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスにはアクセスできません。 
 これはバグではなく、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    などのOTP
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    パターンの機能です。 子プロセスのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    アクセスすることはできません。予期せず(ただし、もちろん必要な場合のみ)プロセスを再起動できますが、実際にはプロセスをpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    し、新しいpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    新しいpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を作成します。 
プロセス名を登録する
  Chat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    アクセスするには、プロセスを指す方法を考え出す必要があります。もう一方はpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    はありません。  Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を介してプロセスが再起動された場合でも(つまり、 pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    が変更された場合でも)ポインターが保存されるように、ポインターが必要です。 
      
        
        
        
      
     そして、そのようなポインターは
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    と呼ばれます! 
 まず、 Chat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    変更しChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     。 
 # ./lib/chat/server.ex defmodule Chat.Server do use GenServer def start_link do # We now start the GenServer with a `name` option. GenServer.start_link(__MODULE__, [], name: :chat_room) end # And our function doesn't need to receive the pid anymore, # as we can reference the process with its unique name. def add_message(message) do GenServer.cast(:chat_room, {:add_message, message}) end def get_messages do GenServer.call(:chat_room, :get_messages) end # ... end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      変更-このコミットで
 これですべてが同じように機能するはずですが、より良いだけです-このpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    どこにでも渡さないでください: 
 $ iex -S mix iex> Chat.Supervisor.start_link {:ok, #PID<0.94.0>} iex> Chat.Server.add_message("foo") :ok iex> Chat.Server.add_message("bar") :ok iex> Chat.Server.get_messages ["bar", "foo"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      プロセスが再起動しても、同じ方法でアクセスできます。
 iex> Process.whereis(:chat_room) #PID<0.111.0> iex> Process.whereis(:chat_room) |> Process.exit(:kill) true iex> Process.whereis(:chat_room) #PID<0.114.0> iex> Chat.Server.add_message "foo" :ok iex> Chat.Server.get_messages ["foo"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      さて、私たちの現在のタスクについては、問題は解決されているように見えますが、もっと複雑な(そして実際のタスクにより近い)ことを試してみましょう。
動的プロセス作成
複数のチャットルームをサポートする必要があるとします。 クライアントは名前を持つ新しいルームを作成でき、希望するルームにメッセージを送信できることを期待しています。 その場合、インターフェースは次のようになります。
 iex> Chat.Supervisor.start_room("first room") iex> Chat.Supervisor.start_room("second room") iex> Chat.Server.add_message("first room", "foo") iex> Chat.Server.add_message("second room", "bar") iex> Chat.Server.get_messages("first room") ["foo"] iex> Chat.Server.get_messages("second room") ["bar"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       上から始めて、これをすべてサポートするようにSupervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を変更しましょう。 
 # ./lib/chat/supervisor.ex defmodule Chat.Supervisor do use Supervisor def start_link do # We are now registering our supervisor process with a name # so we can reference it in the `start_room/1` function Supervisor.start_link(__MODULE__, [], name: :chat_supervisor) end def start_room(name) do # And we use `start_child/2` to start a new Chat.Server process Supervisor.start_child(:chat_supervisor, [name]) end def init(_) do children = [ worker(Chat.Server, []) ] # We also changed the `strategty` to `simple_one_for_one`. # With this strategy, we define just a "template" for a child, # no process is started during the Supervisor initialization, # just when we call `start_child/2` supervise(children, strategy: :simple_one_for_one) end end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       そして、 start_link
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    関数で名前を受け入れるようにしましょう: 
 # ./lib/chat/server.ex defmodule Chat.Server do use GenServer # Just accept a `name` parameter here for now def start_link(name) do GenServer.start_link(__MODULE__, [], name: :chat_room) end #... end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      変更-このコミットで
 そして、ここに問題があります! 複数のChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスを使用できますが、それらすべてに:chat_room
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    という名前を:chat_room
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ことはできません。 トラブル... 
 $ iex -S mix iex> Chat.Supervisor.start_link {:ok, #PID<0.107.0>} iex> Chat.Supervisor.start_room "foo" {:ok, #PID<0.109.0>} iex> Chat.Supervisor.start_room "bar" {:error, {:already_started, #PID<0.109.0>}}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       正直なところ、 VM
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    非常に雄弁です。  2番目のプロセスを作成しようとしていますが、同じ名前のプロセスが既に存在しているため、環境から非常に悪意があります。 他の方法を考え出す必要がありますが、どれですか?.. 
 残念ながら、 name
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    引数のタイプは明確に定義されています。  {:chat_room, "room name"}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ようなものは使用できません。  ドキュメントを見てみましょう: 
サポートされる値:
GenServer
この場合、GenServer
Process.register/2
を使用して、指定されたatom
名でローカルに登録されます。
{:global, term}
-この場合、GenServer
、:global
モジュールの関数を使用して、指定されたterm
名でグローバルに登録されます。
{:via, module, term}
-この場合、GenServer
module
で定義されたメカニズムと名前「term」を使用して登録されmodule
。
サポートされている値は次のとおりです。
atom
GenServer
は、Process.register/2
を使用して、指定された名前でローカルに登録されます。
{:global, term}
GenServer
は、:global
モジュールの関数を使用して、指定された用語でグローバルに登録されます。
{:via, module, term}
GenServer
は指定されたメカニズムと名前で登録されます。
 最初のオプションはatom
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、すでに使用しています。トリッキーなケースでは収まらないことは確かです。 
      
        
        
        
      
      2番目のオプションは、プロセスをノードクラスターにグローバルに登録するために使用されます。 ローカルETS
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    テーブルを使用します。 さらに、クラスター内のノード内で一定の同期が必要になるため、プログラムの速度が低下します。 したがって、 本当に必要な場合にのみ使用してください。 
      
        
        
        
      
      3番目の最後のオプションは、 :via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    をパラメーターとして使用するタプルを使用します。これは、まさに問題を解決するために必要なものです。 これについてはドキュメントに次のように書かれています: 
オプション:via
は、register_name/2
、unregister_name/1
、whereis_name/1
およびsend/2
インターフェースを持つモジュールをパラメーターとして受け入れます。
:viaオプションでは、register_name / 2、unregister_name / 1、whereis_name / 1、send / 2をエクスポートするモジュールが必要です。
何もはっきりしていませんか? 私も! それでは、このメソッドの動作を見てみましょう。
 タプルの使用:via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
       したがって、タプル:via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は、 Elixir
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスを登録するために別のモジュールを使用することを伝える方法です。 このモジュールは次のことを行う必要があります。 
-   
register_name/2
関数を使用して、任意のterm
名前を登録します。 -   
unregister_name/1
関数を使用して、レジスタから名前を削除します。 -   
whereis_name/1
を使用して名前でpid
を見つけます。 -   
send/2
を使用して特定のプロセスにメッセージを送信します。 
 これが機能するためには、上記の関数はOTP
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    定義された特定の形式で応答を送信する必要がありhandle_call/3
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    およびhandle_cast/2
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    特定のルールに従うように。 
これをすべて知っているモジュールを定義してみましょう:
 # ./lib/chat/registry.ex defmodule Chat.Registry do use GenServer # API 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 def whereis_name(room_name) do GenServer.call(:registry, {:whereis_name, room_name}) end def register_name(room_name, pid) do GenServer.call(:registry, {:register_name, room_name, pid}) end 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 # SERVER def init(_) do # We will use a simple Map to store our processes in # the format %{"room name" => pid} {: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
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       繰り返しますが、レジストリが内部でどのように機能するかを選択してください。 ここでは、単純なMap
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を使用して、名前とpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を関連付けます。 このコードは、 GenServer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    どのようにGenServer
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    するかをよく知っている場合は特に、絶対にシンプルで簡単です。 関数によって返される値のみが不慣れに見える場合があります。 
  iex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    セッションでレジストリを試す時がiex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ました: 
 $ iex -S mix iex> {:ok, pid} = Chat.Server.start_link("room1") {:ok, #PID<0.107.0>} iex> Chat.Registry.start_link {:ok, #PID<0.109.0>} iex> Chat.Registry.whereis_name("room1") :undefined iex> Chat.Registry.register_name("room1", pid) :yes iex> Chat.Registry.register_name("room1", pid) :no iex> Chat.Registry.whereis_name("room1") #PID<0.107.0> iex> Chat.Registry.unregister_name("room1") :ok iex> Chat.Registry.whereis_name("room1") :undefined
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      5秒-素晴らしいフライト! レジストリは正常に機能します。レジストリを登録および削除します。 チャットで使用してみましょう。
 問題は、複数のChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    サーバーがChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    で初期化されたことでした。 特定のルームにメッセージを送信するには、 Chat.Server.add_message(“room1”, “my message”)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を呼び出したいので、サーバー名を{:chat_room, “room1”}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    および{:chat_room, “room2”}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    として登録する必要があります{:chat_room, “room2”}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     タプルを介して行う方法は次のとおりです。 
 # ./lib/chat/server.ex 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 in 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, Chat.Registry, {:chat_room, room_name}} end # SERVER (no changes required here) # ... end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      変更-このコミットで
 ここで何が起こるかです: Chat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    にメッセージを送信してルームの名前を渡すChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    に、タプルで渡されたモジュールを使用してpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    このpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    見つけます:via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     (この場合はChat.Registry
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     ) 。 
      
        
        
        
      
     これで問題が解決しますChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスをいくつでも使用できるようになり(名前の空想が終わるまで)、そのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    を知る必要がなくなりました。 絶対に。 
 ただし、このソリューションには別の問題があります。 推測? 
      
        
        
        
      
     まさに! レジストリは、クラッシュしたプロセスを認識していないため、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    使用して再起動する必要があります。 これは、これが発生すると、レジストリは同じ名前のレコードを再作成することを許可せずpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    デッドプロセスのpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    保存することを意味します。 
 理論的には、この問題の解決策はそれほど複雑ではありません。 レジストリに、 pid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    が保存されているすべてのプロセスを監視するよう強制します。 そのような「観察可能な」プロセスが落ちたらすぐに、レジストリから削除します。 
 # in lib/chat/registry.ex defmodule Chat.Registry do # ... def handle_call({:register_name, room_name, pid}, _from, state) do case Map.get(state, room_name) do nil -> # When a new process is registered, we start monitoring it. Process.monitor(pid) {:reply, :yes, Map.put(state, room_name, pid)} _ -> {:reply, :no, state} end end def handle_info({:DOWN, _, :process, pid, _}, state) do # When a monitored process dies, we will receive a # `:DOWN` message that we can use to remove the # dead pid from our registry. {:noreply, remove_pid(state, pid)} end def remove_pid(state, pid_to_remove) do # And here we just filter out the dead pid remove = fn {_key, pid} -> pid != pid_to_remove end Enum.filter(state, remove) |> Enum.into(%{}) end end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      変更-このコミットで
すべてが機能することを確認します。
 $ iex -S mix iex> Chat.Registry.start_link {:ok, #PID<0.107.0>} iex> Chat.Supervisor.start_link {:ok, #PID<0.109.0>} iex> Chat.Supervisor.start_room("room1") {:ok, #PID<0.111.0>} iex> Chat.Server.add_message("room1", "message") :ok iex> Chat.Server.get_messages("room1") ["message"] iex> Chat.Registry.whereis_name({:chat_room, "room1"}) |> Process.exit(:kill) true iex> Chat.Server.add_message("room1", "message") :ok iex> Chat.Server.get_messages("room1") ["message"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
       さて、 Supervisor
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    がChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    プロセスを再起動する回数はまったくChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ルームにメッセージを送信するとすぐに、正しいpid
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    配信されます。 
  gproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    簡素化する 
       原則として、チャットは終了しますが、タプルを使用して登録を簡素化するもう1つの機能について説明:via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ます。 これはgproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ライブラリであるgproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    です。 
      
        
        
        
      
     そして、 gproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    代わりにChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    使用するようgproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    に教えてChat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     、 Chat.Server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    完全にChat.Registry
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ます。 
 依存関係から始めましょう。 これを行うには、 gproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    をmix.exs
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    に追加しmix.exs
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     。 
 # ./mix.exs defmodule Chat.Mixfile do # ... def application do [applications: [:logger, :gproc]] end defp deps do [{:gproc, "0.3.1"}] end end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      次に、依存関係を次のようにプルアップします。
$ mix deps.get
 タプルを使用して登録を変更できます:via
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     - gproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ではなくChat.Registry
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    使用します: 
 # ./lib/chat/server.ex defmodule Chat.Server do # ... # The only thing we need to change is the `via_tuple/1` function, # to make it use `gproc` instead of `Chat.Registry` defp via_tuple(room_name) do {:via, :gproc, {:n, :l, {:chat_room, room_name}}} end # ... end
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
        gproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は{type, scope, key}
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    の3つの値で構成されるgproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    使用します。 
この場合、以下を使用します。
-   
:n
これは
意味し
。つまり、そのようなキーの下に複数のプロセスを登録することはできません。 -   
:l
これはlocal
意味します。つまり、プロセスはノードでのみ登録されます。 -   
{:chat_room, room_name}
は、タプル形式のキーそのものです。 
 可能なgproc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    設定の詳細については、 こちらをご覧ください 。 
 このような変更の後、 iex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    し、 iex
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    セッションですべてが引き続き機能することを確認します。 
 $ iex -S mix iex> Chat.Supervisor.start_link {:ok, #PID<0.190.0>} iex> Chat.Supervisor.start_room("room1") {:ok, #PID<0.192.0>} iex> Chat.Supervisor.start_room("room2") {:ok, #PID<0.194.0>} iex> Chat.Server.add_message("room1", "first message") :ok iex> Chat.Server.add_message("room2", "second message") :ok iex> Chat.Server.get_messages("room1") ["first message"] iex> Chat.Server.get_messages("room2") ["second message"] iex> :gproc.where({:n, :l, {:chat_room, "room1"}}) |> Process.exit(:kill) true iex> Chat.Server.add_message("room1", "first message") :ok iex> Chat.Server.get_messages("room1") ["first message"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      変更-このコミットで
次はどこ?
あなたと私はたくさんの複雑な質問を見つけました。 主な調査結果:
-   
pid
直接操作する場合は注意してください。プロセスが再起動するとすぐに変更されます。 - 1つのプロセスのみへのリンクを取得する必要がある場合(単一のチャットルームの場合のように)、プロセスをアトム形式の名前で登録するだけで十分です。
 -  プロセスを動的に作成する必要がある場合(多くのチャットルーム)、タプル
:via
を使用して独自のレジストリを提供できます。 -  同様の
gproc
(gproc
)がすでに存在し、それらを使用する場合、自転車を構築する必要はありません。 
 もちろん、これだけではありません。 クラスタ内のすべてのノードでグローバル登録が必要な場合は、他のツールも有効です。  Erlang
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    は、グローバル登録用のグローバルモジュール、プロセスグループ用のgprc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    があり、同じgprc
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    が役立ちます。 
 この記事に興味がある場合は、 Saša Jurić. Elixir in Action
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    をお読みくださいSaša Jurić. Elixir in Action
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      Saša Jurić. Elixir in Action
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     。