OTP:スーパーバイザーの章。バージョンが違うおかげで差分があるみたい。
とりあえず
プロジェクト作成。
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/sequence.ex
* creating lib/sequence/application.ex
* creating test
* creating test/test_helper.exs
* creating test/sequence_test.exs
書籍に掲載されている情報と違います。lib/sequence.ex
の情報は lib/sequence/application.ex
になっている模様。
lib/sequence.ex
が以下で
defmodule Sequence do
@moduledoc """
Documentation for Sequence.
"""
@doc """
Hello world.
## Examples
iex> Sequence.hello()
:world
"""
def hello do
:world
end
end
で、lib/sequence/application.ex
が以下。
defmodule Sequence.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: Sequence.Worker.start_link(arg)
# {Sequence.Worker, arg}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Sequence.Supervisor]
Supervisor.start_link(children, opts)
end
end
前の sequence から server.ex をコピィ。で、children なナニを以下に。
children = [
# Starts a worker by calling: Sequence.Worker.start_link(arg)
# {Sequence.Worker, arg}
worker(Sequence.Server, [123])
]
以下な流れ、なのかな。
- アプリケーションを開始すると Sequence.Application の start が呼び出され
- children は子サーバのリスト、それぞれが何をするか、の仕様を作るために worker を使う
- Supervisor.start_link に子サーバの仕様リストを渡し、これに基づいてスーパーバイザプロセスは子プロセスを生成
- スーパーバイザプロセスは管理対象の子サーバについて start_link を呼び出す
試してみます。
$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
== Compilation error in file lib/sequence/application.ex ==
** (CompileError) lib/sequence/application.ex:13: undefined function worker/2
(elixir) src/elixir_locals.erl:107: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
(elixir) src/elixir_locals.erl:107: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
(stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
ありゃ。色々確認してて以下を発見。
例示されているナニが以下。
children = [
worker(SimpleQueue, [], name: SimpleQueue)
]
修正してみたら worker/3
なんて知らぬ、と言われてorz
対処
import が足りてなかった模様。こうして
def start(_type, _args) do
import Supervisor.Spec, warn: false
コンパイルが通った模様。
$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
Generated sequence app
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
動作確認。
$ iex -S mix
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]
Compiling 3 files (.ex)
Generated sequence app
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Sequence.Server.increment_number 3
:ok
iex(2)> Sequence.Server.next_number
126
ワーカープロセスを殺してみます。
iex(3)> Sequence.Server.increment_number "cat"
:ok
iex(4)>
21:06:24.726 [error] GenServer Sequence.Server terminating
** (ArithmeticError) bad argument in arithmetic expression
:erlang.+(127, "cat")
(sequence) lib/sequence/server.ex:21: Sequence.Server.handle_cast/2
(stdlib) gen_server.erl:637: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:711: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:increment_number, "cat"}}
State: [data: [{'State', "My current state is '127', and I'm happy"}]]
nil
iex(5)>
で、next_number してみます。
iex(5)> Sequence.Server.next_number
123
iex(6)> Sequence.Server.next_number
124
プロセスは生き返り、サーバの状態はリセットされてます。
以降
エントリ改めて云々、ということで。