🔖

C10K問題とは?

に公開

あらすじ

今回はとても短いですがNginxの誕生秘話になった問題について簡単にまとめます。

本題

それは遡ること1999年、ダン・ケーゲル(Dan Kegel)が「The C10K Problem」にて提起下そうな。
よくあるApache HTTPサーバーみたいな、「1接続1プロセス/1スレッド」の対応関係だと、Webサーバのハードウェアに余裕があってもレスポンス性能が大きく低下する問題。

Linux、Unix系のOSでは同時に起動できるプロセスに限界があるため限界が来ると待ちが発生し、これがC10K問題の主因になる。

Nginxの解決アプローチ

2004年にロシアのイーゴリ・シソエフ(Igor Sysoev)によってNginxが開発されて、解決策の一つとして世に出た。

Nginxの特徴

  • イベント駆動アーキテクチャ
    • 1つのワーカープロセスで数千の接続を非同期に処理できる
  • メモリ使用量
    • 接続ごとにプロセスやスレッドを作らないから効率良い
  • 並行性
    • epollkqueueなどのシステムコールを利用しているので平行性が高い
  • マスタープロセスとワーカープロセスに分かれる
    • リクエストをさばくのはワーカープロセス。
    • CPUコア数と同じだけ起動する
    • 各ワーカーは独立。

Nginxの弱み

  • CPU集約的な処理
    • ブロッキング処理があるとワーカー全体に影響する
  • モジュール開発が複雑
    • イベント駆動の非同期プログラミングが必要になる
  • デバッグがむずい

ので、リバースプロキシやローロバランサとして任せるケースが多い。

ApacheとNginx

項目 Apache Nginx
アーキテクチャ プロセス/スレッドベース イベント駆動・非同期
同時接続数 数百〜数千程度 数万〜数十万
メモリ使用量 1接続あたり8MB前後 1接続あたり256KB前後
CPU使用率 接続数に比例して増加 接続数が増えても比較的安定
モジュール 動的ロード可能 静的リンク(コンパイル時決定)
リバースプロキシ mod_proxy(追加設定必要) 標準機能として優秀
ロードバランシング mod_proxy_balancer 標準機能として高性能
SSL/TLS処理 普通 高速・効率的
どうやって実現してるん?
  1. ノンブロッキングソケット
// ソケットをノンブロッキングモードに設定
fcntl(socket_fd, F_SETFL, O_NONBLOCK);

// データがなくても即座に戻る
int result = read(socket_fd, buffer, size);
if (result == -1 && errno == EAGAIN) {
    // データなし、後で再試行
}
  1. システムコールに寄るイベント監視
// 監視対象のファイルディスクリプタを登録
epoll_ctl(epfd, EPOLL_CTL_ADD, socket_fd, &event);

// 複数のソケットを一度に監視 ← ここがポイントになるみたい
int ready_count = epoll_wait(epfd, events, MAX_EVENTS, timeout);

えらい点

  • 1回のシステムコールで数千のソケットを同時に監視できる
  • データが来たソケットだけ取り出せる
  1. イベントループの仕組み
while (true) {
    // 1. イベント待機(複数ソケット同時監視)
    ready_events = epoll_wait(epfd, events, MAX_EVENTS, -1);

    for (event in ready_events) {
        if (event.type == "新接続") {
            accept_connection();
        } else if (event.type == "データ読み取り可能") {
            read_data_nonblocking();
        } else if (event.type == "データ送信可能") {
            write_data_nonblocking();
        }
    }
}
typedef struct {
    int socket_fd;
    int state;  // READING_HEADERS, PROCESSING, WRITING_RESPONSE
    char *buffer;
    size_t buffer_pos;
    // ... その他の状態情報
} connection_t;

I/OバウンドなAPIハンドリングに向いてるというわけね。

その他、Node.jsのようなイベント駆動のソフトウェアでも対応できる。

おまとめ

C10K問題は従来のApacheのようなリクエストごとにプロセスやスレッドを作っていると、10K(1万)接続数は捌けん問題。
その解決策との一つとしてNginxが爆誕。
で、俺が生まれたってわけ。

Discussion