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