nginx+hhvm で UNIX Socket接続を試す

先日、PHP7 Casual Talks #2「速さ」に参加してきました。

その中で、nginx + php-fpm、apache+php-fpm 、apache+mod_php、 nginx+apache+mod_php との接続で、何が一番リクエストを処理できるかをベンチマーク(たしかsiegeを使ってた)を取っていました。

結果を見ると、nginx+php-fpmよりもapache+mod_phpのほうがリクエスト/秒の数値が多い。このとき、nginx+php_fpmはUNIX Socketを使って通信していました。まあ、普通UNIX Socketのほうが早いはずです。が、ここでnginx と php-fpmの接続をTCP(localhost:9000)にすると、順当にnginx+php-fpmのほうがリクエスト/秒が多くなったようです。

ここで疑問。なんでUNIX Socketだとリクエスト数/秒が少なくなるの?
とりあえず仮説を立てました。

ということで、やってみました。もちろんKUSANAGIで。

まず、hhvm 設定ファイル(/etc/hhvm/php.ini)の以下の部分を修正します。

;hhvm.server.ip = 127.0.0.1
;hhvm.server.port = 9000 
hhvm.server.file_socket = /var/run/php-fpm/sock

次に、nginx設定ファイル(/etc/nginx/conf.d/XXX_html.conf)の以下の部分を修正します。2か所あるので、両方直しましょう。なお、ssl接続の時は、もう一つの設定ファイル/etc/nginx/conf.d/XXX_ssl.conf も同様に修正します。

;#fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/var/run/php-fpm/sock;

/var/run/php-fpm のパーミションは一時的に777にしておきます。ここでサービス再起動。sock ファイルがうまく動くことを確認。

# chmod 777 /var/run/php-fpm
# systemctl restart hhvm nginx
# ls -l /var/run/php-fpm/sock 
srwxrw---- 1 httpd www 0 Apr 17 02:22 /var/run/php-fpm/sock

ここで自分自身に対して ab をかけます(URLは架空のものです)。リクエスト数1000、同時接続数100で実行すると、Requests per secondは130#/s でエラーなしでした。普通に早い何時ものKUSANAGIですね。

# ab -n 1000 -c 100 http://kusanagi70.local/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

 中略

Server Software:        nginx
Server Hostname:        kusanagi70.local
Server Port:            80

Document Path:          /
Document Length:        10445 bytes

Concurrency Level:      100
Time taken for tests:   7.671 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      10727000 bytes
HTML transferred:       10445000 bytes
Requests per second:    130.36 [#/sec] (mean)
Time per request:       767.080 [ms] (mean)
Time per request:       7.671 [ms] (mean, across all concurrent requests)
Transfer rate:          1365.65 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.6      0       2
Processing:    20  724 139.1    715    1158
Waiting:       16  724 139.1    715    1158
Total:         20  724 139.0    715    1158

Percentage of the requests served within a certain time (ms)
  50%    715
  66%    736
  75%    756
  80%    770
  90%    813
  95%    995
  98%   1105
  99%   1124
 100%   1158 (longest request)

もうちょっと負荷を上げて同時接続数を300にしてみましょう。
おやおやエラーが出ましたね。

# ab -n 1000 -c 300 http://kusanagi70.local/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

中略

Server Software:        nginx
Server Hostname:        kusanagi70.local
Server Port:            80

Document Path:          /
Document Length:        10445 bytes

Concurrency Level:      300
Time taken for tests:   6.044 seconds
Complete requests:      1000
Failed requests:        111
   (Connect: 0, Receive: 0, Length: 111, Exceptions: 0)
Write errors:           0
Non-2xx responses:      111
Total transferred:      9572489 bytes
HTML transferred:       9304031 bytes
Requests per second:    165.45 [#/sec] (mean)
Time per request:       1813.214 [ms] (mean)
Time per request:       6.044 [ms] (mean, across all concurrent requests)
Transfer rate:          1546.67 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   4.4      0      19
Processing:     4 1523 778.0   1934    2257
Waiting:        0 1523 778.2   1934    2257
Total:         13 1526 774.5   1934    2257

Percentage of the requests served within a certain time (ms)
  50%   1934
  66%   2025
  75%   2139
  80%   2151
  90%   2170
  95%   2177
  98%   2187
  99%   2195
 100%   2257 (longest request)

このときのエラーファイル(/home/kusanagi/XXX/log/nginx/error.log)を見ると、以下のようなエラーが出ていました。一時的にSocketファイルにアクセスできなかったようですね。

2016/04/17 02:30:55 [error] 20252#0: *49100 connect() to unix:/var/run/php-fpm/sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 127.0.0.1, server: kusanagi70.local, request: "GET / HTTP/1.0", upstream: "fastcgi://unix:/var/run/php-fpm/sock:", host: "kusanagi70.local"

では、どんな動きをするか、strace で nginxのsystem call を追ってみましょう。strace は、-p  でプロセスID、-f でfork後のプロセスも追いかけます。nginxのプロセスIDはここでは なので、以下のようにstraceを実行後、別端末からab -n 1000 -c 300 で実行します。

# strace -f -p 20251
Process 20251 attached
epoll_wait(13, {{EPOLLIN, {u32=3703730192, u64=139925148299280}}}, 512, -1) = 1
accept4(10, {sa_family=AF_INET, sin_port=htons(39911), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_NONBLOCK) = 17
epoll_ctl(13, EPOLL_CTL_ADD, 17, {EPOLLIN|EPOLLRDHUP|EPOLLET, {u32=3703963688, u64=139925148532776}}) = 0
accept4(10, 0x7ffecb6af920, [110], SOCK_NONBLOCK) = -1 EAGAIN (Resource temporarily unavailable)

中略

epoll_ctl(13, EPOLL_CTL_ADD, 293, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=3703956345, u64=139925148525433}}) = 0
connect(293, {sa_family=AF_LOCAL, sun_path="/var/run/php-fpm/sock"}, 110) = 0
getsockopt(293, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
writev(293, [{"\1\1\0\1\0\10\0\0\0\1\0\0\0\0\0\0\1\4\0\1\2\23\5\0\17*SCRIPT"..., 576}], 1) = 576

中略

epoll_ctl(13, EPOLL_CTL_ADD, 211, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=3703838408, u64=139925148407496}}) = 0
connect(211, {sa_family=AF_LOCAL, sun_path="/var/run/php-fpm/sock"}, 110) = -1 EAGAIN (Resource temporarily unavailable)
write(7, "2016/04/17 01:55:51 [error] 2025"..., 308) = 308
close(211)                              = 0

