2

私は1人のスーパーバイザーと数人のワーカーでアプリケーションを開発しています。これらのワーカーはそれぞれ、tcpソケットを開き、リッスンを実行してから接続を受け入れ、クライアントが到着すると各クライアントのプロセスを生成します(これらを監視してもかまいません)。

アプリケーション構成でリスニングアドレスを構成できるようにしたいので、必要な数のアドレスをリッスンできます(少なくとも1つのアドレスが必要ですが、任意の数を指定できます)。各アドレスは、IP(またはホスト名)とポートアドレスです。これまでのところ、新しいことは何もありません。

私の質問は、未知の数の労働者をどのように宣言し、開始し、監督するかについてです。私の解決策は、次のように、スーパーバイザーコードでinit / 1関数の結果を(実行時に)動的に生成することでした。

-define(
   CHILD(Name, Args),
   {Name, {
       ?MODULE, start_listen, Args
       }, permanent, 5000, worker, [?MODULE]
   }
).

init([]) ->
   {ok, Addresses} = application:get_env(listen),
   Children = lists:map(
       fun(Address) ->
           {X1,X2,X3} = os:timestamp(),
           ChildName = string:join([
               "Listener-",
               integer_to_list(X1),
               integer_to_list(X2),
               integer_to_list(X3)
           ], ""),
           ?CHILD(ChildName, [Address])
       end,
       Addresses
   ),
   {ok, { {one_for_one, 5, 10}, Children }}.

これにより、各ワーカーの名前を動的に生成し、ワーカー定義も生成できます。それで:

  1. この解決策は受け入れられますか?それほどエレガントではないように思えます。この種のユースケースの標準的な解決策(またはベストプラクティスなど)はありますか?

  2. スーパーバイザーにワーカーを動的に追加できる「simple_one_for_one」戦略について知っています。しかし、ワーカーの名前を動的に生成するためにも使用できますか?「one_for_one」を使用する独自のソリューションの代わりに「simple_one_for_one」を使用する方が(とにかく)良いですか?(繰り返しますが、この特定の状況では)。

よろしくお願いします。長い投稿でごめんなさい!:)

4

2 に答える 2

5

simple_one_for_one:

ワーカーの init 関数で、ワーカーをテーブルに登録して、名前を PID に関連付けることができるため、名前から pid を取得できます。

globalモジュール (またはgproc !) を使用して、名前を pid に関連付けることができます。プロセスが終了すると、名前はglobalまたはによって自動的に削除されるgprocため、スーパーバイザーが子プロセスを再起動すると、名前が使用可能になります。

Supervisor:start_child/2 の引数リスト (2 番目のパラメーター) に名前を渡します。

を使用simple_one_for_oneすると、スーパーバイザーの初期化後にリスナーを動的に追加できます。

この機能が必要な場合simple_one_for_oneは、良い解決策です。

sup の init 関数内の動的コンテンツのソリューションに固執する場合、次のようにコードをきれいにすることができます。よりエレガントに見える場合とそうでない場合があります。

-define(
   CHILD(Name, Args),
   {Name, {
       ?MODULE, start_listen, Args
       }, permanent, 5000, worker, [?MODULE]
   }
).

generate_names(Adresses) -> generate_names(Adresses, 1, []).

generate_names([], _, Acc) -> Acc;
generate_names([Addr|Addresses], ID, Acc) -> generate_names(Addresses, ID+1, [{id_name(ID), Addr}|Acc]).

id_name(ID) -> "listener-" ++ integer_to_list(ID).

init([]]) ->
   {ok, Addresses} = application:get_env(listen),
   Children = [?CHILD(Name, Address) || {Name, Address} <- generate_names(Addresses)],
   {ok, { {one_for_one, 5, 10}, Children }}.

またはlists:foldl、これらすべての小さな関数の代わりに a を使用して、コードを短くします。

とにかく、initのArgsリストにアドレスを渡し、initget_env内で呼び出して純粋に保つことはしません。

于 2012-05-04T22:50:13.170 に答える
0

あなたのコードから、環境から子の数を取得したいことがわかります。rabbitmq オープンソース プロジェクトの worker_pool_sup.erl のソース コードで、ほぼ同様の要件コードを読みましたが、コードは非常に洗練されており、参考になると思います。関連する 3 つのファイルは、worker_pool_sup.erl、worker_pool_worker.erl、work_pool.erl です。

次のコードは、worker_pool_sup.erl からのものです。

init([WCount]) ->
    {ok, {{one_for_one, 10, 10},
          [{worker_pool, {worker_pool, start_link, []}, transient,
            16#ffffffff, worker, [worker_pool]} |
           [{N, {worker_pool_worker, start_link, [N]}, transient, 16#ffffffff,
             worker, [worker_pool_worker]} || N <- lists:seq(1, WCount)]]}}.

使い方はworker_pool.erlより以下のコード

get_worker_pid(WId) ->
    [{WId, Pid, _Type, _Modules} | _] =
        lists:dropwhile(fun ({Id, _Pid, _Type, _Modules})
                              when Id =:= WId -> false;
                            (_)               -> true
                        end,
                        supervisor:which_children(worker_pool_sup)),
    Pid.
于 2012-05-04T21:11:04.230 に答える