画像配信など大量にアクセスを捌く際にちょっと気になっていたhttpなupstreamとkeepaliveできない件が、nginx-1.1系でできるようになったので試してみた
今回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使用率が減った気がします。