gen_serverノート


gen_serverノート
by cn Denis  http://cndenis.iteye.com 2013年4月24日
gen_ServerはerlangのOTPフレームの中で最もよく使われている「行動パターン」ですよね.少なくともいくつかのerlang教材はまずこれを紹介します.
これは何に使いますか?それとも、なぜこれを使うのですか?このことについては、まだ数日間しか触れていないので、理解はまだ浅いです.serverには以下のいくつかのメリットがあります.
像に向かって、データと方法をカプセル化します.gen_server内部は状態Stateを維持し、他の言語の「クラス」のように、データと方法を一緒にパッケージして、データが不正に変更されるのを防ぐために、各種の関数を提供する必要があります.コールを簡略化し、通信を遮断する.erlangでは、遠隔プロセス呼び出し(RPC)を自分で実現するためには、メッセージフォーマットを自分で定義し、パッケージとデバッグのコードを自分で作成し、さらに各種の異常問題を処理する必要があります.Serverは全部できました.普通の使用関数のように直接調整すればいいです.手間が省けます.まだ間違いがありません.熱コード置換などの高級機能.ホットコードの交換はerlangが宣伝している特徴的な機能です.停止せずに生産を維持することはとても素晴らしいことです.でも、これは高級機能です.初学はまだ使えません.本でも多く話していません.gen_serverテンプレート
gen_serverはテンプレートを使って書いてもいいです.以下の通りです.
-module().

-behaviour(gen_server).

%% API
-export([]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 
       terminate/2, code_change/3]).

-define(SERVER,?MODULE).

start_link() -> 
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) -> 
    {ok, State}.

handle_call(_Request, _From, State) -> 
    {reply, Reply, State}.

handle_cast(_Msg, State) -> 
    {noreply, State}.

handle_info(_Info, State) -> 
    {noreply, State}.

terminate(_Reason, _State) -> 
    ok.

code_change(_OldVsn, State, _Extra) -> 
    {ok, State}.
意味は以下の通りです
-moduleはモジュール名で、ファイル名です.
-behaviourは「行動パターン」を指定しています.ここでgen_です.serverは、このモジュールがgen_が実現されているかどうかを確認するのに役立ちます.serverのすべてのインターフェース.つまり  init/1、  handle_call/3handle_cast/2、  handle_info/2、  terminate/2、  code_change/3の6つの関数.足りないとコンパイラがエラーします.
-exportは二つあります.一つ目にAPIと書いてあります.つまり他の人のためのインターフェースです.二つ目はgen_です.serverのインターフェースは、上で言った六つです.本当は同じexportに書いてもいいですが、別々に書いたほうがいいです.
次に、各API関数があります.一般的にはパッケージ化、コールバック関数の包装関数です.
後は六つのコールバック関数の具体的な実現です.コールバック関数は具体的な仕事を担当します.
このような比喩をすることができるでしょう.もしモジュール全体が昆虫であれば、APIは頭で、gen_serverは体ですが、リフティング関数は六本足です.頭にどこに行くかと言ったら、足を動かして、虫を全部走らせます.体はそれぞれの足をくっつけているので、さもなくば単独の足はもとの場所で痙攣することしかできなくて、どこにもいけません.
サーバを起動
サーバーを起動するにはstart/3start/4start_link/3start_link/4{ok,Pid}の四つの関数があります.これらのstart関数を使うと、新しいプロセスが生まれます.つまり、一つのgen_です.serverサーバー.これらのstart関数の通常の場合の戻り値はPidであり、(ServerName, Module, Args, Options)はこの新しいプロセスのプロセス番号である.Linkと持たないの違いは、親プロセスとリンクしているかどうかということです.言い換えれば、新起動のプロセスが死んだら、彼のプロセス(親プロセス)を起動することを知らせますか?
start関数は、4つのパラメータServerNameとすることができる.
最初のパラメータ{error, {already_started, Pid}}はサービス名であり、省けます.同じサービス名を持つモジュールは一つのノードで一回しか起動できません.繰り返し起動するとエラーが発生します.  -define(SERVER, ?MODULE).サービス名を持つサービスのプロセスは、名来で呼び出すことができます.サービス名がないのは、プロセス番号pidでしか呼び出しられません.通常名前のサービスプロセスは、モジュール名をサービス名として使用し、上のテンプレートで定義されたマクロ?SERVERを使用して、サービス名が必要なところにModuleを記入します.
第二のパラメータ?MODULEはモジュール名です.一般的にAPIとコールバック関数は同じファイルに書いてあります.  Argsは、本モジュールのモジュール名を表します.第3のパラメータinit/1は、コールバック関数init/1のパラメータであり、そのままOptionsに伝えられる.4番目のパラメータinit/1はいくつかのオプションであり、debug、タイムアウトなどのものを設定することができます.startは対応するコールバック関数init/1であり、一般的にはサーバー起動後の初期化作業を行い、初期の状態Stateを生成し、正常復帰は{ok,State}である.このStateはサーバー全体を通して、六つのコールバック関数を結びつけるきずなです.その値は最初にinit/1によって生成され、その後、3つのhandle関数によって修正され、毎回修正された後、また戻り値に戻して、次の呼出されるhandle関数に使用される.ignore{stop, Reason}またはstart_link/0に戻ると、サーバの起動は中止される.
注意したいのは、API関数とコールバック関数は、慣習的には同じファイルに書かれていますが、実行関数のプロセスは通常は違っています.上記のテンプレートでself()を使用すると、調整者のプロセス番号が表示され、init/1self()を使用すると、サーバのプロセス番号が表示される.
サーバを使う
三つのハンドルの先頭のコールバック関数は三つの異なるサーバーを使用する方式に対応しています.以下のとおりです
gen_server:call     -------------   handle_call/3
gen_server:cast     -------------   handle_cast/2
 !        -------------   handle_info/2
