waste of time

主にPHP

HTTP/2の復習メモ

HTTP/2対応を行う機会が来そうな気がしないでもない感じがしてきたので、いろいろおさらいしてみる。

HTTP/1.1の特徴

  • 1つのコネクション上では、1リクエストが完了するまで、新しいリクエストを送ることができない
    • HTTP/1.1からはHTTPパイプラインがサポートされ、1コネクション上でリクエストの応答を待たずに新しいリクエストを送信することができるようになった
      • HTTPパイプラインの問題点
        • リクエストとレスポンスの順序は同期していなければならず、HOL(Head of Line)ブロッキング(先頭のレスポンスが遅いとその後のレスポンスも待ち状態になる)が生じ得る
        • そもそもモダンブラウザが対応していない・デフォルトで設定がオフになっている(実装が困難であったり、HOLブロッキングを回避するため)
        • よってHTTPパイプラインは広く使われていない
  • 効率化のため、多くのブラウザは、1ドメインへの接続を同時に複数行う(コネクションの多重化)
    • ブラウザは6リクエストを同時に行うために、同時に6コネクション使っている
    • 同時接続はサーバ・ネットワークへの負荷に直結する

HTTP/2の特徴

  • 1つのコネクション上で、複数のリクエストを非同期で並列化出来る
  • ヘッダを圧縮して送信するのに加え、前回からの差分のみ送信する
  • メッセージはテキストの代わりにバイナリでやり取りされる
  • 主要ブラウザは対応済み ただしHTTP/2 over TLSのみなので全ページSSL化必須
  • Apache2.4.17, nginx1.9.5対応済み
  • 開発経緯としては、GoogleのSPDYプロジェクトから発展

非同期並列接続による効率化に加え、ヘッダ圧縮やバイナリ化でデータサイズが小さくなるため、HTTP/2化するだけで基本的には恩恵を受けられそう。 テキストでなくバイナリでのやり取りになるので、多少デバッグしづらくなったりなどあるのだろうか…?

良いところばかりのように思えるが、弱点もある。 非同期並列接続の実現によりHTTPレベルのHOLブロッキングは発生しなくなるが、TCPレベルのHOLブロッキングが問題になる場合が出てくるようだ。 そもそもHOLブロッキングとは、TCPとHTTPそれぞれのプロトコルで発生しうるものらしい。

  • HTTPにおけるHOLブロッキング
    • HTTPパイプラインにおいて、コネクション上のある遅いHTTPリクエストにより後続のHTTPリクエストがブロックされる状態
  • TCPにおけるHOLブロッキング
    • IPパケットがロストして再送される際に、後続のIPパケットがブロックされる状態

HTTP/2化したからといって必ずしも全てのケースで高速化するかというとそうでもないようだ。 例えばサイズの大きい画像が大量にあるページの場合、HTTP HOLブロッキングよりもTCP HOLブロッキングのほうが問題になる可能性がある。HTTP1.1であればTCPのHOLブロッキングが生じても複数コネクションで回避(?)できていたが、HTTP/2ではコネクションは1つのみなのでTCPのHOLブロッキングは回避できない。 改善策としてmTCPやらQUICやら新しい取り組みがあるらしいがまだ普及には至っていない。 ということでHTTP/2で高速化するかどうかは実際のところは環境に依るし検証してみないと分からないというオチ。

参考

PHPのキャッシュ機構のおさらいと、opcacheの設定とかキャッシュクリアとか

PHPのキャッシュ機構

  • APC
    • PHP: APC - Manual
    • オペコードキャッシュ機能+データキャッシュ(ユーザキャッシュ)機能をもつ。現在は開発が停止されており、PHP5.5.0以降は以下2つのキャッシュ機構の使用が推奨されている。
  • APCu
    • PHP: APCu - Manual
    • APCからオペコードキャッシュ機能を取り除いて、データキャッシュ機能のみを提供する。デフォルトでバンドルされていない。
  • Zend OPcache
    • PHP: OPcache - Manual
    • オペコードキャッシュ機能を提供する。PHP5.5.0以降はデフォルトでバンドルされている。

APCが廃止されたのは、重大なバグがあったためらしい。

opcacheの推奨設定

PHP: インストール手順 - Manual

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

opcacheは、opcache.revalidate_freqで設定した秒数(デフォルト2秒)おきにファイルの変更をタイムスタンプで確認し、変更があればキャッシュを再生成します。 上記の推奨設定例だと、自動デプロイされたファイルがキャッシュに反映されるまで最大60秒かかってしまうため、CIツールなどで自動デプロイされたタイミングでopcache_reset()またはopcache_invalidate()関数を使ってキャッシュをクリアすると良さそうです。

opcache_reset()の罠

opcache_reset()を呼び出すと、全てのオペコードキャッシュがリセットされ、次回ヒット時は再びソースを読み込んでパースします。 ただし、opcache_reset()の呼び出し方によって以下のような違いがあるため注意が必要です。

  • CLIPHPで呼び出す
    • CLIPHPで生成されたキャッシュのみリセットされる。
  • モジュール版PHPで呼び出す
    • 全てのキャッシュがリセットされる。

例えば、Jenkinsでデプロイシェルを実行したあとにphp -r 'opcache_reset();'しても、webのオペコードキャッシュはリセットされないため、web経由でopcache_resetを実行させる仕組みを用意する必要があります。

参考

2016-08 大阪・京都・神戸 旅行メモ

8/26(金)

