LoginSignup
48
44

More than 5 years have passed since last update.

nginxでHTTP2接続(not spdy3.1)の検証

Last updated at Posted at 2015-09-17

今年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問題を解決した。

スクリーンショット 2015-09-17 11.15.05.png
※一本のTCP接続の簡単な概念図

SnapCrab_NoName_2015-9-17_15-14-10_No-00.png
※HTTPのアクセス例

SnapCrab_NoName_2015-9-17_15-12-28_No-00.png
※HTTP2のアクセス例

SnapCrab_NoName_2015-9-17_14-49-45_No-00.png
※streamの概念図

また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

SnapCrab_NoName_2015-9-17_15-0-33_No-00.png

※google.com
雷アイコン青色:http2
雷アイコン黄色:spdy
雷アイコン灰色:非対応

googleはさすがgoogleといった感じで対応済み。
世界のTOP10を見るだけでも、大体http2かspdyに対応しているようだ。
Alexa:世界Webサイト TOP500

検証環境

お名前.com のVPSを調達。

Spec
OS: CentOS 6.5
CPU: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
MEMORY: 1GB

http2対応のnginxの導入

必要な物をyumで導入。

CentOS6.5
yum groupinstall -y "Development Tools"
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel

nginxのダウンロード

CentOS6.5
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を当てる

CentOS6.5
cd nginx-1.9.4/
curl http://nginx.org/patches/http2/patch.http2.txt > patch.http2.txt
patch -p1 < patch.http2.txt 

ビルド

CentOS6.5
./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の起動

CentOS6.5
/usr/local/nginx-1.9.4/sbin/nginx
ps aux | grep nginx
CentOS6.5
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の許可

iptables
# 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の作成

CentOS6.5
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

表示するページの作成

CentOS6.5
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の設定ファイルの修正

nginx.conf
#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

CentOS6.5
/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で見てみる。

SnapCrab_NoName_2015-9-17_14-57-53_No-00.png

おぉ。確かにhttp2通信になってる。
一応nginxのログも確認する。

access.log
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証明書を取得し、再度色々検証していきたいと思う。

48
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
44