HTTP/2対応のWebサーバソフトウェアであるh2oを試してみました。
OS Xでインストール&セットアップ
インストール$ brew install h2o
launchctlで操作できるようにplistのリンクを作成しておきます。
$ ln -sfv /usr/local/opt/h2o/*.plist ~/Library/LaunchAgents
リンクを貼ったplistはlaunchctlでstart/stopできるように以下のように修正します
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>homebrew.mxcl.h2o</string>
<key>RunAtLoad</key>
<false/>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/h2o/bin/h2o</string>
<string>-c</string>
<string>/usr/local/etc/h2o/h2o.conf</string>
</array>
</dict>
</plist>
launchdに読み込ませます
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.h2o.plist
あとは以下のコマンドでstart/stopできます
$ launchctl start homebrew.mxcl.h2o
$ launchctl stop homebrew.mxcl.h2o
HTTP/2を試したい場合はTLS接続が前提となっています。/usr/local/opt/h2o/share/doc/h2o/examplesにサンプルのconfigや秘密鍵、証明書が入っているので、それらを使ってHTTP/2をすぐに試せます。(もちろんオレオレ証明書ですが)
$ cd /usr/local/opt/h2o/share/doc/h2o
$ h2o -c ./examples/h2o/h2o.conf
Ubuntuでインストール
ソースからインストール(mruby対応版)$ sudo apt-get install locate git cmake build-essential \
checkinstall autoconf pkg-config libtool python-sphinx wget \
libcunit1-dev nettle-dev libyaml-dev libuv-dev bison ruby-dev -y
$ git clone git@github.com:h2o/h2o.git
$ cd h2o
$ cmake -DWITH_BUNDLED_SSL=on -DWITH_MRUBY=on .
$ make
$ sudo make install
サンプルの設定で回す場合は以下のようにして実行すればOK。
$ cd /usr/local/share/doc/h2o
$ sudo /usr/local/bin/h2o -c examples/h2o/h2o.conf
ChromeでHTTP/2を体感する
以下のHTMLにアクセスしてHTTP/2を体感してみます。
<!DOCTYPE html>
<html>
<body>
<img src="hoge1.png" />
<img src="hoge2.png" />
<img src="hoge3.png" />
<img src="hoge4.png" />
<img src="hoge5.png" />
<img src="hoge6.png" />
<img src="hoge7.png" />
<img src="hoge8.png" />
<img src="hoge9.png" />
<img src="hoge10.png" />
</body>
</html>
<!DOCTYPE html>
<html>
<body>
<img src="hoge1.png" />
<img src="hoge2.png" />
<img src="hoge3.png" />
<img src="hoge4.png" />
<img src="hoge5.png" />
<img src="hoge6.png" />
<img src="hoge7.png" />
<img src="hoge8.png" />
<img src="hoge9.png" />
<img src="hoge10.png" />
</body>
</html>
HTTP/1.1バージョン(http://{Host Name}:8080)
HTTP/2バージョン(https://{Host Name}:8081)
HTTP/1の場合は同一ドメインに対して同時接続数6の制限が効いていますが、HTTP/2の場合は1つのTCP接続を使うことになるので、同時にリクエストが行われます。
mruby拡張
404 Not Foundエラーをmrubyから返す場合paths:
/:
file.dir: /usr/local/share/doc/h2o/examples/doc_root
mruby.handler: |
Proc.new do |env|
[404, {'content-type' => 'text/plain'}, ["File Not Found By MRuby\n"]]
end
IPアドレス制限
paths:
"/":
mruby.handler: |
Proc.new do |env|
if /xxx\.xxx\.xxx\.xxx/.match(env["REMOTE_ADDR"])
[399, {}, []]
else
[403, {'content-type' => 'text/plain'}, ["access forbidden\n"]]
end
end
標準で用意されているディレクティブによらず柔軟な処理が可能なので便利
HTTP/2のサーバPushを体感する
H2Oのmruby拡張を使ってサーバPushを体感してみます。設定ファイルのpaths以下は以下のように記載します。mruby.handlerだと設定ファイルに直接Rubyコードを書けますが、色々と不便なので、mruby.handler-fileで外部ファイルを読み込ませています。
paths:
/:
mruby.handler-file: /path/to/handler.rb
file.dir: /usr/local/share/doc/h2o/examples/doc_root
handler.rbはこんな感じで
Proc.new do |env|
push_paths = []
if /(\/|\.php)\z/.match(env["PATH_INFO"])
10.times do |i|
push_paths << "/hoge#{i+1}.png"
end
end
[399, push_paths.empty? ? {} : {
"link" => push_paths.map{|p| "<#{p}>; rel=preload; as=image"}.join("\n")
}, []]
end
リクエストしてみるとこんな感じでドキュメント読み込みが完了する前には既に画像取得が完了しているため、トータルのリクエスト時間は短くなります(注・以下のリクエストはsleepを意図的に入れているため、上記例とのタイムライン上の比較は出来ません)
サンプルなのでlinkが固定値(hoge*.rb)になっていますが、実際には各ドキュメントに対して静的なファイルをマッピングして、handler側でマッピングされた静的ファイルのリストをlinkヘッダで返すような形になります。実運用的にはクローラのようなHTMLパーサでマッピングを自動生成するような感じになるんですかね。
PHPと連携してみる
php-fpmを使ってphpを利用できるようにします。php5-fpmがUnixドメインソケットを利用している場合は以下のように記述します。user: www-data
listen: 8080
listen:
port: 8081
ssl:
certificate-file: examples/h2o/server.crt
key-file: examples/h2o/server.key
file.custom-handler:
extension: .php
fastcgi.connect:
port: /var/run/php5-fpm.sock
type: unix
hosts:
"127.0.0.1.xip.io:8080":
paths:
/:
file.dir: /usr/local/share/doc/h2o/examples/doc_root
access-log: /dev/stdout
"alternate.127.0.0.1.xip.io:8081":
listen:
port: 8081
ssl:
certificate-file: examples/h2o/alternate.crt
key-file: examples/h2o/alternate.key
paths:
/:
file.dir: /usr/local/share/doc/h2o/examples/doc_root.alternate
access-log: /dev/stdout
file.dirは絶対パスにしないとFastCGI側で以下のエラーになります。
[lib/handler/fastcgi.c] in request:/index.php:Primary script unknown
userが未指定の場合はh2oのプログラムを起動したユーザが実行ユーザになります。実行ユーザがphp5-fpmのソケットに対して適切なパーミッションが無い場合、以下のエラーになります。
[lib/handler/fastcgi.c] in request:/index.php:
connection failed:failed to connect to host
今回試した環境では以下のようなパーミッションになっていたので、www-dataユーザを実行ユーザにする必要がありました。
$ ls -la /var/run/php5-fpm.sock
srw-rw---- 1 www-data www-data 0 10月 22 02:32 php5-fpm.sock
参考URL
- Mac OS X: Mac OS X: HTTP2 サーバーの h2o を起動させる - Sarabande.jp
- h2oでHTTP/2を使って同時リクエスト数を上げて表示高速化を試してみる - Qiita
- H2O - the optimized HTTP/2 server
- Using Mruby - Configure - H2O - the optimized HTTP/2 server
- HTTP/2 Directives - Configure - H2O - the optimized HTTP/2 server
- 自宅サーバーUbuntuをhttp/2化した - new_pill’s blog
- h2o での server-push タイミングの最適化 | tech - 氾濫原