津田沼駅から新今宮駅まで青春18切符でだいたい10時間くらい。

  • 熱海駅 乗り継ぎで時間が空いたから降りたが特に何もなかった。
  • 弁天島駅 静岡県浜松市浜名湖の河口にある駅。謎の鳥居を見ながら海岸沿いを散歩。
  • 名古屋駅 ひつまぶし。普通にうな重のまま食べたほうが美味しくない?

大阪駅地下の餃子屋チャオチャオで良い感じに呑んだり、ドヤ街で有名な西成区のホテルジパングに宿泊(1泊2000円)したりする。

8/27(土)

朝、ドヤ街にある入船温泉に入る。番頭さんがいる銭湯に入るのは初めてだったかも。

  • 神戸
  • 有馬温泉 激安ノークレームプランで宿泊した旅館の部屋でゴキブリを確認する。フロントがゴキジェット常備してて助かった(?)

8/28(日)

  • サントリー京都ビール工場 3杯まで試飲できる太っ腹さ。
  • 山崎蒸留所 有料テイスティング。響JAPANESE HARMONY/響17年/響21年の中だと17年が好き。
  • 新世界 定番の串かつ

サントリー巡礼の日。この日は天王寺駅前のビジネスホテルに宿泊する。

8/29(月)

台風が怖かったので新幹線で帰る。秋葉原、田中そば店の肉そばで〆。

bundle installしたらeventmachineのインストールに失敗した件

たぶんまたハマるだろうからメモ。

事象

$ bundle install --path vendor/bundle
Fetching git://github.com/eventmachine/eventmachine.git
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Resolving dependencies...
Using backports 3.6.8
Using daemons 1.2.3
Using eventmachine 1.2.0.1 from git://github.com/eventmachine/eventmachine.git (at master@6580e27)

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /usr/bin/ruby2.1 extconf.rb
mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h

extconf failed, exit code 1

Gem files will remain installed in /var/www/html/hoge/vendor/bundle/ruby/2.1.0/bundler/gems/eventmachine-6580e27f695d for inspection.
Results logged to /var/www/html/hoge/vendor/bundle/ruby/2.1.0/bundler/gems/extensions/x86_64-linux/2.1.0/eventmachine-6580e27f695d/gem_make.out
Using multi_json 1.12.1
Using rack 1.6.4
Using tilt 2.0.5
Using bundler 1.12.5
An error occurred while installing eventmachine (1.2.0.1), and Bundler cannot continue.

解決

$ sudo apt-get -y install ruby-dev ruby2.1-dev

rbenv + ruby-buildでrubyをインストールする

yum installで入るrubyが1.8だったので、rbenvのプラグインであるruby-build経由で最新のruby(2016/5/29現時点の安定版v2.3.1)をインストールする。

この記事に書いてあることはすべて公式ドキュメントから辿れる内容なので、公式ドキュメントを見よう。
Rubyのインストール

rbenvをインストールする

github.com 書いてある通り、clone→make→パス追加→initするだけ。

ruby-buildをインストールする

github.com rbenvのプラグインとして動作させる場合は、.rbenv/pluginsの中にcloneするだけ。簡単。

rubyをインストールする

これで準備完了したので、rubyをインストールする。 インストールできるrubyの一覧を表示。

$ rbenv install --list

普通のruby(CRuby)の他に、jrubyとかmrubyとかrbxとかいろんなruby実装が出てくる。 rbxってのはrubyによるruby実装らしい(Rubinius)。pypyみたいな。

普通のrubyがやりたいので、一覧からバージョン表記のみのものを指定してインストールする。 今回は一番新しい2.3.1を入れる。

$ rbenv install 2.3.1

エラー出た。

Downloading ruby-2.3.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.bz2
Installing ruby-2.3.1...

BUILD FAILED (CentOS release 6.7 (Final) using ruby-build 20160426-33-g3304f96)

Inspect or clean up the working tree at /tmp/ruby-build.20160529232814.3766
Results logged to /tmp/ruby-build.20160529232814.3766.log

Last 10 log lines:
installing rdoc:              /home/{USER}/.rbenv/versions/2.3.1/share/ri/2.3.0/system
installing capi-docs:         /home/{USER}/.rbenv/versions/2.3.1/share/doc/ruby
The Ruby readline extension was not compiled.
ERROR: Ruby install aborted due to missing extensions
Try running `yum install -y readline-devel` to fetch missing dependencies.

Configure options used:
  --prefix=/home/{USER}/.rbenv/versions/2.3.1
  LDFLAGS=-L/home/{USER}/.rbenv/versions/2.3.1/lib
  CPPFLAGS=-I/home/{USER}/.rbenv/versions/2.3.1/include

言われるがままreadline-develをインストールする。

$ sudo yum install readline-devel

再度rubyインストール。

$ rbenv install 2.3.1

Downloading ruby-2.3.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.bz2
Installing ruby-2.3.1...
Installed ruby-2.3.1 to /home/{USER}/.rbenv/versions/2.3.1

Installed!
rbenv versionsコマンドで、インストール済みのrubyバージョンが見られる。
rbenv global 2.3.1でglobalのバージョンが設定できるし、rbenv local 2.3.1な感じでディレクトリごとのバージョンも設定できる。

rubyとかirbのパスはどこに通せば良いのだろうか。 ~/.rbenv/versions/2.3.1/bin/に直接通すものなのかな?globalのバージョンが自動で割り当てられるようにしたいんだけども…

2016/5/30追記
~/.rbenv/shimsの中が現在のバージョンに切り替わっている(?)ぽいのでここをパスに追加すれば良さそう。

2016/8/8追記
rbenv initでshimsとautocompletionが有効になるので、.bash_profile或いは.bashrcにeval "$(rbenv init -)"を加える必要があった。