GenServerとは
GenServerとは状態を保持し、クライアントからのリクエストに応答するプロセスのこと。
具体的にはクライアントから呼び出す関数と、サーバー側のコールバックを実装することで、
クライアントとサーバー間でやり取りを行うことができるようになる。
GenServerモジュールの定義
GenServerのBehaivor(振る舞い)を実装したモジュールのことを、
GenServerモジュールと呼ぶ。
基本的なGenServerモジュールは、次の手順で定義することができる。
-
start_link(初期値)
の実装 - GenServerプロセスを開始するためのコールバック -
init(初期値)
の実装 - 上記の関数から引数を受け取り、GenServerプロセスの初期状態を設定するためのコールバック - クライアント側の関数を実装する
- サーバー側のコールバックを実装する
start_link(初期値)
の実装
GenServerプロセスは次の2つの関数を使用して生成することができる。
GenServer.start(GenServerモジュール, 初期値, オプション)
GenServer.start_link(GenServerモジュール, 初期値, オプション)
GenServerモジュールを定義するにはGenServer.start_link(GenServerモジュール, 初期値, オプション)
を使用する。
またこれらの関数は、呼び出し元のプロセスとリンクしているかが異なる。
GenServerモジュールのstart_link(初期値)
は、このGenServer.start_link(GenServerモジュール, 初期値, オプション)
を呼び出す関数となる。
GenServerモジュールのstart_link(初期値)
は、次の記述で実装できる。
def start_link(init_status) do
GenServer.start_link(__MODULE__, init_status, name: __MODULE__)
end
init(初期値)
の実装
GenServerモジュールのinit(初期値)
は以下のように実装することができる。
def init(init_status) do
{:ok, init_status}
クライアント側の関数を実装する
GenServerプロセスはクライアント-サーバ間でメッセージでやり取りを行う。
実際にはクライアントが実行する関数内で、GenServerプロセスへメッセージの送信を行う。
メッセージを送信する関数は次の2つが存在する。
-
GenServer.call(__MODULE__, メッセージ, ミリ秒(オプション)
- クライアントがサーバにメッセージを送信し、応答を待つための関数。またタイムアウトまでのミリ秒を設定することができる。 -
GenServer.cast(__MODULE__, メッセージ)
- サーバーがメッセージを受信して、処理を行う関数。応答は返さない。
記述例は以下のようになる。
der initialize_status() do
GenServer.cast(__MODULE__, {:update, :init})
end
def get_by_key(key) do
GenServer.call(__MODULE__, {:get, key})
end
サーバー側のコールバックを実装する。
クライアント側の処理を行うには、次の関数を実装する。
-
handle_call(メッセージ, from, status)
- GenServer.callからメッセージを受信し、応答を返す。fromは呼び出し元プロセスの情報。 -
handle_cast(メッセージ, status)
- GenServer.castからメッセージを受信し、処理を行う。
handle_call(メッセージ, form, status)
は次のように実装することができる。
def handle_call(メッセージ, _from, status) do
{:reply, 応答, 状態}
end
この関数は同期的な操作を行い、戻り値は:reply
から始まる。
また同期的な操作を行うため、応答が返るまでクライアントは操作ができない。
handle_cast(メッセージ, status)
は次のように実装することができる。
def handle_cast(メッセージ, status) do
{:noreply, 更新後状態)
end
この関数は非同期的な操作を行い、戻り値は:noreply
から始まる。
また非同期的な操作を行うため、クライアントはメッセージを送信直後に操作を行うことができる。
結論
GenServerモジュールを定義するには以下を行う。
-
start_link(初期値)
を定義する。 -
init(初期値)
を定義する。 -
GenServer.call(__MODULE__, メッセージ)
やGenServer.cast(__MODULE__, メッセージ)
を呼び出すクライアント側の関数を実装する。 - メッセージを処理する
handle_call(メッセージ, from, status)
やhandle_cast(メッセージ, status)
を実装する。
定義したGenServerモジュールを起動することで、GenServerプロセスを生成し、メッセージを処理することができるようになる。