後略

通常は、connect()でソケットファイルを開き、hhvmとの通信処理を続行するのですが、大量通信時に、connect()が エラー(EAGEIN)を返します。これは一時的にサービスできないというエラーです。やはり同時接続数が多いと socket ではさばききれないようです。

同時接続数が多い場合、socketへの接続エラー発生率が高くなり、その結果秒間リクエストが少なくなる、ということのようです。

ということで、結論から言うと、以下の仮説は間違いでしたね。

正解は、Socketアクセスエラーが多くなる→正常処理できるリクエストが少なくなる、でした。

ちなみに、同じ環境でnginx+hhvmをtcp/9000で接続した場合、abの結果は以下のようになり、 エラーは発生してませんでした。

# ab -n 1000 -c 300 http://kusanagi70.local/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

中略

Server Software:        nginx
Server Hostname:        kusanagi70.local
Server Port:            80

Document Path:          /
Document Length:        10445 bytes

Concurrency Level:      300
Time taken for tests:   6.245 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      10727000 bytes
HTML transferred:       10445000 bytes
Requests per second:    160.13 [#/sec] (mean)
Time per request:       1873.421 [ms] (mean)
Time per request:       6.245 [ms] (mean, across all concurrent requests)
Transfer rate:          1677.51 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    7  11.8      0      29
Processing:    48 1586 580.1   1830    2890
Waiting:       12 1586 580.2   1830    2890
Total:         48 1594 575.8   1830    2915

Percentage of the requests served within a certain time (ms)
  50%   1890
  66%   1936
  75%   2115
  80%   2363
  90%   2786
  95%   2951
  98%   3085
  99%   3135
 100%   7068 (longest request)
  95%   1917
  98%   1921
  99%   1925
 100%   1934 (longest request

もっと過激にリクエスト数5000、同時接続数2000にしてみても、エラーは発生してませんね。リクエスト/秒もあまり変わらないのでエライ。

# ab -n 5000 -c 2000 http://kusanagi70.local/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

中略

Server Software:        nginx
Server Hostname:        kusanagi70.local
Server Port:            80

Document Path:          /
Document Length:        10445 bytes

Concurrency Level:      2000
Time taken for tests:   31.341 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      53635000 bytes
HTML transferred:       52225000 bytes
Requests per second:    159.53 [#/sec] (mean)
Time per request:       12536.570 [ms] (mean)
Time per request:       6.268 [ms] (mean, across all concurrent requests)
Transfer rate:          1671.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   39  48.6      0     118
Processing:   132 10045 3796.8  12311   13817
Waiting:       15 10045 3796.9  12311   13817
Total:        196 10084 3764.5  12312   13927

Percentage of the requests served within a certain time (ms)
  50%  12312
  66%  12567
  75%  12618
  80%  12631
  90%  12653
  95%  12672
  98%  13225
  99%  13607
 100%  13927 (longest request)

 

Follow me!

Leave a Reply