今年5月にHTTP2のRFCが公開されて、今後サービス導入が加速しそうな雰囲気になってきていますね。
nginx, H2O, apache等々対応がささやかれている昨今ですが、2015/8/5にnginx がEarly Alpha Patch for HTTP/2を出したので、ちょっと触ってみた。
httpからhttp2への進化
 今までのhttpは1本のTCP接続の中で、次のリクエストを送るには、前のリクエストのレスポンスを待つ必要があった。(Head of Line Blocking)
更に、ブラウザから同時接続できるTCP接続数は最大6本であるため、
非常にリソースの無駄と考え、googleが2009年にspdyというhttpベースの新たなプロトコルを考え、これが後のhttp2の元になる。
 http2は、streamという仮想的な通信チャンネルを用意し、1本のTCP接続の中に複数のstreamを作ることでHead of Line Blocking問題を解決した。
またhttp2はstreamによる同時接続により接続のPriorityの制御もを導入している。
例えば、index.htmlがダウンロードされる前にcssやjsがダウンロードされるとまずいからである。
プライオリティ制御に関してはJxck_さんの記事を参考にしてもらうといいかも。
HTTP2 のプライオリティ制御
さらにhttp2にはServer Pushができるようになっている。
これに関しては次回以降チャレンジしてみようかな。
各種ブラウザの対応状況
http2の仕様上、httpでもhttpsでも利用できるのだが、Chrome, FireFox, IEは基本的にはhttpsの通信しか許可していないようだ。
ブラウザ対応状況
APPLEもios9からhttps通信を推奨していたり、無料のSSL証明局サービスの開始がささやかれるなど、業界全体としてhttpsへシフトしているようだな。
新しいSSL証明局:Let's Encrypt
http2を導入しているサービスってどこがあるんだろう
chromeのextensionでHTTP/2 and SPDY indicatorというのがあるので、入れて確認してみる。
対象サイトはalexaの世界ランキングのTOP10に関してみてみる。
| ランク | サイト | 対応状況 | 
|---|---|---|
| 1 | Google.com | http2 | 
| 2 | Facebook.com | spdy3.1 | 
| 3 | Youtube.com | http2 | 
| 4 | Baidu.com | http | 
| 5 | Yahoo.com | spdy3.1 | 
| 6 | Amazon.com | http | 
| 7 | Wikipedia.org | spdy3.1 | 
| 8 | Qq.com | http | 
| 9 | Twitter.com | http2 | 
| 10 | Google.co.in | http2 | 
※google.com
雷アイコン青色:http2
雷アイコン黄色:spdy
雷アイコン灰色:非対応
googleはさすがgoogleといった感じで対応済み。
世界のTOP10を見るだけでも、大体http2かspdyに対応しているようだ。
Alexa:世界Webサイト TOP500
検証環境
お名前.com のVPSを調達。
OS: CentOS 6.5
CPU: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
MEMORY: 1GB
http2対応のnginxの導入
必要な物をyumで導入。
yum groupinstall -y "Development Tools"
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
nginxのダウンロード
cd /usr/local/src
curl -O http://nginx.org/download/nginx-1.9.4.tar.gz
tar xvfz nginx-1.9.4.tar.gz
pacheを当てる
cd nginx-1.9.4/
curl http://nginx.org/patches/http2/patch.http2.txt > patch.http2.txt
patch -p1 < patch.http2.txt 
ビルド
./configure --prefix=/usr/local/nginx-1.9.4 --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --without-http_userid_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_v2_module
make
make install
nginxの起動
/usr/local/nginx-1.9.4/sbin/nginx
ps aux | grep nginx
root      1350  0.0  0.2  55496  3012 ?        Ss   Sep12   0:00 nginx: master process sbin/nginx
nobody    6585  0.0  0.3  56248  3676 ?        S    Sep15   0:50 nginx: worker process
root      7702  0.0  0.0 103252   832 pts/0    S+   18:59   0:00 grep nginx
なんか立ちあがったっぽい。
firewallの許可
# Generated by iptables-save v1.4.7 on Sat Sep 12 14:14:19 2015
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [21:2684]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22  -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80  -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 53  -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
-A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -m state --state NEW -j DROP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,SYN,RST,PSH,ACK,URG -j DROP
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
-A INPUT -d 255.255.255.255 -j DROP
-A INPUT -d 224.0.0.1 -j DROP
-A INPUT -p udp -m multiport --dport 137,138,139,445 -j DROP
-A INPUT -p udp --dport 17500 -j DROP
-A INPUT -m limit --limit 1/s -j LOG --log-prefix "[iptables firewall] : " --log-level=info
COMMIT
# Completed on Sat Sep 12 14:14:19 2015
オレオレ証明書の作成とssl session ticketの作成
mkdir /usr/local/etc/cert/
cd    /usr/local/etc/cert/
openssl genrsa 2048 > server.key
openssl req -new -key server.key > server.csr
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
openssl rand 48 > sslsessionticket_`date +’%Y%m%d’`.key
cd    /usr/local/nginx-1.9.4/
ln -s /usr/local/etc/cert cert
表示するページの作成
mkdir /var/www/vhost/xxx.xxx.xxx.co.jp/htdocs/
cd    /usr/local/nginx-1.9.4
ln -s /var/www/vhost/xxx.xxx.xxx.co.jp/htdocs/ vhost
nginxの設定ファイルの修正
# user  nobody;
worker_processes  1;
# error_log  logs/error.log;
# error_log  logs/error.log  notice;
# error_log  logs/error.log  info;
# pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location ~ .*\.(jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO) {
            root    /usr/local/nginx-1.9.4/vhost/xxx.xxx.xxx.co.jp/htdocs;
            index   index.html;
            ssi     on;
            break;
        }
        location / {
            root    /usr/local/nginx-1.9.4/vhost/xxx.xxx.xxx.co.jp/htdocs;
            index  index.html index.htm;
        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }
    # HTTPS server
    #
    server {
        #listen  443 ssl;
        listen  443 ssl http2 ;
        server_name localhost_ssl;
        ssl_certificate     /usr/local/nginx-1.9.4/cert/server.crt;
        ssl_certificate_key /usr/local/nginx-1.9.4/cert/server.key;
    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
        #charset koi8-r;
        #access_log  logs/host.access.log  main;
        location ~ .*\.(jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO) {
            root    /usr/local/nginx-1.9.4/vhost/xxx.xxx.xxx.co.jp/htdocs;
            index   index.html;
            ssi     on;
            break;
        }
        location / {
            root    /usr/local/nginx-1.9.4/vhost/xxx.xxx.xxx.co.jp/htdocs;
            index  index.html index.htm;
        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
設定のreload
/usr/local/nginx-1.9.4/sbin/nginx -t
/usr/local/nginx-1.9.4/sbin/nginx -s reload
http2の確認
さっきも出てきた、HTTP/2 and SPDY indicatorで見てみる。
おぉ。確かにhttp2通信になってる。
一応nginxのログも確認する。
210.174.39.237 - - [12/Sep/2015:16:13:42 +0900] "GET / HTTP/2.0" 200 755 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"
ちゃんとHTTP/2できていることが確認できました!!
ちなみにchromeのchrome://net-internals にアクセスし、Events を確認すると、http2のストリームの情報も見れるので、チューニングする際とかは抑えておきたいところかも。
httpとhttp2の比較
・色んなサイズのimageを129個
・同じサイズのimageを360個
・同じサイズのjsを360個
ダウンロードしてくるindex.htmlを用意して比較してみる。
サイズの違う画像ダウンロード (sec)
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 14.76 | 14.8 | 15.04 | 15.73 | 14.74 | 15.0 | 0.2 | 
| http + TLS | 15.46 | 16.48 | 15.06 | 14.73 | 14.77 | 15.3 | 0.3 | 
| http 2 + TLS | 14.76 | 15.52 | 14.62 | 14.75 | 14.79 | 14.9 | 0.2 | 
サイズの同じ画像ダウンロード(sec):18.3kB * 360枚
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 12.18 | 11.77 | 11.83 | 12.05 | 13.57 | 12.3 | 0.3 | 
| http + TLS | 11.77 | 11.68 | 11.44 | 44.79 | 35.34 | 23.0 | 7.1 | 
| http 2 + TLS | 11.4 | 11.25 | 11.25 | 13.36 | 11.44 | 11.7 | 0.4 | 
jsダウンロード(sec): 33.1kB * 360枚
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 102 | 564 | 102 | 102 | 102 | 194 | 92 | 
| http + TLS | 150 | 156 | 138 | 102 | 132 | 136 | 9 | 
| http 2 + TLS | 66 | 60 | 59.57 | 57.47 | 57.64 | 60.1 | 1.6 | 
どれをとってもhttp2の方が優秀なデータが出た!
nginxのdirectiveを色々設定してみる
以下のように設定してみた。directiveの解説は今回はしない。
ファイルはgithubに置いているので、参考までにどうぞ。
nginx-http2-setting-sample
| directive | value | 
|---|---|
| worker_rlimit_nofile | 2048 | 
| accept_mutex_delay | 100ms | 
| sendfile | on | 
| tcp_nopush | on | 
| tcp_nodelay | on | 
| open_file_cache | max=1000 inactive=20s | 
| gzip | on | 
| gzip_comp_level | 9 | 
| gzip_types | text/css text/plain text/js text/javascript application/javascript application/jron-rpc | 
| ssl | on | 
| ssl_prefer_server_ciphers | on | 
| ssl_protocols | TLSv1 TLSv1.1 TLSv1.2 | 
| ssl_ciphers | ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!EXPORT:!DES:!3DES:!MD5:!DSS; | 
| ssl_session_tickets | on | 
| ssl_session_ticket_key | /usr/local/nginx-1.9.4/cert/sslsessionticket_20150915.key | 
| ssl_session_cache | shared:SSL:10m | 
| ssl_session_timeout | 10m | 
| http2_max_concurrent_streams | 64 | 
| http2_streams_index_size | 64 | 
| http2_idle_timeout | 30s | 
| http2_recv_timeout | 3m | 
| http2_chunk_size | 200k | 
| http2_max_field_size | 4096 | 
| http2_max_header_size | 16384 | 
| http2_pool_size | 8192 | 
再度検証してみる
サイズの違う画像ダウンロード(sec)
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 14.97 | 14.69 | 15.53 | 14.73 | 14.81 | 14.9 | 0.2 | 
| http + TLS | 18.9 | 15.12 | 14.48 | 15.81 | 16.15 | 16.1 | 0.8 | 
| http 2 + TLS | 14.84 | 14.89 | 14.67 | 14.67 | 14.66 | 14.75 | 0.05 | 
サイズの同じ画像ダウンロード(sec):18.3kB * 360枚
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 11.67 | 11.54 | 11.56 | 11.58 | 11.56 | 11.58 | 0.02 | 
| http + TLS | 13.13 | 28.69 | 14.26 | 15.99 | 17.48 | 18 | 3 | 
| http 2 + TLS | 11.34 | 11.22 | 11.34 | 11.23 | 11.27 | 11.28 | 0.03 | 
jsダウンロード(sec): 33.1kB * 360枚
| 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 平均 | 誤差 | |
|---|---|---|---|---|---|---|---|
| http | 54.59 | 55.52 | 54.37 | 54.28 | 59.37 | 55.6 | 1.0 | 
| http + TLS | 60 | 49.47 | 51.22 | 57.5 | 60 | 56 | 2 | 
| http 2 + TLS | 49.35 | 48.21 | 50.16 | 35.13 | 25.4 | 42 | 5 | 
どれをとってもhttp2がよかった。
画像に関しては、オレオレ証明書の関係でsslの設定の恩恵はほとんど得られなかったが、
js等の静的コンテンツのダウンロードは、gzipの恩恵としてはhttp+TLSのデータの通りだが、http2+TLSの方で大分良くなった感じが見受けられる。
結論はhttp2のほうがやっぱ優れてるので、Webサイトへの導入はかなりいいかもしれない。
とりあえず、Let's Encryptがリリースされたら、SSL証明書を取得し、再度色々検証していきたいと思う。





