[Elixir in Action]OTP/GenServerを学んで非同期/並行処理を学ぶ

Elixir in Actionの続き。ようやっと3分の2くらい。写経もほどほどに実施しながらだとこのくらいですかね。

Chatpter6、7ではOpen Telecom Platformと、その上に構成されるGenServerに関する内容をざーっと通してました。
自前のloopで作っていた簡易サーバから、GenServerへと一般化、どのように処理しているのか?をボトルネックを解消していくという話の流れのなかで説明していっています。

サーバの基本的な役割は以下

  • Spawn でプロセスを分ける
  • loopでプロセスを回す
  • processの状態を管理する
  • messageに反応する
  • 送信元に応答を返す(送る)

最終的にはGenServerに近づいていくので、大きくここら辺は割愛。
ただ、 非常に考え方は重要 だし、ここら辺がErlang/Elixirのアーキテクチャとして選ぶ価値があると判断される箇所だと思うので、ちゃんと理解する方が良さそうです。

Elixir/Erlangのサーバで重要な役割を持つのは以下。

  • gen_server
    • init, handle_call, handle_castなどのcallbacksを持つ
    • 想定していない処理を無視するために、 def handle_info(_, state), do: something といった処理も入れる
  • superevisor
    • errorハンドリングやリカバリを担う
  • application
  • gen_event
  • gen_fsm

single processで処理される時のボトルネックは、多くのリクエストが溜まるにつれて応答(process)が遅くなること。
そこで、concurrentに処理できるように拡張する。ただ、cncurrentに処理できるように spawn により別プロセスで処理可能にすると、同期的に処理したい時が複雑になる。そこで、Elixir/Erlangでは以下のように GenServer.reply を使い、うまいこと非同期処理を利用している。

def hanldle_call(....) do
  spawn(fn ->
    data = # 処理
    GenServer.reply(caller, data) # 処理が終われば、GenServerの機構でメッセージを送る
  end)

  {:noreply, do_folder} # まずは非同期的に応答する
end

single processでリクエストを操作し、その実際の処理は子プロセスに実施させる。必ずこの方法が良いというわけではないが、本書ではこの方法を解の1つとして紹介していた。

ボトルネックの話でいうと、databaseを相手にしはじめると、DBとアプリをつなぐ間のpoolを処理するためのプロセスも関係してきますが、そこら辺はEctoでは poolboy が役割を担っているそうな。

poolboy

Getting Startedを読んでなくていきなりこれでは辛いけれど、Getting Startedの後なら特にconcurrencyやfault toleranceの話を知る上ではこれは読んで価値ありそうです。特に、Erlang/Elixirに限らず、concurrencyやfault toleranceの考え方は参考になると思います。


補足コード

  • Process.registerは以下
  @doc """
  Associates the name with a pid or a port identifier. `name`, which must
  be an atom, can be used instead of the pid / port identifier with the
  `Kernel.send/2` function.
  `Process.register/2` will fail with `ArgumentError` if the pid supplied
  is no longer alive, (check with `alive?/1`) or if the name is
  already registered (check with `whereis/1`).
  """
  @spec register(pid | port, atom) :: true
  def register(pid, name) when not name in [nil, false, true] do
    :erlang.register(name, pid)
  end
  • GenServer.reply/2は以下。Erlangのgen_server.replayも検索してみると似た感じ。
  @doc """
  Replies to a client.
  This function can be used by a server to explicitly send a reply to a
  client that called `call/3` or `multi_call/4`. When the reply cannot be
  defined in the return value of `handle_call/3`.
  The `client` must be the `from` argument (the second argument) received
  in `handle_call/3` callbacks. Reply is an arbitrary term which will be
  given back to the client as the return value of the call.
  This function always returns `:ok`.
  """
  @spec reply({pid, reference}, term) :: :ok
  def reply(client, reply)

  def reply({to, tag}, reply) do
    try do
      send(to, {tag, reply})
      :ok
    catch
      _, _ -> :ok
    end
  end

[Elixir in Action]OTP/GenServerを学んで非同期/並行処理を学ぶ」への1件のフィードバック

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中