Thursday, June 18, 2015

H2OとPHPを組み合わせるの、超簡単です(もしくはmod_rewriteが不要な理由)

FastCGI対応機能がH2Oにマージされたことを受けて、uzullaさんが「H2OでPHP(がちょっとだけ動くまで)」という記事を書いてくださっています。

ありがたやありがたや。

その中で、
http://hoge/entry/1 みたいなのをphpにマップする方法はまだよくわかってません。その内しらべます

github.comを読む限り
FastCGI (or PHP) applications should be as easily configurable as it is for the Apache HTTP server
ということで、やったぜ!ってなるんですけど、nginxはもとより、Apacheにおいても現状ルーターをつかっているようなアプリだとhtaccessをいちいちかかないといけないので、Apacheみたいなスタイルが楽なのか?というとちょっと疑問があります。

(たとえば以下みたいなの)
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
ここらへんがスッっとかけないと、まあApache、nginx同様にH2Oの設定のタレが必要になるかなあ、という感じはあります。
いやはや、まったくそのとおりなわけです。

ちなみにこの手のことをNginxでやろうとするともっと難しくて、以下のようにtry_filesfastcgi_split_path_infoを使わないと脆弱性が発生するなんて話があるそうです(参照: Setting up PHP-FastCGI and nginx? Don’t trust the tutorials: check your configuration! » Neal Poole)。これ、みんなちゃんとできてるんですかね?
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ \.php$ {
   try_files $uri =404;

   fastcgi_split_path_info ^(.+\.php)(/.+)$;
   include fastcgi_params;
   fastcgi_index index.php;
   fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
   fastcgi_pass php;
}

こんな難しい設定、書けるわけないだろうが!!!!!!!

というわけで、H2Oの場合はもっと簡単に設定できるようになっています。以下のような感じ。
paths:
  "/":
    # /path/to/doc-root以下の静的ファイルを返す(存在した場合)
    file.dir: /path/to/doc-root
    # 存在しなければ、/index.php/ に内部転送
    redirect:                     
      url: /index.php/
      internal: YES
      status: 307

単純ですね。どうなってるかはコメントを見れば自明かとも思いますが、以下解説します。

実は、H2Oの設定ファイルにおいては、1つのパスに複数のハンドラを設定することができるのです。

複数のハンドラが設定されている場合、H2O は有効なレスポンスが生成できるまで、ハンドラを順次実行していきます。つまり、コメントにあるように、ファイルが存在すればそれを返すし、存在しなければ /index.php/ 以下にリクエストを内部的に転送し、その転送した結果の応答をクライアントに返すわけです。

わかりやすいですね。簡単ですね。


少し理屈っぽい話をすると、mod_rewriteのようなリクエストを書き換える黒魔術は、おおむね3種類の目的に使われてきました。第1の目的は、複数のローカルのディレクトリツリーを重ね合わせてひとつのhttpサイトとして表示すること。第2の目的は、uzullaさんが指摘したような、特定の条件をもつ以外のURLの処理を、FastCGIやアプリケーションサーバに委譲すること。第3の目的は、User-Agentや言語設定によって異なる応答を返すこと。

ですが、前2者を実現するためだけならば、正規表現を用いてURLを書き換えるような手法は過剰にすぎて管理がしづらかったわけです。H2OにおけるPHP対応においては、この点を意識し、複数のハンドラの連鎖を可能にすることで、設定が安全簡潔にできるようにしたのでした。

ちなみに、uzullaさんの例ではphp-fpmを使っていますが、H2Oはphp-cgiを自分で管理することができるので、小規模なウェブサイトにおいては、
file.custom-handler:          
  extension: .php
  fastcgi.spawn: "PHP_FCGI_CHILDREN=10 exec /usr/bin/php-cgi"
のように書くと、別途FastCGIデーモンを管理する必要がなくなって、より幸せになれるでしょう(名前はphp-cgiでも、この設定においてはFastCGIとして動作します)。


最後になりますが、ここで説明したFastCGI対応機能を盛り込んだH2Oバージョン1.3は今朝方リリースされたので、PHPerの皆様もそれ以外の皆様も、お試しいただければ幸甚に存じます。

リンク先のリリース告知エントリにも書いてあるけど、他のウェブサーバより表示速度(first-paint time)が、かなり速いですよ!

H2O HTTP/2 server version 1.3.0 released; provides faster response to user, adds support for FastCGI, range-request

Today we are happy to announce the release of H2O version 1.3.0. The new release includes many changes and bug fixes since 1.2.0, but the notable ones are as follows.

Faster response to user

It is known that providing faster response to users greatly improve their experience, and many web engineers work hard on optimizing the time spent until user starts seeing the contents. The theoretically easiest way to optimize the speed (without reducing the size of the data being transmitted) is to transfer essential contents first, before transmitting other data such as images.

With the finalization of HTTP/2, such approach has become practical thanks to it's dependency-based prioritization features. However, not all web browsers (and web servers) optimally prioritize the requests. The sad fact is that some of them do not prioritize the requests at all which actually leads to worse performance that HTTP/1.1 in some cases.

