nginx
CentOS
SPDY
http2.0
http2

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

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