
本日公開する翻訳の資料は、Node.jsのHTTP / 2、特にServer Pushでの作業に専念しています。
基本
HTTP / 2をテストするための最も簡単な方法は、新しい
http2
カーネル
http2
一部である互換性レイヤーを使用すること
http2
。
const http2 = require('http2'); const options = { key: getKeySomehow(), cert: getCertSomehow() }; // https, // const server = http2.createSecureServer(options, (req, res) => { res.end('Hello World!'); }); server.listen(3000);
互換性レイヤーは、
http
モジュールを
require('http')
コマンドでプロジェクトに接続することで使用できる同じ高レベルAPI(使い慣れた
request
および
response
オブジェクトを持つ要求リスナー)を提供します。 これにより、既存のプロジェクトをHTTP / 2に簡単に変換できます。
互換性レイヤーは、HTTP / 2フレームワークの作成者に移行する便利な方法も提供します。 そのため、 RestifyおよびFastifyライブラリは、HTTP / 2 Node.js互換レイヤーを使用してHTTP / 2を既にサポートしています。
Fastifyは、パフォーマンス指向で、プログラマが作業しやすいように設計された新しいWebフレームワークです。 プラグインの豊富なエコシステムがあります。 最近そのバージョン1.0.0をリリースしました 。
fastify
HTTP / 2を使用するの
fastify
非常に簡単です。
const Fastify = require('fastify'); // https, // const fastify = Fastify({ http2: true https: { key: getKeySomehow(), cert: getCertSomehow() } }); fastify.get('/fastify', async (request, reply) => { return 'Hello World!'; }); server.listen(3000);
プロトコルの実装フェーズでは、HTTP / 1.1の上とHTTP / 2の上で同じアプリケーションコードを実行できることが重要ですが、互換性レイヤー自体は、最も強力なHTTP / 2機能の一部へのアクセスを提供しません。
http2
カーネル
http2
使用
http2
と、ストリームリスナーからアクセスできる新しいカーネルAPI( Http2Stream )を介してこれらの追加機能を操作できます。
const http2 = require('http2'); const options = { key: getKeySomehow(), cert: getCertSomehow() }; // https, // const server = http2.createSecureServer(options); server.on('stream', (stream, headers) => { // stream - // headers - , // respond // - (:) stream.respond({ ':status': 200 }); // , , stream.respondWithFile() // stream.pushStream() stream.end('Hello World!'); }); server.listen(3000);
fastifyでは、
request.raw.stream
APIを介して
Http2Stream
アクセスできます。 次のようになります。
fastify.get('/fastify', async (request, reply) => { request.raw.stream.pushStream({ ':path': '/a/resource' }, function (err, stream) { if (err) { request.log.warn(err); return } stream.respond({ ':status': 200 }); stream.end('content'); }); return 'Hello World!'; });
HTTP / 2サーバープッシュ-機能と課題
HTTP / 1と比較して、HTTP / 2は多くの場合、パフォーマンスを大幅に改善します。 サーバープッシュテクノロジは、これに関連するHTTP / 2の機能の1つです。
典型的なHTTPセッションがどのように単純化されているかを以下に示します。