コールは戻り値の呼び出しがあります.castは戻り値なしの呼び出し、すなわち通知である.直接サーバーのプロセスに送るメッセージはhandle_から送られます.info処理
コール
コールは戻り値のコールであり、同期コールとも言われています.コールバック関数が戻ってくるまで、プロセスは呼び出された後、待ち続けます.その関数形式は  gen_server:call(ServerRef, Request, Timeout) -> Reply
第1のパラメータServerRefは、起動されたサーバであり、サーバ名またはサーバのpidである.第二のパラメータRequestは、コールバック関数handle_call に直接伝達される.
最後のパラメータTimeoutはタイムアウトであり、省略可能であり、デフォルト値は5秒である.マルチノードの場合、multi_callに使用されて、各ノードに同じ登録名を持つサービスプロセスから呼び出しを開始する機会がある.(この関数の文書上の表現はちょっと分かりにくいです.詳しくは  ここです)
コールは、コールバック関数handle_call/3の動作を指示するために使用されます.具体的な形式は  handle_call(Request, From, State)最初のパラメータRequestは、callによって送られてきたもので、プログラムを書く際の関心と処理の重点である.第二のパラメータFromはgen_である.Serverが伝えたのは、コールの元、つまりどのプロセスがコールを実行しましたか?  Fromの形式は{Pid, Ref}で、Pidはソースプロセス番号であり、Refは呼び出しの識別子であり、毎回呼び出しは違っていて、区別するために用いられる.Pidがあれば、ソースプロセスにメッセージを送る必要があるときに使用できますが、コールは戻り値があるので、戻り値を使ってデータを転送するのが一般的です.第3のパラメータStateはサーバの状態であり、これはinitまたは他のhandle関数によって生成され、必要に応じて修正した後、戻り値に戻すことができる.コール対応のコールバック関数handle_call/3は、通常の場合の戻り値{reply, Reply, NewState}である.  Replyは、コールの戻り値として伝えられ、NewStateはサーバの状態となる.また、{stop, Reasion, State}を使用してサーバの動作を中止することもできます.これは比較的少ないです.
callを使う時に注意しなければならないのは、2つのサーバーのプロセスはお互いにcallしないとロックされます.
cast
castは戻り値のない呼び出しで、一般的には通知と呼ばれています.これは「非同期」の呼び出しです.呼び出し後に直接届きます.  ok、コールバック関数の実行が完了するまで待つ必要はない.
その形式はgen_server:cast(ServerRef, Request)です.パラメータの意味はコールと同じです.戻りを待つ必要はないので、タイムアウトを設定する必要はありません.3番目のパラメータはありません.
マルチノードの場合、abcastを用いて、各ノードに指定された名前のサービスプロセスに通知することができる.(不思議なことに、なぜmulti_castと呼ばないですか?multi_callに似ているのに)
castたちの対応するコールバック関数はhandle_cast/2で、具体的にはhandle_cast(Msg, State)です.一つ目のパラメータはcastで送りました.二つ目はサーバーの状態です.コールと似ています.多くは言いません.handel_cast/2の戻り値は、一般的に{noreply, NewState}であり、これはサーバの状態を変更するために使用されるか、または{stop, Reason, NewState}であり、これはサーバを停止する.通常、サーバー停止命令はcastで実現することが多いです.
生の知らせ
生メッセージとは、コールやcastを通じて直接サーバーに送信しないというメッセージで、一部の本には「メッセージ付き」と訳されています.例えば、ネットソケットのsocketからのメッセージ、他のプロセス用!送ってきたメッセージ、サーバとのリンクを作るプロセスが死んでしまいました.{'EXIT', Pid, Why}などを送ってきました.普通はプログラムを書く時、できるだけAPIを使って、直接に使わないでください.サーバプロセスにメッセージを送るが、メッセージに依存するアプリケーションのようなsocketに対しては、生メッセージを処理しなければならない.
生メッセージはhandle_info/2を用いて処理され、具体的にはhandle_info(Info, State)である.その中でInfoは送ってきたメッセージの内容です.回答はhandle_castと同じです.
サーバを停止
上記で紹介されたhandle関数は、「stop」に戻ると、コールバック関数terminate/2を使用してバックグラウンド動作を行います.一般的には、開いているリソース、ファイル、ネットワーク接続をオフにして、ログを記録し、他のプロセスに「私は死にました」または「信春哥、満血復活」を通知します.
一番簡単なのは何もしないで、okに戻るといいです.
参考資料とリソース:
gen_serverの文書
OTP設計原則におけるgen_serverの紹介