Since the release of version 1.2.0, we have conducted benchmark tests that measure first-paint time (time spent until the web browser starts rendering the new webpage), and have added a tuning parameter that can be turned on to optimize the first-paint time of web browsers that do not leverage the dependency-based prioritization, while not disturbing those that implement sophisticated prioritization logic.

The chart below shows the first-paint time measured using a virtual network with 100ms latency (typical for 4G mobile networks), rendering a web page containing jquery, CSS and multiple image files.

First-Paint Time (network latency: 100ms)

It is evident that the prioritization logic implemented in H2O and the web browsers together offer a huge reduction in first-paint time. As the developer of H2O, we believe that the prioritization logic implemented in H2O to be the best of class (if not the best among all), not only implementing the specification correctly but also for having practical tweaks to optimize against the existing web browsers.

In other words, web-site administrators can provide better (or the best) user-experience to the users by switching their web server to H2O. For more information regarding the topic, please read HTTP/2 (and H2O) improves user experience over HTTP/1.1 or SPDY.

Version 1.3.0 also supports TCP fast open, an extension to TCP/IP that reduces the time required for establishing a new connection. The extension is already implemented in Linux (and Android), and is also expected to be included in iOS 9. As of H2O version 1.3.0 the feature is turned on by default to provide even quicker user experience. Kudos go to Tatsuhiko Kubo for implementing the feature.

FastCGI support

Since the initial release of H2O many users have asked for the feature; it is finally available! And we are also proud that it is easy to use.

First, it can be configured either at path-level or extension-level. The latter means that for example you can simply map .php files to the FastCGI handler without writing regular expressions to extract PATH_INFO.

The second is the ability to launch FastCGI process manager under the control of H2O. You do not need to spawn an external FastCGI server and maintain it separately.

Using these features, for example Wordpress can be set-up just by writing few lines of configuration.
paths:
  "/":
    # serve static files if found
    file.dir: /path/to/doc-root
    # if not found, internally redirect to /index.php/...
    redirect:                     
      url: /index.php/
      internal: YES
      status: 307
    # handle PHP scripts using php-cgi (FastCGI mode)
    file.custom-handler:          
      extension: .php
      fastcgi.spawn: "PHP_FCGI_CHILDREN=10 exec /usr/bin/php-cgi"
Of course it is possible to configure H2O to connect to FastCGI applications externally using TCP/IP or unix sockets.

Support for range-requests

Support for range-requests (HTTP requests that request a portion of a file) is essential for serving audio/video files. Thanks to Justin Zhu it is now supported by H2O.

Conclusion

All in all, H2O has become a much better product in version 1.3 by improving end-user experience and by adding new features.

We plan to continue improving the product. Stay tuned!

Wednesday, June 3, 2015

HTTP/2 (and H2O) improves user experience over HTTP/1.1 or SPDY

HTTP/2 is expected to offer better user experience than HTTP/1.1, the unanswered question is how much the benefit is in practice.

Tonight I have given a presentation regarding the issue, showing HTTP/2 performance of H2O HTTP server at shibuya.pm, a popular technology meetup at Tokyo. This blog post is a summary of the presentation at the meetup, following my recent blog post about the prioritization logic of HTTP/2 and web browser implementations.

Test Scenario:
  • a modified version of http2rulez.com (<script> tags moved into <head>)
    • contains 5 CSS files
    • 8 script files in <head>, including a minified version of jquery.js
    • contains 18 not-blocking assets (e.g. images files), both small and large
  • client: Chrome/43 and Firefox/38
  • server: Nginx/1.9.1 and H2O/HEAD
  • network: dedicated virtual network to avoid noise; 100ms latency (typical for 4G mobile) added artificially using tc disc command

Benchmark Results:


Analysis:

When using H2O (and HTTP/2) with optimal settings, the first-paint times are reduced by about 30% when compared to HTTP/1.1 or SPDY/3.1.

First-paint time is a good index of user experience; it shows the timing when the user sees the page being rendered for the first time (or when all assets that block rendering arrives at the client side) since he/she triggered the navigation.

The improvements are due to the fact that Firefox (or Chrome with H2O's http2-reprioritize-blocking-assets configuration directive set to ON) downloads assets files that block rendering (e.g. CSS or JS files) before other asset files (e.g. images).

Conclusion:

With the benchmark showing 30% reduction in first-paint time, it is obvious that HTTP/2 (implemented by H2O) is offering superior performance against the protocols of previous generations. However, implementations might not be mature enough to offer such boost in performance (as discussed in recent blog post, Chrome has issues with its prioritization logic which is worked around by the configuration directive of H2O; Nginx does not prioritize the streams even though such feature is available in SPDY).

For the time being it is suggested that users conduct benchmarks to see if their applications actually become faster, or consult the developers of the HTTP servers for benchmark results.

Or, you can use H2O, which has been hereby shown to provide good performance under the described scenario :-)


PS. The presentation slides used at the meetup is available here: