WEBサーバーを比較してみる

はてなブックマーク -WEBサーバーを比較してみる

はじめまして、kaoruです。

ゆめみでは伝統的にWEBサービスを提供するにあたって、Apache HTTP Server+PHPというミドルウェア構成を利用してきました。

近年では単純な閲覧型のWEBサイトを越えて、SNSやコミュニティーサービスなど、ユーザーが自らリアルタイムに参加できるようなWEBサービスが一般的になってきています。このようなWEBサービスを提供するためには、膨大なトラフィックをリアルタイムに処理できるようなシステム構成が求められています。

 このような背景もあり、今回は今までよく使っていたApache HTTP Serverと最近よく目にするようになってきたnginxというWEBサーバーの基本的な性能を比較してみましたのでご紹介してみたいと思います。

なお、今回の性能の計測は簡易的なものであり、参考程度のものであることはご了承ください。

20100331.WEB-server-compara.gif

グラフの縦軸は1秒間あたりに処理したリクエスト数の平均、横軸は同時接続数で、localhostからApacheBenchを使って計測しました。上の3つはApache HTTP Serverで、configure時に指定するMPMオプションを、prefork, worker, eventと変えたものです。nginxは最近よく聞くようになったWEBサーバーです。Boost.AsioはC++のライブラリーで、非同期のネットワーク処理ができます。ライブラリーに付属しているサンプルプログラムを試してみました。

結果を見ると、Apache HTTP Serverよりもnginxはコネクション数にかかわらず性能が高いことが分かります。また、グラフからはわかりませんが、アーキテクチャーの違いからか、nginxの方がリソースの消費量が大幅に少なくなっているようです。Boost.Asioはライブラリーなので他のアプリケーションと単純な比較はできませんが、アプリケーションを実装するプラットフォームとしてはかなり高性能であると言えます。

今回は、手始めとして、固定の簡単なテキストファイルを取得する試験を行いました。動的にコンテンツを返さなければいけないかどうかや、使いたい機能によってもかわってきますが、nginxで必要なことができるなら、とても魅力的な選択肢になると思います。

今回の試験に用いたサーバーのスペックとアプリケーションのバージョンを挙げておきます。

Intel(R) Xeon(R) 2.33GHz (64bit, 8コア)
Linux 2.6.18 x86_64
Mem:   1048576k total

httpd-2.2.14
nginx-0.8.34
boost_1_42_0
日時: 2010年04月01日 17:25 | コメント (0) | トラックバック (0)

C++/Boostとコルーチンを使って非同期I/Oを実現(C10K問題もこれで解決?)

はてなブックマーク -C++/Boostとコルーチンを使って非同期I/Oを実現(C10K問題もこれで解決?)

みなさん、こんにちは、
kaoruです。

今回は、C10K問題 (参考1, 参考2) を回避するためにコルーチンを使う方法をご紹介したいと思います。

また、ちょっと実用的な例として、言語としてC++、ライブラリーとしてBoost.AsioとBoost.Asio付属のコルーチンを利用して、 ApacheBenchもどきを実際に作ってみました。

ネットワークプログラミングを調査しようと思ったきっかけは、 技術評論社から出ている「WEB+DB PRESS Vol.55」の 「モダンネットワークプログラミング入門」という記事です。 こちらには、ネットワークプログラミングの基本的な設計モデルが分かりやすく説明されているため、とても参考になります。

ご存じのように、C10K問題とは、ハードウェアに余裕があるにもかかわらず、同時コネクション数が1万前後になると、パフォーマンスが急速に劣化するという問題です。 これは、ネットワークアプリケーションの伝統的な設計ではコネクション数に比例する数のプロセスやスレッドが生成されることに原因があります。 例として、Apache HTTP Serverのpreforkモードでは、コネクション数に比例する数の子プロセスが起動して、リクエストを処理します。 同時コネクション数が1万前後にするには、子プロセスを1万前後起動しなければならず、これはあまり現実的な数字ではありません。

C10K問題を回避するためには、コネクション数とプロセス数やスレッド数が比例しないような設計をしなければなりません。
これには、非同期I/Oを利用したイベント駆動モデルが有効です。 これはすなわち、接続成功、ヘッダーの読み取り完了、レスポンスの送出完了などといったイベントの発生毎にあらかじめ登録しておいたイベントハンドラを呼び出してもらう方法です。 この方法は非常に効率がよいのですが、本来はひと続きの処理がたくさんのイベントハンドラの中にばらばらになって散らばってしまうため、 処理の流れが分かりにくくソースコードの可読性が悪いという欠点がありました。

コルーチンと呼ばれる手法を使うと、非同期I/Oを利用したイベント駆動モデルでありながら、同期モデルとほぼ同じようにプログラミングを行うことができ、 処理の流れが非常に分かりやすくなります。
コルーチンとは、プログラムの流れの中で中断・再開ができたり、スレッドのように制御を分岐したりできるプログラミングのしくみです。Luaなどの一部の言語では言語レベルでサポートされていますが、C/C++ではライブラリーを使って実装します。

今回作ったソースコードの一部を掲載します。 (この記事の下のリンクから全てのソースコードをダウンロードできます。)


#include "yield.hpp" // Enable the pseudo-keywords reenter, yield and fork.

void client::operator()(

        boost::system::error_code ec,
        std::size_t length,
        tcp::resolver::iterator endpoint_iterator)
{

    static int n = 0;
    static int c = 0;

    if (ec && ec != boost::asio::error::eof) {

        std::cerr << ec.message() << std::endl;
        return;
    }

    // On reentering a coroutine, control jumps to the location of the last
    // yield or fork. The argument to the "reenter" pseudo-keyword can be a
    // pointer or reference to an object of type coroutine.
    reenter (this)
    {
        for (c = 0; c < concurrency_level_ && c < num_request_; ++c) {

            fork client(*this)();

            if (is_child())
                break;
        }

        if (is_parent())
            return;

        while (n < num_request_) {

            socket_.reset(new boost::asio::ip::tcp::socket(io_service_));

            yield socket_->async_connect(endpoint_, *this);

            n++;
            if (n % 1000 == 0)

                cout << "Completed " << n << " requests" << endl;

            // Send the request.
            yield boost::asio::async_write(
                *socket_,
                boost::asio::buffer(

                    request_.c_str(),
                    request_.size()
                ),
                *this
            );

            // Start reading remaining data until EOF.
            static char dummy_buffer[1 * 1024 * 1024]; // Dirty buffer.

            yield boost::asio::async_read(
                *socket_,
                boost::asio::buffer(dummy_buffer, sizeof(dummy_buffer)),

                boost::asio::transfer_all(),
                *this);

            // Initiate graceful connection closure.
            socket_->shutdown(tcp::socket::shutdown_both, ec);
        }
    }
}

#include "unyield.hpp" // Disable the pseudo-keywords reenter, yield and fork.

#include "yield.hpp" から #include "unyield.hpp" の区間では、 reenter, yield, fork という3種類のキーワードが有効になります。
(実際にはプリプロセッサーマクロですが、キーワードであるかのように使えます。)

コルーチン化したいコードの部分をreenter { } で囲み、 async_connect, async_read, async_write といった非同期I/Oを行う関数の手前に、 yieldキーワードをつけます。(下図の(1),(2))
forkキーワードは、UNIXのforkシステムコールと同じような感じに、コルーチンオブジェクトをコピーして子供のコルーチンを生成します。(下図の親・子) is_child(), is_parent()という関数で、子として実行しているのか、親として実行しているのかがわかりますので、 処理を分岐することが可能です。forkキーワードを使っても、実際にはプロセスやスレッドは生成されませんので、非常に軽量です。

yieldキーワードを使うと、非同期I/Oが完了したときに次の行から処理が再開されます。すなわち、下図で説明すると、まず最初に(1)から(2)まで処理が進み、ここでyieldキーワードがありますので、async_connect関数(非同期接続を行う関数です。)が実行された後、一旦reenterブロックを抜けます。抜ける際に、再開する場所として(2)の場所が自動的に保存されます。(2)のasync_connectの引数として*thisが渡されていますので、接続完了時にもう一度(1)に制御が来ます。ここで、先ほど(2)まで処理が進んだことを保存していましたから、(1)から(2)に制御が移動し、(2)から処理が再開されるという仕組みです。

co-routine.gif

以前はこのような処理を記述する際には、あらかじめ同時接続数と同じ数のスレッドを生成した後、 同期処理を行うAPIを用いて記述することが多かったのですが、今回はスレッドは明示的に生成することなく実装ができました。

この例のように、まるで同期処理を書いているかのような感覚でプログラミングを行うことができるのがうれしいところです。

前回の記事「WEBサーバーを比較してみる」と同じ環境で、nginxサーバーに対してリクエストを発行したところ、 ApacheBench を若干上回る 9345.84 req/s という結果を得ました。

また、機能が違うため正しい比較はできませんが、同時コネクション数を10000程度まで増やしても、
ApacheBenchよりもメモリー使用量が少なくなりました。

今回、コルーチンを使うことで、ApacheBenchもどきのクライアントを非常に簡単に作ることができました。
みなさんが、非同期I/Oを使ったプログラミングをされる際に、少しでも参考になるところがありましたら幸いでございます。

今回作成したソースコード
ファイルをダウンロード

ビルド方法
※ビルドには、あらかじめBoost C++ Librariesをインストールして、適切にパスの設定をする必要があります。

Windows環境の場合
Visual Studio 2008 で、http_stress_tool.slnを開いてください。

UNIX環境の場合
Makefile等は用意していません。以下のようなコマンドでビルドができます。
g++ -O3 -o http_stress_tool http_client.cpp main.cpp -Wall -Wextra -lboost_system -lboost_date_time


日時: 2010年04月19日 16:00 | コメント (0) | トラックバック (0)

TokyoTyrantとMySQLはSSDによって性能が上がるのか?

はてなブックマーク -TokyoTyrantとMySQLはSSDによって性能が上がるのか?

お久しぶりです。kouです。


先日、社内勉強会を実施しました!

今回は「Tokyo TyrantとMySQLはSSDによって性能があがるのか?」をテーマにちきんさんが社内講師を務めました。現在、ゆめみではソーシャルアプリ開発を進めていて、そこで利用するTTやSSDの技術検証を行った結果をみんなで共有です。


勉強会の詳細はスライドをご覧ください。




以下、勉強会の様子。


study_201004_01.jpg

study_201004_02.jpg


次回はUstreamで中継してみたいです。

日時: 2010年04月23日 18:10 | コメント (0) | トラックバック (0)
« 2010年02月 | 2010年04月 | 2010年05月 »
ゆめみ深田浩嗣のブログ Mercury mobmail
YUMEMI Labs Sweet