ハッカーニュースセッション
- ブラウザーはサーバーにHTMLドキュメントを要求します
- サーバーはリクエストを処理し、ドキュメントをブラウザに送信し、場合によっては事前に生成します。
- ブラウザはサーバーの応答を受信し、HTMLドキュメントを解析します。
- ブラウザは、スタイルシート、画像、JavaScriptファイルなど、HTMLドキュメントの出力に必要なリソースを識別します。 その後、ブラウザはこれらのリソースを受信するためのリクエストを送信します。
- サーバーは各リクエストに応答し、リクエストしたものをブラウザに送信します。
- ブラウザは、HTMLドキュメントコードと関連リソースを使用してページをレンダリングします。
これはすべて、一般的なブラウザとサーバーの通信セッション中に、1つのHTMLドキュメントを出力するために、ブラウザはいくつかの独立したリクエストを作成し、それらに対する応答を待つ必要があることを意味します。 最初のリクエストはHTMLコードをロードし、残りは追加のマテリアルをロードします。これがないと、ドキュメントを正しく表示できません。 これらの追加資料のすべてを元のHTMLドキュメントとともにブラウザに送信できれば、ブラウザがそれらを個別にダウンロードする必要がなくなります。 実際のところ、HTTP / 2サーバープッシュテクノロジは、このような作業シナリオの編成を対象としています。
HTTP / 2を使用する場合、サーバーは独自のイニシアチブで、元の要求への応答とともに追加のリソースを自動的に送信できます。 これらは、サーバーによると、ブラウザーが確実に後で要求するリソースです。 ブラウザがこれらのリソースを必要とする場合、それらを受信するために追加のリクエストを送信する代わりに、サーバーが事前に送信したデータを使用するだけで十分です。
たとえば、サーバー
/index.html
次の内容の
/index.html
ファイル
/index.html
保存する
/index.html
ます。
<!DOCTYPE html> <html> <head> <title>Awesome Unicorn!</title> <link rel="stylesheet" type="text/css" href="/static/awesome.css"> </head> <body> This is an awesome Unicorn! <img src="/static/unicorn.png"> </body> </html>
対応する要求を受信すると、サーバーはこのファイルを送信して応答します。 同時に、サーバーは、/
/index.html
ファイルの正しい出力に
/static/awesome.css
および
/static/unicorn.png
ファイルが必要であることを認識しています。 その結果、サーバーはサーバープッシュメカニズムを使用して、これらのファイルを
/index.html
ファイルとともに送信します。
for (const asset of ['/static/awesome.css', '/static/unicorn.png']) { // stream - ServerHttp2Stream. stream.pushStream({':path': asset}, (err, pushStream) => { if (err) throw err; pushStream.respondWithFile(asset); }); }
クライアント側では、ブラウザが
/index.html
ファイルのコードを解析するとすぐに、このドキュメントをレンダリングするには
static/awesome.css
/static/unicorn.png
および
/static/unicorn.png
ファイルが必要であることを理解します。 さらに、これらのファイルはサーバーの主導で既に送信され、ブラウザーのキャッシュに保存されていることがブラウザーに明らかになります。 その結果、サーバーに2つの追加リクエストを送信する必要がなくなります。 代わりに、すでにロードされているキャッシュからデータを取得するだけです。
これまで、これはすべて非常に良いように見えます。 ただし、上記のシナリオでよく見ると、潜在的な困難を見つけることができます。 そもそも、サーバーが最初のブラウザー要求に応じて、イニシアチブで送信できる追加リソースを見つけることはそれほど簡単ではありません。 この決定のロジックは、開発者を非難して、アプリケーションレベルに持ち込むことができます。 しかし、サイト開発者でさえ、そのような決定を下すのは難しいと感じるかもしれません。 これを行う1つの方法は次のとおりです。開発者はHTMLコードを見て、ページがブラウザーに正しく表示されるために必要な追加リソースのリストをコンパイルします。 ただし、アプリケーションの開発中、そのようなリストを最新の状態に維持するには時間がかかり、エラーが発生します。
もう1つの考えられる問題は、ブラウザの内部メカニズムが最近ダウンロードされたリソースのキャッシュに関与しているという事実にあります。 上記の例に戻りましょう。 たとえば、昨日ブラウザがファイル
/index.html
ロードした場合、ファイル
/static/unicorn.png
をダウンロードしたことになります。これは通常、ブラウザキャッシュに分類されます。 ブラウザが
/index.html
再度ロードしてから
/static/unicorn.png
ファイルをダウンロードしようとすると、このファイルが既にキャッシュにあることが
/static/unicorn.png
ます。 したがって、ブラウザはこのファイルをダウンロードする要求を実行せず、代わりにキャッシュから受信します。 この場合、サーバーの主導で
/static/unicorn.png
ファイル
/static/unicorn.png
ブラウザに送信すると、ネットワークリソースが無駄になります。 サーバーがブラウザが特定のリソースを既にキャッシュしているかどうかを理解できる何らかのメカニズムを備えていると便利です。
実際、サーバープッシュテクノロジに関連する他の重要なタスクがあります。 興味がある場合は、 このドキュメントをお読みください。
HTTP / 2サーバープッシュの使用を自動化する
Node.js開発者向けのサーバープッシュのサポートを簡素化するために、Googleは自動化のためのnpmパッケージh2-auto-pushを公開しました。 このパッケージは、多くの困難な問題を解決するために設計されています 。その中には、上記で説明した問題や、 このドキュメントで言及されている問題があります 。
このパッケージは、ブラウザーからのリクエストのパターンを明らかにし、ブラウザーが適用するソースリソースに関連付けられている追加リソースを見つけます。 後で、同じソースリソースを要求すると、サーバーの主導で追加のリソースが自動的にブラウザーに送信されます。 さらに、パッケージは、ブラウザーのキャッシュに既にいくつかのリソースがある可能性を評価し、それが判明した場合、これらのリソースをブラウザーに送信しません。
このパッケージは、さまざまなWebフレームワークのミドルウェア層で使用するために設計されています。 特に、静的ファイルを提供するツールについて話している。 その結果、このパッケージを使用すると、サーバー主導でブラウザーへの素材の送信を自動化するための補助ツールの開発が容易になります。 たとえば、 fastify-auto-pushパッケージを見てください。 これはfastifyのプラグインであり 、サーバーのイニシアチブでブラウザーへのマテリアルの送信を自動化し、 h2-auto-pushパッケージを使用するように設計されています。
このミドルウェアは、アプリケーションからも非常に簡単に使用できます。
const fastify = require('fastify'); const fastifyAutoPush = require('fastify-auto-push'); const fs = require('fs'); const path = require('path'); const {promisify} = require('util'); const fsReadFile = promisify(fs.readFile); const STATIC_DIR = path.join(__dirname, 'static'); const CERTS_DIR = path.join(__dirname, 'certs'); const PORT = 8080; async function createServerOptions() { const readCertFile = (filename) => { return fsReadFile(path.join(CERTS_DIR, filename)); }; const [key, cert] = await Promise.all( [readCertFile('server.key'), readCertFile('server.crt')]); return {key, cert}; } async function main() { const {key, cert} = await createServerOptions(); // HTTP/2 https. const app = fastify({https: {key, cert}, http2: true}); // AutoPush. // . app.register(fastifyAutoPush.staticServe, {root: STATIC_DIR}); await app.listen(PORT); console.log(`Listening on port ${PORT}`); } main().catch((err) => { console.error(err); });
まとめ
Node.js Foundationが実施したテストによると、
h2-auto-push
を使用すると、サーバープッシュテクノロジーなしでHTTP / 2を使用した場合に比べてパフォーマンスが約12%向上し、パフォーマンスが約135%向上することがわかりました。 HTTP / 1。
親愛なる読者! サーバープッシュテクノロジーについてどう思いますか?
