画像配信など大量にアクセスを捌く際にちょっと気になっていたhttpなupstreamとkeepaliveできない件が、nginx-1.1系でできるようになったので試してみた

nginx11keepalive.png

今回keepaliveできるようになったのは↑のbackendと通信するところ。

本家のドキュメントはこちら
http://nginx.org/en/docs/http/ngxhttpupstream_module.html#keepalive

keepalive機能を使うには、以下のように設定します

http {
    upstream backend {
        server 127.0.0.1:5000;
        keepalive 16;
    }
    server {
        listen       8080;
        server_name  localhost;
        location / {
            proxy_http_version 1.1;
            proxy_set_header Connection ";
            proxy_pass http://backend;
        }
    }
}

upstreamブロックにkeepaliveで、保持するコネクション数を指定し、proxy_http_version で「1.1」を(1.0については後述)、proxy_set_headerでConnectionヘッダを削除します。削除しなければならないのは、nginxがデフォルトで「close」を設定するからです。これで http://127.0.0.1:5000/ への通信がkeepalive可能になるはずです。

確認には、HTTP/1.1をサポートしたStarmanに、以下のpatchをあてて利用します

diff --git a/lib/Starman/Server.pm b/lib/Starman/Server.pm
index 67d1528..137972a 100644
--- a/lib/Starman/Server.pm
+++ b/lib/Starman/Server.pm
@@ -175,6 +175,7 @@ sub process_request {

         my $env = {
             REMOTE_ADDR     => $self->{server}->{peeraddr},
+            REMOTE_PORT     => $self->{server}->{peerport},
             REMOTE_HOST     => $self->{server}->{peerhost} || $self->{server}->{peeraddr},
             SERVER_NAME     => $self->{server}->{sockaddr}, # XXX: needs to be resolved?
             SERVER_PORT     => $self->{server}->{sockport},

そして用意したPSGIアプリケーションが以下。

sub {
    my $env = shift;
    my $port = $env->{REMOTE_PORT};
    [200,['Content-Type'=>'text/plain','Content-Length'=>length($port)+1],["$port\n"]];
};

keepaliveが有効であれば、backend側でのREMOTE_PORTは変わらないはずです。試しにstarmanを起動し、直接アクセスします。

$ starman --preload-app test.psgi
$ curl http://localhost:5000/{1..5}
55075
55075
55075
55075
55075

curlもHTTP/1.1 keepaliveをサポートするので、全て同じポート番号が戻ります。

次に nginx を起動して試してみます

$ ./sbin/nginx
$ curl http://localhost:8080/{1..5}
55099
55099
55099
55099
55099

おぉ。同じ番号になってます。keepaliveできてそう。

確認のために、nginx側でkeepaliveに関する設定を削除してみると、

$ curl http://localhost:8080/{1..5}
55233
55234
55235
55236
55237

このようにポート番号が次々に変わって行きます。keepaliveが有効になっているのがわかりますね!

ここまで検証してサービスで使えそう!と思ったのですが、今回使う事を考えていた画像配信で使っているSquidは残念なことに、HTTP/1.1 keepaliveをサポートしていません。Connectionヘッダにkeep-aliveを入れてリクエストをするHTTP/1.0が必要となります。

そこで、nginxの設定を

server {
    listen       8080;
    server_name  localhost;
    location / {
        proxy_set_header Connection "keep-alive";
        proxy_pass http://backend;
    }
}

このように変更し試してみましたが、keepaliveされませんでした。そこでnginxのソースコードを追って行くと、HTTP/1.1でない場合はコネクションを必ずcloseしてしまうようになってたので、レスポンスのConnectionヘッダに「keep-alive」が入っていた場合に、切断しないようにする以下のpatchをあてました。

--- nginx-1.1.14.orig/src/http/ngx_http_upstream.c      2012-01-19 00:07:43.000000000 +0900
+++ nginx-1.1.14/src/http/ngx_http_upstream.c   2012-02-09 17:11:02.000000000 +0900
@@ -3426,6 +3426,13 @@
         r->upstream->headers_in.connection_close = 1;
     }

+    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+                         (u_char *) "keep-alive", 10 - 1)
+        != NULL)
+    {
+        r->upstream->headers_in.connection_close = 0;
+    }
+
     return NGX_OK;
 }

これでHTTP/1.0でもkeepaliveできるはずなので、今度はHTTP/1.1をサポートしないStarletを使って検証します。Starmanと同じくREMOTE_PORTが取れるようpatchをあてます

diff --git a/lib/Starlet/Server.pm b/lib/Starlet/Server.pm
index ea5a34a..63b54f2 100644
--- a/lib/Starlet/Server.pm
+++ b/lib/Starlet/Server.pm
@@ -106,6 +106,7 @@ sub accept_loop {
                     SERVER_NAME => $self->{host},
                     SCRIPT_NAME => '',
                     REMOTE_ADDR => $conn->peerhost,
+                    REMOTE_ADDR => $conn->peerport,
                     'psgi.version' => [ 1, 1 ],
                     'psgi.errors'  => *STDERR,
                     'psgi.url_scheme' => 'http',

これで、plackup使いStarletサーバを起動し検証します。

$ plackup -s Starlet --max-keepalive-reqs=1000 test.psgi
$ curl http://localhost:8080/{1..5}
55961
55961
55961
55961
55961

同じポート番号がならんだので、keepaliveできてそう!やった!!1

最後に、参考程度にMacBookAir上でApacheBenchを使ってベンチマークとって見ました

$ ab -k -c 10 -n 5000 http://localhost:8080/

# keepaliveなし
Requests per second:    1910.71 [#/sec] (mean)
# keepaliveあり
Requests per second:    2935.73 [#/sec] (mean)

通常のアプリケーションサーバでは接続数が問題となるので、使わないと思いますが、後ろの接続数が問題とならない特定の用途では使えます。
ということで、既に画像配信の本番サーバにもpatchあてたnginx-1.1系が投入されています。多少CPU使用率が減った気がします。

このブログ記事について

このページは、Masahiro Naganoが2012年2月15日 16:48に書いたブログ記事です。

ひとつ前のブログ記事は「hb qp bp study 新年LT&ビアバッシュ2012に参加してビール飲んでピザ食べてきた」です。

次のブログ記事は「Apache 2.2.15から入った mod_reqtimeout を Reverse Proxyで使う場合の注意点」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja