HTTP/2の復習メモ
HTTP/2対応を行う機会が来そうな気がしないでもない感じがしてきたので、いろいろおさらいしてみる。
HTTP/1.1の特徴
- 1つのコネクション上では、1リクエストが完了するまで、新しいリクエストを送ることができない
- 効率化のため、多くのブラウザは、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で高速化するかどうかは実際のところは環境に依るし検証してみないと分からないというオチ。
参考
- HTTP/2 Frequently Asked Questions | HTTP/2 Japan Local Activity
- HTTP/2 - Wikipedia
- HTTPパイプライン - Wikipedia
- NginxによるHTTP/2 事始め - Qiita
- 「HTTP/2」がついに登場! 開発者が知っておきたい通信の仕組み・新機能・導入方法 (1/3):CodeZine(コードジン)
- SUUMOスマホサイトでHTTP/2対応 | Tech Blog - リクルート住まいカンパニー
- Head of Line Blocking - High Performance Web 2015 - Qiita
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の推奨設定
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()の呼び出し方によって以下のような違いがあるため注意が必要です。
例えば、Jenkinsでデプロイシェルを実行したあとにphp -r 'opcache_reset();'
しても、webのオペコードキャッシュはリセットされないため、web経由でopcache_resetを実行させる仕組みを用意する必要があります。
参考
2016-08 大阪・京都・神戸 旅行メモ
8/26(金)
津田沼駅から新今宮駅まで青春18切符でだいたい10時間くらい。
- 熱海駅 乗り継ぎで時間が空いたから降りたが特に何もなかった。
- 弁天島駅 静岡県浜松市、浜名湖の河口にある駅。謎の鳥居を見ながら海岸沿いを散歩。
- 名古屋駅 ひつまぶし。普通にうな重のまま食べたほうが美味しくない?
大阪駅地下の餃子屋チャオチャオで良い感じに呑んだり、ドヤ街で有名な西成区のホテルジパングに宿泊(1泊2000円)したりする。
8/27(土)
朝、ドヤ街にある入船温泉に入る。番頭さんがいる銭湯に入るのは初めてだったかも。
8/28(日)
サントリー巡礼の日。この日は天王寺駅前のビジネスホテルに宿泊する。
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 -)"
を加える必要があった。