Node.jsでのHTTP / 2サーバープッシュテクノロジーのサポート

20177月、 Node.js 8はHTTP / 2実装を導入しました。 それ以来、いくつかの改善段階を経ており、Node.js Foundationは、HTTP / 2サポートを実験的機能のカテゴリーから外す準備がほぼ整っていると述べています。 Node.js環境でHTTP / 2を試してみたい場合は、Node.js 9を使用してこれを行うのが最善です-最新のバグ修正と改善がすべてあります。

画像

本日公開する翻訳の資料は、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セッションがどのように単純化されているかを以下に示します。





ハッカーニュースセッション



  1. ブラウザーはサーバーにHTMLドキュメントを要求します
  2. サーバーはリクエストを処理し、ドキュメントをブラウザに送信し、場合によっては事前に生成します。
  3. ブラウザはサーバーの応答を受信し、HTMLドキュメントを解析します。
  4. ブラウザは、スタイルシート、画像、JavaScriptファイルなど、HTMLドキュメントの出力に必要なリソースを識別します。 その後、ブラウザはこれらのリソースを受信するためのリクエストを送信します。
  5. サーバーは各リクエストに応答し、リクエストしたものをブラウザに送信します。
  6. ブラウザは、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。



親愛なる読者! サーバープッシュテクノロジーについてどう思いますか?






All Articles