Comet – 1,000,000ユーザーの負荷を持つMochiwebのアプリケーション。 パート1/3

パート2

パート3



このシリーズの記事では、Mochiwebが多数のオープン接続を処理する方法について経験的に学んだことを共有し、Mochiwebを使用してCometアプリケーションを作成する方法を示します。各接続はルーターに登録されます。 最終的には、1,000,000の同時接続を処理でき、これに必要なメモリ量を確認できる実用的なアプリケーションになります。



最初の部分:

•単純なCometの作成-10秒ごとにクライアントにメッセージを送信するアプリケーション。

•多数の接続をサポートするためのLinuxカーネルの構成。

•テストユーティリティを作成して、多数の接続を作成します。

•必要なメモリ量の決定。



このシリーズの以下のパートでは、実際のシステムの構築方法、メモリ使用量を削減するための追加のコツを示し、100,000および1,000,000の並列接続を使用したテストを示します。



bashに精通しており、Erlangに少し精通していることを前提としています。



テストアプリケーションの作成



要するに:



1. Mochiwebをインストールします。

2.実行:/your-mochiweb-path/scripts/new_mochiweb.erl mochiconntest

3.cd mochiconntestおよびsrc / mochiconntest_web.erlの変更



このコード(mochiconntest_web.erl)は接続のみを受け入れ、グリーティングメッセージを送信し、各クライアントに10秒ごとに1つのメッセージを送信します。



-module(mochiconntest_web). -export([start/1, stop/0, loop/2]). %% External API start(Options) -> {DocRoot, Options1} = get_option(docroot, Options), Loop = fun (Req) -> ?MODULE:loop(Req, DocRoot) end, %we'll set our maximum to 1 million connections. mochiweb_http:start([{max, 1000000}, {name, ?MODULE}, {loop, Loop} | Options1]). stop() -> mochiweb_http:stop(?MODULE). loop(Req, DocRoot) -> "/" ++ Path = Req:get(path), case Req:get(method) of Method when Method =:= 'GET'; Method =:= 'HEAD' -> case Path of "test/" ++ Id -> Response = Req:ok({"text/html; charset=utf-8", [{"Server","Mochiweb-Test"}], chunked}), Response:write_chunk("Mochiconntest welcomes you! Your Id: " ++ Id ++ "\n"), %% router:login(list_to_atom(Id), self()), feed(Response, Id, 1); _ -> Req:not_found() end; 'POST' -> case Path of _ -> Req:not_found() end; _ -> Req:respond({501, [], []}) end. feed(Response, Path, N) -> receive %{router_msg, Msg} -> % Html = io_lib:format("Recvd msg #~w: '~s' ", [N, Msg]), % Response:write_chunk(Html); after 10000 -> Msg = io_lib:format("Chunk ~w for id ~s\n", [N, Path]), Response:write_chunk(Msg) end, feed(Response, Path, N+1). %% Internal API get_option(Option, Options) -> {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
      
      







Mochiwebアプリケーションの起動



 make && ./start-dev.sh
      
      







デフォルトでは、mochiwebはポート8000​​でリッスンします。自宅のコンピューターで作業している場合は、任意のWebブラウザーを使用して確認できます: http:// localhost:8000 / test / foo



以下は、コマンドラインのテストです。

 $ lynx --source "http://localhost:8000/test/foo" Mochiconntest welcomes you! Your Id: foo<br/> Chunk 1 for id foo<br/> Chunk 2 for id foo<br/> Chunk 3 for id foo<br/> ^C
      
      







はい、動作します。



多数のtcp接続用のLinuxカーネルのセットアップ



多数の接続を使用してテストを実行する前に時間を節約してカーネルを構成します。そうしないと、テストが機能せず、多くの「ソケットメモリ不足」メッセージが表示されます。



sysctl設定は次のとおりです。

 # General gigabit tuning: net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.tcp_syncookies = 1 # this gives the kernel more memory for tcp # which you need with many (100k+) open socket connections net.ipv4.tcp_mem = 50576 64768 98152 net.core.netdev_max_backlog = 2500 # I was also masquerading the port comet was on, you might not need this net.ipv4.netfilter.ip_conntrack_max = 1048576
      
      







それらを/etc/sysctl.confに入れてから、sysctl -pを実行して適用します。 システムを再起動する必要はありません。これで、カーネルはより多くのオープン接続を処理できるようになります。



多数の接続を作成する



