ksauzz weblog

technical note....

Erlang/OTPを利用したAcceptor Poolの実装

サーバを実装する際にはpreforkモデル云々でacceptor pool(worker pool?)を実装する事がある。 これをErlang/OTPで実装するにあたり、いくつかのOSSコードを参考にさせて頂いた。 これはその参考にさせて貰ったMochiweb, Kaiのacceptor pool実装に関するメモ。

Mochiweb

MochiwebはMochi Media, Inc.が公開しているのErlang製の軽量Http Server.というかWeb Framework。

mochiwebではproc_libを使ってacceptor poolを実装している。 acceptorがクラッシュしてもsupervisorに頼らずに直接再起動をする実装がなされている。

MochiwebのCall sequence.拡大図

実際にacceptor poolを生成しているのはmochiweb_socket_server:new_acceptor_pool/2。 ここでmochiweb_acceptor経由でproc_lib:spawn_link/3を連続的に実行することでacceptor poolを生成している。

mochiweb_socket_server:new_acceptor_pool/1
1
2
3
4
5
6
7
8
9
10
new_acceptor_pool(Listen,
                  State=#mochiweb_socket_server{acceptor_pool=Pool,
                                                acceptor_pool_size=Size,
                                                loop=Loop}) ->
    F = fun (_, S) ->
                Pid = mochiweb_acceptor:start_link(self(), Listen, Loop),
                sets:add_element(Pid, S)
        end,
    Pool1 = lists:foldl(F, Pool, lists:seq(1, Size)),
    State#mochiweb_socket_server{acceptor_pool=Pool1}.

acceptorがクラッシュした場合にはmochiweb_server_socketでクラッシュのメッセージを受け取り、 ここでacceptorを起動する仕組みになっている。当然、process_flag(trap_exit, true)は事前に呼ばれている。

Kai

国産のErlangで実装されたKVS. Amazon Dynamoクローン。slideshare

Kaiはsupervisorを利用してacceptor poolを実装している。supervisor:start_linkにacceptorの定義をリストにして渡すことで、 supervisorのworkerとしてacceptorを多数起動し、これによりpoolを実現している。当然、acceptorの再起動もSupervisor任せである。

acceptor定義のListを生成しているのは以下のkai_tcp_sup:acceptor_spec/6である。

kai_tcp_server_sup:acceptor_specs/6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
acceptor_specs(
  ListenSocket, State, {Dest, Name}, MonitorBaseName, Mod, Option
) ->
    #tcp_server_option{
        max_processes = MaxProcesses,
        shutdown      = Shutdown
    } = Option,
    MonitorName = case Dest of
      local   -> MonitorBaseName;
      _Global -> {Dest, MonitorBaseName}
    end,
    lists:map(
        fun (N) ->
            AcceptorName = build_acceptor_name(Name, N),
            {
                AcceptorName,
                {
                    kai_tcp_server_acceptor,
                    start_link,
                    [
                        {Dest, AcceptorName},
                        ListenSocket,
                        State,
                        MonitorName,
                        Mod,
                        Option
                    ]
                },
                permanent,
                Shutdown,
                worker,
                []
            }
        end,
        lists:seq(1, MaxProcesses)
    ).

まとめ

Acceptorの生成

  • Mochiweb: proc_libで個別に起動
  • kai: supervisor任せ

Acceptorの再起動

  • Mochiweb: 自前実装
  • kai: supervisor任せ

なんか他のコードも読んだ気がしたけど、すっかり忘れてしまったので以上2つだけのまとめ。

ちなみに自分が実装する際にはKaiの方式を利用させて貰った。Supervisor便利である。

Comments