地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Nginx】イベント駆動アーキテクチャとは

NginxとかNode.jsで使われてるあれ

イベント駆動アーキテクチャとは

イベント駆動とは「イベント」と呼ばれるアプリや端末上で起きた出来事に対して処理を行うプログラムの実行形式のことです。
イベントがトリガーとなって、関数やメソッドが実行される、というイメージです。
javascriptでのプログラミングもobjective-Cでのプログラミングも、このイベント駆動の仕組みを理解するとプログラミングがしやすくなります。

qiita.com

NginxやらNode.jsで採用されているイベント駆動モデル

httpリクエストを1プロセス1スレッドで処理することでc10k問題などを回避している。

基本的には1プロセスでは同時に複数の処理を行うことができないが非同期IO/ノンブロッキングIO/IOの多重化といった技術を組み合わせて 1プロセスで複数のリクエストを捌いている。

今回はIOの多重化の話。 IOの多重化とは1つのプロセス内で複数のファイルディスクリプタを継続的に監視して読み込み可能なディスクリプタがあれば処理を行うもの 具体的にpoll(2)やselect(2)を使って行われる。

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#define _GNU_SOURCE         /* feature_test_macros(7) 参照 */
#include <signal.h>
#include <poll.h>

int ppoll(struct pollfd *fds, nfds_t nfds, 
        const struct timespec *timeout_ts, const sigset_t *sigmask);

pollの定義は上記の通り。 ファイルディスクリプタの配列を受けとって読み込み可能なディスクリプタがあれば処理をするというような実装を実現できる。 (nginxやnodeではソケットの生成時にノンブロッキングオプションを付加して生成。)

nginx実装

せっかくなのでnginxの実装をみてみる pollを読んでるのは具体的には下記

static ngx_int_t
ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
    int                 ready, revents;
    ngx_err_t           err;
    ngx_uint_t          i, found, level;
    ngx_event_t        *ev;
    ngx_queue_t        *queue;
    ngx_connection_t   *c;

// 省略

    ready = poll(event_list, (u_int) nevents, (int) timer);
    /*
        event_list : ngx_poll_add_event()などでイベントを追加
        nevents : イベントの数
        timer : ngx_process_events_and_timers()関数にて定義
    */

イベントごとにリストを生成してpollを呼び出している。 呼び出したあとはreadyの値をみて「POLLOUT」「POLLIN」のビットをみて処理を分けている。

        // ngx_connection_tはコネクションの状態を保持する構造体
        if ((revents & POLLIN) && c->read->active) {
            found = 1;

            ev = c->read;
            ev->ready = 1;
            ev->available = -1;

            queue = ev->accept ? &ngx_posted_accept_events
                               : &ngx_posted_events;

            ngx_post_event(ev, queue);
        }

        if ((revents & POLLOUT) && c->write->active) {
            found = 1;

            ev = c->write;
            ev->ready = 1;

            ngx_post_event(ev, &ngx_posted_events);
        }

その他

ネットワークIOでの非同期/ノンブロッキングは理解はしやすいが実装するのはとても難しそう。。 nodejsも読みたかったけどjsで書かれていたので断念しますw

github.com