LoginSignup
5
1
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

【TIPS】プロセスの名前付けと検索①【シングルクラスタ編】

Last updated at Posted at 2024-01-10

この記事は、Elixir Advent Calendar 2023 シリーズ14 の5日目です


piacere です、ご覧いただいてありがとございます :bow:

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.startGenServer.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.exsmod を指定しておけば、Process.registerstart_link 等で名前付けをしなくても名前付きプロセスを使えます

mix.exs
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.registerstart_link 等で名前付けしたケースと同様です

プロセス名での呼び出し
iex> send(:named_process, "hoge")
spawn: hoge
"hoge"

なお、このテクニック、書籍「プログラミングElixir」 のP278(第一版だとP220)で解説されているのですが、マイクロサービスやエッジコンピューティング、組み込みでは重要なテクニックになります :wink:

image.png

image.png

検索の方法

a)Process.whwereis

Process.registerGenServer.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.registerGenServer.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
5
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1