これを行うには多くの方法があります。 Tsungは非常にセクシーで、多くのリクエスト(ab、httperf、httploadなど)を使用してhttpdスパムを作成する性的な方法でさえ十分です。 それらのどれも私たちのアプリケーションをテストするのに理想的ではないので、これらの目的のためにErlangで基本的なテストを書きました。



私たちができるからといって、そうすべきだという意味ではありません...接続ごとに1つのプロセスは間違いなくコストがかかります。 1つのプロセスを使用してファイルからURLをロードし、別のプロセスを使用して接続を確立し、すべてのhttp接続からメッセージを受信しました(1つのプロセスを10秒ごとにレポートを印刷するタイマーとして使用)。 サーバーから受信したデータはすべて破棄されますが、カウンターが増加するため、配信されたHTTPメッセージの数を追跡できます。



 -module(floodtest). -export([start/2, timer/2, recv/1]). start(Filename, Wait) -> inets:start(), spawn(?MODULE, timer, [10000, self()]), This = self(), spawn(fun()-> loadurls(Filename, fun(U)-> This ! {loadurl, U} end, Wait) end), recv({0,0,0}). recv(Stats) -> {Active, Closed, Chunks} = Stats, receive {stats} -> io:format("Stats: ~w\n",[Stats]) after 0 -> noop end, receive {http,{_Ref,stream_start,_X}} -> recv({Active+1,Closed,Chunks}); {http,{_Ref,stream,_X}} -> recv({Active, Closed, Chunks+1}); {http,{_Ref,stream_end,_X}} -> recv({Active-1, Closed+1, Chunks}); {http,{_Ref,{error,Why}}} -> io:format("Closed: ~w\n",[Why]), recv({Active-1, Closed+1, Chunks}); {loadurl, Url} -> http:request(get, {Url, []}, [], [{sync, false}, {stream, self}, {version, 1.1}, {body_format, binary}]), recv(Stats) end. timer(T, Who) -> receive after T -> Who ! {stats} end, timer(T, Who). % Read lines from a file with a specified delay between lines: for_each_line_in_file(Name, Proc, Mode, Accum0) -> {ok, Device} = file:open(Name, Mode), for_each_line(Device, Proc, Accum0). for_each_line(Device, Proc, Accum) -> case io:get_line(Device, "") of eof -> file:close(Device), Accum; Line -> NewAccum = Proc(Line, Accum), for_each_line(Device, Proc, NewAccum) end. loadurls(Filename, Callback, Wait) -> for_each_line_in_file(Filename, fun(Line, List) -> Callback(string:strip(Line, right, $\n)), receive after Wait -> noop end, List end, [read], []).
      
      







作成する各接続にはポートが必要で、デフォルトではその数は1024に制限されています。問題を回避するために、シェルのulimitパラメーターを変更する必要があります。 これは/etc/security/limits.confで行われますが、システムの再起動が必要です。

 $ sudo bash # ulimit -n 999999
      
      







利用可能なポートの範囲を最大に増やすこともできます。

 # echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
      
      





テストユーティリティのURLを含むファイルを生成します。

 ( for i in `seq 1 10000`; do echo "http://localhost:8000/test/$i" ; done ) > /tmp/mochi-urls.txt
      
      







次に、Erlangコンソールを使用してユーティリティをコンパイルおよび実行します。

 erl> c(floodtest). erl> floodtest:start("/tmp/mochi-urls.txt", 100).
      
      







コードは1秒あたり10個の新しい接続を確立します(つまり、100ミリ秒ごとに1つの接続)。

統計は{Active、Closed、Chunks}として表示されます。Activeは現在確立されている接続の数、Closedは何らかの理由で完了した接続の数、ChunksはMochiwebから送信されたメッセージの数です。 すべてのアクティブな接続には多くのメッセージ(10秒ごとに1)が含まれるため、Closedは0のままであり、ChunksはActiveよりも大きくなければなりません。



10,000のアクティブな接続を持つMochiwebプロセスのサイズは450 MBでした-これは各接続で45 KBです。 マシンのCPU使用率は実質的にゼロでした。



結論



これが最初の試みでした。 各接続の45 Kbはかなり大きいようです-おそらく、各接続に4.5Kbを費やして同様のことができるlibeventを使用してCで何かを構築できます(これは単なる仮定です。コメントを残す)。 コードの量と、Cに比べてErlangでこれを行うのにかかった時間を考慮すると、使用されるメモリの増加は許されると思います。



今後の記事では、メッセージルーターを示し(mochiconntest_web.erlの25行目と41-43行目をコメント解除できる)、メモリ使用量を削減するいくつかの方法について説明します。 また、100,000および1,000,000接続のテスト結果を示します。



更新: パート2



All Articles