swagger-codegenですわっとscaffoldingせん
こんにちは. 今回は(swagger-codegen)https://github.com/swagger-api/swagger-codegenを使ったりカスタマイズしてみようの会です.
swagger-codegenとは?
リポジトリのへのリンク github - swagger-codegen
Overview This is the swagger codegen project, which allows generation of API client libraries (SDK generation), server stubs and documentation automatically given an OpenAPI Spec. Currently, the following languages/frameworks are supported:
swagger-codegenは,swaggerエコシステムのツールの一つであり, OpenAPI3.0仕様(もしくはSwagger2.0仕様)から, クライアントライブラリやサーバのスタブ, ドキュメントを生成するツールです.
swagger自体とその周辺ツールについては, おおむね知っている前提で話を進めていきます.
swagger-codegenを使う動機
今回はサーバ側コードのscaffoldingを行うことを目的にswagger-codgenを使用します.
リポジトリのreadmeにあるように,基本的にはswagger-codegenで生成したものは プロダクション環境ではドキュメントとクライアントライブラリのみを使用することを想定されています(サーバはスタブのみ)
しかしながら,スタブ生成の出力先言語/フレームワーク一覧を見てみると, 以下のようになっています.
Server stubs: Ada, C# (ASP.NET Core, NancyFx), C++ (Pistache, Restbed), Erlang, Go, Haskell (Servant), Java (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework, PKMST), Kotlin, PHP (Lumen, Slim, Silex, Symfony, Zend Expressive), Python (Flask), NodeJS, Ruby (Sinatra, Rails5), Rust (rust-server), Scala (Finch, Lagom, Scalatra)
注目したのは
のように,scafoldingに対応していないフレームワークのコード生成も行ってくれることです. そこで,今回はswagger-codegenでサーバスタブを生成し, そのコードを足場(scafold!)として開発を行っていきます.
やってみよう!
コード生成自体は,Open API仕様のjsonかyamlを用意すれば簡単に行えます. 今回はswaggerプロジェクト公式で配布しているサンプルを使用します.
1. OpenAPI仕様のファイルの準備
サンプルへのリンク このリンクから今回使用するサンプルファイルを取得します. 自分で用意する場合は,swagger-editor等のツールを使用して用意するのがいいと思います. ~(ただ正直swagger-editorを使ってもかなり定義する部分があるので結構大変です)~
2. コード生成
コード生成はおおむね2つの方法があります.
- swagger-editorで生成
- ローカルでビルド or jarを取得して生成
swagger-editorで生成
先ほど紹介したswagger-editorにコード生成機能が実装されています.editorにjson or yamlをはりつけて上部リンクの Generate ServerかGenerate Clientから生成対象の言語/フレームワークを選択すれば,生成されたコードがzip圧縮したものが入手できます.
ローカルで生成
swagger-codegenのreadme内getting Startedにあるように,ソースコードを取得してからビルドすることで,実行可能jarを入手できます.ただ,ビルド済みのjarがmavenリポジトリ 上に配置されているため,ここからjarを取得する方が早いです.
コード生成コマンド
jarを取得した後は,以下のコマンドでコード生成ができます.
java -jar swagger-codegen-cli.jar generate -i [swagger.yaml or json]\ -l [生成対象の言語/フレームワーク] \ (-o [生成先フォルダ]\) (-c [config.jsonへのパス]\)
生成可能言語/フレームワーク一覧表示
生成可能な対象は以下のコマンドでリストを見ることができます
java -jar swagger-codegen-cli.jar langs
生成対象言語/フレームワーク毎の設定項目表示
生成対象によってはconfig.json
でオプションを設定できます.
例えばjava-play-framework
向けの設定項目を調べるには以下のコマンドを使います.
java -jar .\hoge.jar config-help -l java-play-framework
生成結果
実際にコードを生成すると,フレームワークに応じたファイルが出力されます. これら生成されるコードを利用することでmodelやapiのための記述量を減らすことができます.
java -jar .\hoge.jar generate -l java-play-framework -i http://petstore.swagger.io/v2/swagger .json -o out [main] INFO io.swagger.parser.Swagger20Parser - reading from http://petstore.swagger.io/v2/swagger.json ... └─out ├─.swagger-codegen ├─app │ ├─apimodels │ ├─controllers │ └─swagger ├─conf ├─project └─public
生成されるコードについては生成対象ごとのテンプレート次第です. なので気に食わなかった場合はテンプレートのカスタマイズ,ロジックのカスタマイズを行っていきましょう.
後半(カスタマイズ編)に続く(予定)
参考リンク
nginxのソースコードを読む1
今回はnginxのメインループをたどっていきます。
ソースコードの入手
ソースコードは(ここ)https://nginx.org/en/download.htmlからダウンロードできます。 今回は1.11.2を利用しています。
ソースのディレクトリ構造は以下の通りです。
/ ├── core(エントリポイントや内部で汎用的に使用するデータ構造等) ├── event(イベント処理の仕組み) │ └── modules(イベントを通知する際に選べるシステムコール毎のモジュール実装) ├── http(httpを処理する部分?) │ ├── modules(ssl, auth, アクセス制限等configで設定するような細かな項目のモジュール) │ │ └── perl(perlモジュール?) │ └── v2(不明) ├── mail(メール関係 stmp, imap等) ├── misc(google_perftoolとそのためのc++用の何かがある) ├── os │ └── unix(ソケットやforkの処理等システムコールを用いる処理) └── stream(不明)
ここからざっくばらんにnginxのソースコードを読んでいきます。
エントリポイントからメインループまで
nginxはmaster-worker構成のためメインループが複数存在することになりますが、 まずはそれぞれのメインループに入るまでと入ってからの処理を大まかに観ていきたいと思います。
まずnginx起動時、/core/nginx.cのmain関数から実行されます。
//core/nginx.c main(int argc, char *const *argv){ /* ポインタ作成 */ /* debugとerrorの初期化 */ /* オプションのパース */ /* 各種初期化及びシグナル送信等*/ if (ngx_process == NGX_PROCESS_SINGLE) { ¦ ngx_single_process_cycle(cycle); } else { ¦ ngx_master_process_cycle(cycle); } return 0; }
いろいろ省略していますが、 -vや-tや-s等のすぐに処理が終わる場合でなければ、 上記のngx_single_process_cycleかngx_master_process_cycleが呼ばれます。 single_processは詳細は知りませんがnginxをシングルプロセスで動かすオプションを つけた場合に実行されると思われます。 そこで今回はmaster_processの方に注目して追っていきます。
ngx_master_process_cycleは/os/unix/ngx_process_cycle.cに定義されています。
/os/unix/ngx_process_cycle.c
void ngx_master_process_cycle(ngx_cycle_t *cycle){ /* 変数の初期化 */ /* シグナル受信時の処理を設定 */ /* プロセスのタイトル等設定 */ ngx_setproctitle(title); /* コンフィグの読み取り */ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); /* ワーカプロセスの起動とキャッシュマネージャの起動予約 */ ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); /* masterのメインループ */ for ( ;; ) { /* 何か */ sigsuspend(&set); /* 何か */ if (ngx_reap) { /* 子プロセスの削減? */ live = ngx_reap_children(cycle); } if (!live && (ngx_terminate || ngx_quit)) { /* マスタの終了 */ ngx_master_process_exit(cycle); } if (ngx_terminate) { /* 子を終了させる */ /* 省略 */ if (delay > 1000) { ngx_signal_worker_processes(cycle, SIGKILL); } else { ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_TERMINATE_SIGNAL)); } continue; } if (ngx_quit) { /* 子プロセスへシャットダウンシグナル */ ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); ls = cycle->listening.elts; for (n = 0; n < cycle->listening.nelts; n++) { if (ngx_close_socket(ls[n].fd) == -1) { ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, ngx_close_socket_n " %V failed", &ls[n].addr_text); } } cycle->listening.nelts = 0; continue; } if (ngx_reconfigure) { /* 設定ファイルのリロード */ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_JUST_RESPAWN); ngx_start_cache_manager_processes(cycle, 1); /* allow new processes to start */ ngx_msleep(100); live = 1; ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); if (ngx_restart) { ngx_restart = 0; ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); live = 1; if (ngx_reopen) { /* ログファイルのリオープン */ if (ngx_change_binary) { /* バイナリを起動しながら交換 gracefull*/ if (ngx_noaccept) { /* 何か */ } }
これがマスタプロセスの起動時の動きとメインループ内の処理です。 基本的には子プロセスとキャッシュマネージャの起動予約※をした後、 シグナルを受け付けるだけになります。 マスタプロセスが子を制御しかしていないことがよくわかりますね。 ※キャッシュマネージャは常に存在しているわけではないので定期的に起動しているはず
子プロセスの起動
次にngx_start_worker_processesで何しているかを観ます。 ほげ この関数は先ほどと同じ/os/unix/ngx_process_cycle.cに記述されています。
/os/unix/ngx_process_cycle.c
static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { /* ログ記入、変数初期化 */ ngx_channel_t ch; for (i = 0; i < n; i++) { ¦ ngx_spawn_process(cycle, ngx_worker_process_cycle, ¦ ¦ ¦ ¦ ¦ ¦ (void *) (intptr_t) i, "worker process", type); ¦ ch.pid = ngx_processes[ngx_process_slot].pid; ¦ ch.slot = ngx_process_slot; ¦ ch.fd = ngx_processes[ngx_process_slot].channel[0]; ¦ ngx_pass_open_channel(cycle, &ch); } }
観ての通りngx_spawn_processを起動したい数だけ実行したのちにチャンネルの設定を行っています。ngx_spawn_processはnginx内部でプロセス生成する際に用いる汎用的な関数です。第二引数に生成したプロセスで実行したい関数へのポインタを指定します。このプロセス生成部については後程取り上げます。 ngx_process_slotは生成したプロセスの通し番号のため生成したばかりのプロセスの情報をchに格納していることになります。(たぶん生成したプロセスの遠し番号だと思う)
ここの子プロセス生成部で着目すべきはとngx_pass_open_channelです。少し追ってみましょう。(※本当か?)
ngx_pass_open_channelは同じ/os/unix/ngx_process_cycle.cにありまぁす
/os/unix/ngx_process_cycle.c
static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch) { ngx_int_t i; for (i = 0; i < ngx_last_process; i++) { ¦ if (i == ngx_process_slot ¦ ¦ || ngx_processes[i].pid == -1 ¦ ¦ || ngx_processes[i].channel[0] == -1) ¦ { ¦ ¦ continue; ¦ } ¦ ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, ¦ ¦ ¦ ¦ ¦ "pass channel s:%i pid:%P fd:%d to s:%i pid:%P fd:%d", ¦ ¦ ¦ ¦ ¦ ch->slot, ch->pid, ch->fd, ¦ ¦ ¦ ¦ ¦ i, ngx_processes[i].pid, ¦ ¦ ¦ ¦ ¦ ngx_processes[i].channel[0]); ¦ /* TODO: NGX_AGAIN */ ¦ ngx_write_channel(ngx_processes[i].channel[0], ¦ ¦ ¦ ¦ ¦ ¦ ch, sizeof(ngx_channel_t), cycle->log); } }
ここの処理は子プロセスを生成するごとに行われます。 ①既存のプロセスをひとつ取り出す ①-a 自分自身か無効なプロセスな場合continue ①-b 取り出したプロセスと生成したプロセスを引数にngx_write_channel
個々から見たいもの
ngx_write_channel
ngx_spawn_process(ngx_process_slot)
ngx_channel_t
つづく
キャッシュについてまとめ
キャッシュについてまとめ
基本
リクエストが増えるとIO待ちが増えて処理時間の低下を引き起こす 多くのWebサービスでは少数のファイルにリクエストが集中する この参照頻度の偏りを利用する - 頻度の多いファイル⇒高速なデバイス - その他⇒ふつうのデバイス
用語 - キャッシュサーバ - オリジンサーバ
キャッシュサーバとしての設定と クライアントにキャッシュさせる場合の設定がある。 (ヘッダフィールドの指定等)
基本的にキャッシュとオリジナルコンテンツと同じサーバにある場合その有効性は限定的。 (コンテンツがすべてメモリのページキャッシュに乗っている場合キャッシュでなくてもメモリから高速に応答できるため)
キャッシュに関するディレクティブ
ngx_http_proxy_moduleに実装されている (proxyと書いてあるがnginxのみの静的ファイル配信の際にもこれを用いる)
proxy_cache_path 保存先パス keys_zone=キーゾーン名:サイズ
キャッシュの保存先とキーゾーンの設定
proxy_cache キーゾーン名|off
キャッシュに利用するキーゾーンを指定する
proxy_cache_key キャッシュキー文字列;
キャッシュのキーに使用する文字列を指定する
キャッシュの基本
proxy_cache_pathによってキャッシュファイルの保存先を指定する。 キーゾーン名やその容量などのパラメタを指定する。 どのキーゾーンに保存するかはproxy_cacheディレクティブを使用する。 proxy_cache_keyでキャッシュのキーとして使用する値を使用する。 子の値をキーとしてキャッシュの同一性を確認する。 (デフォルトはスキーム、プロキシ先のホスト名、リクエストされたURIをキーに使用する)
補足
- プロセス間でキャッシュを共有するためキャッシュのメタデータを共有メモリに保存する。
- ゾーンの必要容量=キャッシュの総容量÷1ファイルの平均サイズ*128B
- キャッシュファイルの保存ディレクトリの内部階層の指定をできる
キャッシュマネージャの制御
proxy_cache_pathディレクティブのloader_files, loader_sleep, loader_threasholdでキャッシュマネージャを制御できる
有効期限の指定
- キーゾーンごとのキャッシュ有効期限を設定する
- オリジンサーバのレスポンスヘッダに有効期限を指定する
レスポンスヘッダで指定されなかった場合のhttpステータスコードごとの有効期限を指定する
キーゾーン
proxy_cache_pathにinactiveパラメータを指定する
指定した有効期限より長くリクエストされないとキャッシュファイルが削除される。
デフォルト値は10分間
2.レスポンスヘッダ - X-Accel-Expires - Cache-Control - Expires これらのヘッダフィールドをオリジンサーバからのレスポンスに付加することでキャッシュの有効期限を指定できる 3.ステータスコード毎に有効期限を設定 proxy_cache_valid [HTTPステータスコード] 有効期限; これを設定することでステータスコードごとに有効期限を指定できる
キャッシュ条件の指定
nginxはデフォルトでGET, HEADメソッドのレスポンスをキャッシュする。 (上記の設定をした場合の話) - GET,HEAD以外もキャッシュしたい場合 proxy_cache_methodsディレクティブを使う - URLでキャッシュするか決めたい場合locationディレクティブを使う - それ以外の時proxy_cache_bypassとproxy_no_cacheを使う proxy_cache_bypass 1 のときレスポンスをキャッシュから取得しない proxy_no_cache 1 の時 レスポンスをキャッシュに保存しない (変数を使って1 0 を切り替える)
一時ファイルの保存先の指定
nginxはキャッシュするレスポンスを一旦一時ファイルに保存し、その後指定したフォルダに移動する そのため、一時保存先と保存先が違うデバイスだとファイルをコピーする必要がありIOが発生する この一時ファイルの保存先はデフォルトはコンパイル時に指定 明示的に指定する場合proxy_temp_pathを使用する proxy_temp_path 一時ファイルの保存先パス
キャッシュ更新負荷の削減
キャッシュを利用した場合リクエスト数を減らすことができる しかし失効した瞬間にリクエストがバーストする。 キャッシュ失効は大きなネットワーク負荷やディスクIOの原因になりうる。
proxy_cache_revalidate on|off; キャッシュ更新の際にオリジンサーバに if-Modifed-Since, If-None-Matchヘッダフィールドで送って 更新されていなかった場合304が帰ってくるので、帯域負荷を抑えられる。
proxy_cache_lock on|off もう一つの方法としてキャッシュロックがある。 これを有効にすると同じURIに対してのアクセスを一つ目以外ブロックする。 これによりアップストリーム鯖への問い合わせ回数を制限できる このブロックのタイムアウトはproxy_cache_lock_timeoutで指定できて、デフォルトは5秒になっている。
クライアントにキャッシュさせる場合の設定
クライアントにレスポンスをキャッシュさせるための設定はヘッダフィールドに設定を付加して行う。
add_header ヘッダフィールド名 値 [always] alwaysを付加すると404,500と行ったエラーの場合にもヘッダフィールドを付加できる。 (ヘッダの追加のみ行うためアップストリームで設定しているフィールドをaddしてしまうと重複して出力される) (重複されている場合の挙動は定義されていないため不具合の原因になる可能性がある)
関係するヘッダフィールド
- X-Accel-Expires 秒数 キャッシュサーバのnginxがコンテンツキャッシュの有効期限として解釈する (一般のブラウザでは解釈されない!!!)
- Cache-Control
- Expires HTTp/1.1ではこの二つのヘッダでキャッシュの制御を行う
Expiresヘッダの制御
expires [modified] 有効期限; expires epoch|max|off;
HHTP/1.1に準拠するヘッダはexpiresディレクティブで行う
expires 10d; #10日間キャッシュを行う。
デフォルトではリクエストされた時点の時刻を起点に計算される modifiedをつけるとファイルの最終更新時刻からの有効期限になる。
expires modified 5d; #更新日時から5日までキャッシュされる
epochを指定した場合Expiresヘッダフィールドの値が1970/00:00:00になる(最古の値) maxを指定した場合もっとも未来の値になる。 常に無効にしたい場合epoch、常に有効にしたい場合maxに指定する 通常はexpiresでExpriresを設定するだけで期限を設定できるが、他の細かい制御をするには Cache-Controlヘッダが必要なため場合によってはadd_headerでCache-Controlを指定する必要がある。
Cache-Controlヘッダ
- max-age=秒数
- no-cache #キャッシュしない
- max-age=315360000 #未来の最大値(expiresのmaxと同じ)
- private #キャッシュの共有を禁止(ブラウザレベルでしかキャッシュしないようにする)
条件付きリクエスト
ブラウザ、キャッシュサーバのキャッシュ以外にトラフィックを最小化する手法として条件付きリクエストが存在する。 キャッシュの期限が切れた際にキャッシュが有効か確認をし、無効であれば新しいレスポンスを受け取る方法。
条件付きリクエストには以下のヘッダが付与されている - If-Modified-Since: "レスポンスのLast-Modifiedヘッダ" - If-None-Match: "レスポンスのETagヘッダフィールド"
サーバはこれらの値を確認し送信するファイルの内容が前回のキャッシュと変更されていないと判断できれば、 304(Not Modified)を送信しファイル送信を省略できる。 もし変更されていれば通常通りファイルの内容を返す。
一般的なブラウザは条件付きリクエストに対応しており、nginxでもproxy?cache_revalidateを使用すれば、 条件付きリクエストによるキャッシュの有効性確認ができる。
Last-Modified
リソースの最終更新日時を指定する nginxが静的リソースを配信する場合は自動的に付与される。 Webアプリケーションの出力などには自動的には付与されない。
ETag
リソースを一意に識別できるユニークな文字列を指定する nginxではetagディレクティブで制御できる。
etag on|off; #(デフォルトon)
nginxではファイルの更新日時とファイルサイズから自動的に計算される。 基本的にはEtagを増やした状態で構わない
秘伝のタレの解説
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
try_files $uri @app;
expires max;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
etag off;
}
画像ファイルのとき パスに存在すればそれを配信 クライアント側で永遠にキャッシュする privateの逆 privateの逆 必ずサーバに再認証を行う キャッシュサーバに対して再認証を要求 etagをつけない(←!?
etagがなくても条件付きリクエストをやるのかもしれない
nginxをよむ
ソースコードリーディングが何ぼのもんじゃい
core/nginx.c if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else { ngx_master_process_cycle(cycle); }
os/ngx_process_cycle.c ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); //後ろの方にはシグナルとかrestartの処理が書いてあった static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) { ngx_spawn_process(cycle,ngx_worker_process_cycle,(void *) (intptr_t) i, "worker process", type); }
os/unix/ngx_process.c ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn){ //省略 pid = fork(); switch (pid) { case -1: ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "fork() failed while spawning \"%s\"", name); ngx_close_channel(ngx_processes[s].channel, cycle->log); return NGX_INVALID_PID; case 0: ngx_pid = ngx_getpid(); proc(cycle, data); break; default: break; } //省略 }
procが関数として呼び出されている
procの型はngx_spawn_proc_pt(なんだこりゃ
os/unix/ngx_process.h typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data);
ngx_spawn_proc_ptは上記二つの引数をとる関数の型
呼び出し元を見るとos/unix/ngx_process_cycle.cのngx_worker_process_cycle
os/unix/ngx_process_cycle.c static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) { //~ ngx_process_events_and_timers(cycle); //~ }
./event/ngx_event.c:194 void ngx_process_events_and_timers(ngx_cycle_t *cycle){ //~ (void) ngx_process_events(cycle, timer, flags); //~ }
./event/ngx_event.h:408 #define ngx_process_events ngx_event_actions.process_events
わった
検索結果
[root@hote src]# grep -rne ngx_event_actions ./ ./event/modules/ngx_win32_select_module.c:106: ngx_event_actions = ngx_select_module_ctx.actions; ./event/modules/ngx_devpoll_module.c:186: ngx_event_actions = ngx_devpoll_module_ctx.actions; ./event/modules/ngx_epoll_module.c:368: ngx_event_actions = ngx_epoll_module_ctx.actions; ./event/modules/ngx_eventport_module.c:279: ngx_event_actions = ngx_eventport_module_ctx.actions; ./event/modules/ngx_kqueue_module.c:224: ngx_event_actions = ngx_kqueue_module_ctx.actions; ./event/modules/ngx_poll_module.c:96: ngx_event_actions = ngx_poll_module_ctx.actions; ./event/modules/ngx_select_module.c:105: ngx_event_actions = ngx_select_module_ctx.actions; ./event/ngx_event.c:44:ngx_event_actions_t ngx_event_actions;
環境と方式によって違うメソッドを実行している
/event/modules/ngx_eventport_module.c:279
static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer) { //~ ngx_event_actions = ngx_epoll_module_ctx.actions; //~ }
/event/ngx_event.c
static char * ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) { ngx_event_conf_t *ecf = conf; #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) int fd; #endif ngx_int_t i; ngx_module_t *module; ngx_event_module_t *event_module; module = NULL; #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL) fd = epoll_create(100); if (fd != -1) { (void) close(fd); module = &ngx_epoll_module; } else if (ngx_errno != NGX_ENOSYS) { module = &ngx_epoll_module; } } |||< 切り替えをしているのはこの部分 (パラメータで切り替えるのになぜマクロ?) (気力が失せた) module_epoll >|| ngx_event_module_t ngx_epoll_module_ctx = { &epoll_name, ngx_epoll_create_conf, /* create configuration */ ngx_epoll_init_conf, /* init configuration */ { ngx_epoll_add_event, /* add an event */ ngx_epoll_del_event, /* delete an event */ ngx_epoll_add_event, /* enable an event */ ngx_epoll_del_event, /* disable an event */ ngx_epoll_add_connection, /* add an connection */ ngx_epoll_del_connection, /* delete an connection */ #if (NGX_HAVE_EVENTFD) ngx_epoll_notify, /* trigger a notify */ #else NULL, /* trigger a notify */ #endif ngx_epoll_process_events, /* process the events */ ngx_epoll_init, /* init the events */ ngx_epoll_done, /* done the events */ } };
このうちのactionsに対応する部分を実行している
events.h
typedef struct { ngx_str_t *name; void *(*create_conf)(ngx_cycle_t *cycle); char *(*init_conf)(ngx_cycle_t *cycle, void *conf); ngx_event_actions_t actions; } ngx_event_module_t;
actionsに相当するのは関数のリストであるぞ
このリストの中でメインループなのはprocess_events
なかでepoll掘って完了したイベントごとに何かしている感じ
■
named return value
naked return
func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return }
decrea var
var hoge hage:=1
golang memo
named return value
naked return
>|go|
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
||<
decrea var
>|go|
var hoge
hage:=1
||<
わこつ
わこつ~