こんにちは。ベトナムからエンジニアCCです。
2015末から全て主要なブラウザがhttp2をサポートになりました。
2年前http2を運用しようと思いましたが、色んな疑問がありました。
http2のServer Pushの問題
- railsがhttp2を対応しますか?
- nginxがhttp2を対応しますか?
- メリットってありますか?(Server Pushとか利用できますか?)
など
特にServer Pushですね。
例えば、nginxじゃなくてh2oを切り替えてmagic的にserver pushを利用できるかと思っていました。
そう出来ればrails側はhttp2を対応しなくてもhttp2のメリットがあるかと思いました。
普通のリクエストは基本には5つのフェーズがあります。
Proxy(nginxやh2o)はhtmlを受けて、処理して以下の状態になります。
で、各フェーズのタイミングを確認したらあまり効果がないかと思っていました。
Download時間だけ節約できましたが、日本のインターネットスピードはめっちゃ早いので、結局何もメリットできない。
理想的にはserver pushはproxyサーバーからじゃなくてアプリから送るのほうがいいではないですか。
(こんな感じ)
そうすと別の問題起こってしまいました。
まずはrailsはhttp2を対応必要。
例えrailsはhttp2を対応しても以下のような形が必要と思いました。
http2自体はsslが必要ので、↑のようアーキテックはめっちゃ複雑になっています。
Rails 5.2
Rails 5.2から HTTP/2 Early Hints
を対応くれました
https://railsguides.jp/5_2_release_notes.html
具体的にはどんな対応だろか?
http/2 early hints対応プルリク: https://github.com/rails/rails/pull/30744
→ 簡単説明すると、javascript_include_tag
やstylesheet_link_tag
を実行する瞬間に
Link: </style.css>; as=style; rel=preload
のheaderを反します。
これ自体はhttp1だけら以下の設定で動けます
やってみましょう
ということで実際やってみました。
以下のdocker-composeで試しました。
version: "3.7"
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- ./:/app
- ./tmp/root/:/root # save .bash_history
stdin_open: true
tty: true
nginx:
image: nginx:latest
links:
- app
ports:
- "443:443"
- "80:80"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/certs/:/etc/ssl/certs:ro
nginx.conf
server {
listen 443 ssl http2;
ssl_certificate /etc/ssl/certs/localhost.crt;
ssl_certificate_key /etc/ssl/certs/localhost.key;
http2_push_preload on;
add_header Strict-Transport-Security "max-age=31536000";
location / {
proxy_set_header Host $http_host;
proxy_pass_request_body on;
proxy_pass_request_headers on;
proxy_pass http://upstream;
}
}
chromeのdevtoolでNetworkタブを見てhttp2を見えました
ただ、最初のページはスタチックページなのでjavascript_include_tag
やstylesheet_link_tag
を実行されないです。→ Server pushまだ為できない。
→ controllerを作成して、見たら。。。
ERR_SPDY_PROTOCOL_ERROR
エラー出た
以下のurlでみてエラーログを収集して確認しました
chrome://net-export/
エラーのログ:
{"description":"DATA received before headers.","net_error":"ERR_HTTP2_PROTOCOL_ERROR","stream_id":3}
なぜ?この記事によって1.13.9からhttp2が対応くれるはずなのに、↑のエラー出てしました
https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
nginx version: nginx/1.17.6
そこで諦めしました。h2oを使って試しました。
h2o.conf
h2o:
image: lkwg82/h2o-http2-server:latest
links:
- app
ports:
- "443:1443"
- "80:8080"
restart: always
volumes:
- ./docker/h2o/h2o.conf:/home/h2o/h2o.conf:ro
- ./docker/certs/:/etc/ssl/certs:ro
動けました
結論
- 違いドメインは対応くれないからassetsとhtmlは別originだとpushできません
- 直接htmlを利用しなくて必ずjavascript_include_tagやstylesheet_link_tagを書かないとpushできません
- controllerで遅い処理があったらあまり効果がない、queryとかはviews側で発生したら効果あります
- キャッシュされたリソースがメリットがありません
- なぜかNGINX動きません
- jsとcssしか対応していません(画像やfontまだ対応されていません)
-
@import
とかで書いたらpushできません - 本番ではダイナミックCSSやダイナミックJavascriptがあったらメリットがあるが、普通はあまりないです。
- 開発環境で使ったらいいだと思います。