まだHTTP/1で消耗してるの？って煽られたのでPHPでHTTP/2試した

[PHP Advent Calendar 2016](http://qiita.com/advent-calendar/2016/php)の17日目及び
[Fusic Advent Calendar 2016](http://qiita.com/advent-calendar/2016/fusic)の17日目の記事です。

# 自己紹介

みなさまPHPしてますか？　seike460と申します。
PHPを使って仕事してますが、最近Pythonもお気に入りです。
[こっちでPythonの記事](http://qiita.com/seike460/items/1cd1f85dd2f782a48d6a)も書いてるんで興味ある方は是非覗いて見て下さい。
インフラも好きで、サーバーも触っています。

# 背景

突如[弊社](https://fusic.co.jp/)の[HICP(Hyper Infrastructure Creator Pudding)](https://www.google.co.jp/search?q=fusic%20%E3%83%97%E3%83%AA%E3%83%B3&rct=j)が社内情報共有ツール内でこんな記事を書きました。

「まだHTTP/1で消耗してるの？」

**完全に煽られた。**

釣りタイトルで、記事内容はちゃんとしてましたが、インフラを嗜むエンジニアとしては

「え？消耗してないです。もう普通に使ってますよ？」(ドヤァ)

と答えたいものです。
ドヤァする為にHTTP/2対応を進める事にしました。

# ゴール設定

* PHPでHTTP/2してますよ(ドヤァ)出来る状態になる
* どうせなのでパフォーマンス図る
* HTTP/2の良さを感じる

# 構成

みんなが大好きApacheを使いたいところですが、~~なんか色々辛そうだったので~~今回はNginxをメインに検証します。
Dockerだとコンテナ間のオーバーヘッドが気になったので素直にVagrantを使用する事にしました。
OSはCentOs7を使用しています。

# Nginx（HTTP/1） + PHP7

ひとまずNginx(80)にてPHP動作するところまで進めます。

```zsh:Nginx+PHP7環境セットアップ
【なにはともあれ】
# yum update
# systemctl stop firewalld
【一応日本語化】
# yum -y install ibus-kkc vlgothic-*
# localectl set-locale LANG=ja_JP.UTF-8
【みんな大好きremiリポジトリ】
# yum -y install epel-release
# rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
【nginx】
# yum --enablerepo=epel install nginx
【php-fpm(どうせだからphp71!)】
# yum --enablerepo=remi install php71-php-fpm
【実行ユーザー等変更 *1】
# vim /etc/opt/remi/php71/php-fpm.d/www.conf
【php-fpm起動】
# systemctl start php71-php-fpm
【ひとまずHTTP/1 *2】
# vim /etc/nginx/nginx.conf
# systemctl start nginx
# echo "<?php phpinfo();" > /usr/share/nginx/html/info.php
```
* php-fpmの設定 *1

```vim:www.conf
【ユーザー変更】
user = nginx

【グループ変更】
group = nginx 
```

* nginxの設定 *2

```vim:nginx.conf-80のserverセクションに追記

【phpファイルはphp-fpmに流す】
location ~ .php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO $fastcgi_path_info;
    include        fastcgi_params;
}
```
* nginx再起動してブラウザにてinfo.phpにアクセス

ひとまず動作している事を確認

<img width="950" alt="80.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/d997c8c3-c59a-3723-0e36-b87bb29750b8.png">

# Nginx（HTTP/2） + PHP7

オレオレ証明書を作成してNginxに読み込ませます。

```vim:server.key-server.crt作成
# mkdir /etc/pki/nginx/
# cd /etc/pki/nginx/
# openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.....................+++
.......................................+++
e is 65537 (0x10001)
# openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:example.seike460.com　【※ここは適当に名前を決めてます※】
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
```
* 自分のPCのhostsファイルに、追記（今回の場合、example.seike460.com）

```vim:nginx.conf-443のserverセクションに追記
    server {
        listen       443 ssl http2 default_server;
        listen       [::]:443 ssl http2 default_server;
        server_name  example.seike460.com;
        root         /usr/share/nginx/html;

        ssl_certificate "/etc/pki/nginx/server.crt";
        ssl_certificate_key "/etc/pki/nginx/server.key";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
        location ~ .php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO $fastcgi_path_info;
            include        fastcgi_params;
        }
    }
```

* nginx再起動してブラウザにてinfo.phpにアクセス


Chromeの拡張機能の「HTTP/2 and SPDY indicator」が反応せずぐぬぬ…と唸っていたのですが、
どうもChromeだと今回の設定では反応してくれないようです。
今回の本質とは別のお話ですので、素直にFireFoxにて反映を確認しました。
pyonkさんありがとうございます↓
http://qiita.com/pyonk/items/45626c712e4a9938c980

PHPでHTTP/2してますよ(ドヤァ)出来る事を確認

<img width="963" alt="443.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/de309827-4fe4-30b9-9487-24671803a4a6.png">

# パフォーマンス測定(ab)

abを1000リクエスト、クライアント数10で実行

```vim:HTTP/1
# ab  -n 1000 -c 10 https://example.seike460.com/info.php
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/

Benchmarking example.seike460.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.10.2
Server Hostname:        example.seike460.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256

Document Path:          /info.php
Document Length:        891 bytes

Concurrency Level:      10
Time taken for tests:   2.121 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      1053000 bytes
HTML transferred:       891000 bytes
Requests per second:    471.55 [#/sec] (mean)
Time per request:       21.207 [ms] (mean)
Time per request:       2.121 [ms] (mean, across all concurrent requests)
Transfer rate:          484.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        4   15   5.3     14      39
Processing:     0    6   4.7      5      28
Waiting:        0    6   4.7      5      28
Total:          7   21   6.9     20      54

Percentage of the requests served within a certain time (ms)
  50%     20
  66%     23
  75%     25
  80%     26
  90%     31
  95%     34
  98%     36
  99%     39
 100%     54 (longest request)
```

```vim:HTTP/2
# ab  -n 1000 -c 10 https://example.seike460.com/info.php
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/

Benchmarking example.seike460.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.10.2
Server Hostname:        example.seike460.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256

Document Path:          /info.php
Document Length:        891 bytes

Concurrency Level:      10
Time taken for tests:   2.172 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      1053000 bytes
HTML transferred:       891000 bytes
Requests per second:    460.44 [#/sec] (mean)
Time per request:       21.719 [ms] (mean)
Time per request:       2.172 [ms] (mean, across all concurrent requests)
Transfer rate:          473.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        5   15   5.0     14      32
Processing:     0    7   5.0      6      27
Waiting:        0    6   5.1      5      27
Total:          5   22   7.0     21      50

Percentage of the requests served within a certain time (ms)
  50%     21
  66%     24
  75%     26
  80%     27
  90%     30
  95%     34
  98%     39
  99%     42
 100%     50 (longest request)
```

差はほぼないですね。リソースに複数アクセス出来るのが強みだと思うので、
PHP経由で画像を複数アクセスするHTML作成して検証してみます。

# パフォーマンス測定(ブラウザ)
imageタグを多く生成するPHPを実行します。

* HTTP/1
<img width="932" alt="firefoxhttp1.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/57d7e1bb-f37c-bc58-d2d7-d3023277bcc0.png">

* HTTP/2　
<img width="934" alt="firefoxhttp2.png" src="https://qiita-image-store.s3.amazonaws.com/0/120576/b59e09c4-fbb3-1ea7-3268-df5255aaf0de.png">

若干 HTTP/2の方が早い程度ですね。
（ダウンロード待ちが画像のダウンロード待ちがほとんどですので、ネットワーク見直せばもっと早くなるかもしれません。）

# 感想
社内情報共有ツールに投稿された釣りタイトルの記事に釣られてHTTP/2をPHPで試してみました。
特に大きな障壁なく導入出来たのですが、
最低限の内容だった事もあるのか少しだけパフォーマンスに注目すると不完全燃焼な結果になってしまいました。

このままではドヤァ出来ませんし、HTTP/2の恩恵をもっと感じたいと思っていますので、今後もっと深掘りしてみたいと思っています。