この記事は、Elixir Advent Calendar 2023 シリーズ14 の5日目です
piacere です、ご覧いただいてありがとございます
Elixirは、様々な方法でプロセスの名前付けができるので、これらがどの位、相互運用できるかを確認するため、名前付けとその検索について探ります
今回は、シングルクラスタに限定しています
名前付けの方法
Elixirのプロセス生成や名前付けは、沢山のバリエーションがあるので、1つずつチェックしていきます
① Process.register【動的】
spawn
でプロセス名は付けられませんが、後で、Process.register
すれば、名前付けできます
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
#PID<0.162.0>
+iex> Process.register(pid, :named_process)
+true
iex> send(:named_process, "hoge")
spawn: hoge
"hoge"
Process.info
でプロセス名取得できます
iex> Process.info(pid)
[
+ registered_name: :named_process,
current_function: {:prim_eval, :receive, 2},…
Process.registered
でも拾えます
iex> Process.registered
[:kernel_sup, :inet_db, Mix.TasksServer, :kernel_refc, IEx.Supervisor, IEx.Pry,
:logger, Logger.Supervisor, :logger_std_h_default, :logger_handler_watcher,
Mix.ProjectStack, :file_server_2, :elixir_sup, :user_drv, :standard_error_sup,
+Mix.State, :user, :code_server, :socket_registry, :named_process,
…
Process.unregister
で名前付けを解除できます
iex> Process.unregister(:named_process)
true
iex> Process.info(pid)
[
- registered_name: :named_process,
current_function: {:prim_eval, :receive, 2},…
…
② GenServer.start/start_link【動的】
GenServer.start
や GenServer.start_link
の第三引数に :name
を指定することで名前付けできます
defmodule Named do
use GenServer
def init(_), do: {:ok, nil}
+ def start(_, _), do: GenServer.start_link(__MODULE__, "", name: :named_process)
+ def start_link(), do: GenServer.start_link(__MODULE__, "", name: :named_process)
def handle_call(:now, _, _), do: {:reply, NaiveDateTime.utc_now, nil}
def handle_info(_, _), do: {:noreply, IO.puts("info: #{NaiveDateTime.utc_now}")}
end
iex> Named.start_link()
iex> send(:named_process, "dummy")
info: 2024-01-10 13:10:39.331147
"dummy"
GenServerも Process.info
でプロセス名取得できます(Process.unregister
で名前付け解除もできます)
iex> Process.info(pid)
[
+ registered_name: :named_process,
current_function: {:gen_server, :loop, 7},
…
なお、GenServerをベースとしている下記もこれらは同様です
- Task
- Agent
- Registry
- Supervisor
- Logger
- Broker
- ETS
- Mnesia
- GenTcp
- GenFsm
- GenEvent
- GenStatem
- GenStage
③ :global.register_name【動的】
複数クラスタ間でプロセス共有できる :global.register_name
でも、名前付けできます
Process.info
と引数の順番が逆なので、気を付けてください
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
#PID<0.162.0>
+iex> :global.register_name(:named_process, pid)
+:yes
:global.whereis_name
でプロセス名からプロセスIDを取得できます
iex> send(:global.whereis_name(:named_process), "hoge")
spawn: hoge
"hoge"
:global.registered_names
でプロセス名取得できます
iex> :global.registered_names
[:named_process]
しかし、Process.info
では、:global.register_name
のプロセス名が取得できません
iex> Process.info(:global.whereis_name(:named_process))
[
current_function: {:prim_eval, :receive, 2},
initial_call: {:erlang, :apply, 2},
status: :waiting,
message_queue_len: 0,
links: [],
dictionary: [],
trap_exit: false,
error_handler: :error_handler,
priority: :normal,
group_leader: #PID<0.66.0>,
total_heap_size: 376,
heap_size: 376,
stack_size: 9,
reductions: 4007,
garbage_collection: [
max_heap_size: %{error_logger: true, kill: true, size: 0},
min_bin_vheap_size: 46422,
min_heap_size: 233,
fullsweep_after: 65535,
minor_gcs: 0
],
suspending: []
]
④ mix.exsのmod【アプリケーション起動時】
mix.exs
で mod
を指定しておけば、Process.register
や start_link
等で名前付けをしなくても名前付きプロセスを使えます
defmodule Named.MixProject do
use Mix.Project
…
# Run "mix help compile.app" to learn about applications.
def application do
[
+ mod: {Named, nil},
extra_applications: [:logger]
]
end
…
利用時は、Process.register
や start_link
等で名前付けしたケースと同様です
iex> send(:named_process, "hoge")
spawn: hoge
"hoge"
なお、このテクニック、書籍「プログラミングElixir」 のP278(第一版だとP220)で解説されているのですが、マイクロサービスやエッジコンピューティング、組み込みでは重要なテクニックになります
検索の方法
a)Process.whwereis
Process.register
、GenServer.start/start_link
、mix.exsのmodで作られたプロセス名は特定でき、:global.register_name
は特定不可のようです
① Process.register … 特定できる
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
iex> Process.register(:named_process)
iex> Process.whereis(:named_process)
#PID<0.131.0>
② GenServer.start/start_link … 特定できる
iex> Named.start_link()
iex> Process.whereis(:named_process)
#PID<0.131.0>
③ :global.register_name … 特定不可
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
iex> :global.register_name(:named_process, pid)
iex> Process.whereis(:named_process)
nil
④ mix.exsのmod … 特定できる
iex> Process.whereis(:named_process)
#PID<0.131.0>
b):global.whereis_name
:global.register_name
は特定でき、Process.register
、GenServer.start/start_link
、mix.exsのmodで作られたプロセス名は特定不可のようです
① spawn+Process.register … 特定不可
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
iex> Process.register(pid, :named_process)
iex> :global.whereis_name(:named_process)
:undefined
② GenServer.start/start_link … 特定不可
iex> Named.start_link()
iex> :global.whereis_name(:named_process)
:undefined
③ :global.register_name … 特定できる
iex> pid = spawn(fn -> receive do n -> IO.puts("spawn: #{n}") end end)
iex> :global.register_name(:named_process, pid)
iex> :global.whereis_name(:named_process)
#PID<0.131.0>
④ mix.exsのmod … 特定不可
iex> :global.whereis_name(:named_process)
:undefined