最近、Habréにもトピックが登場し、著者がこのキャッシュ方法を説明しました。
ソリューションの第3バージョンのこのトピックでは、著者はトピックの読者を招待して、この問題に対する独自のソリューションを提供しました。
実際、このトピックはこのトピック専用です。
問題の声明
ほとんどの場合、Webページはブロックで構成されています。 たとえば、単純なページの場合、これらはブロックです。ヘッダー、フッター、右または左のブロック、メインコンテンツのブロックです。 サイトがより複雑な場合、それに応じて、たとえば、Habrの場合、これらのブロックはより多くなります。これらは「最後の投稿」、「最後のコメント」、「類似の投稿」などのブロックです。 したがって、プレゼンテーションレベルでページをキャッシュする場合、つまり 直接生成されたhtml このページに配置されたブロックのいずれかが変更された場合、そのようなページのキャッシュを無効にする必要があります。
そのため、ほとんどの場合、キャッシュはモデルまたはデータレベルで適用され、特定のページテンプレートに入力されます。
ここでSSIが助けになります。このテクノロジーのおかげで、実際にページをこれらの同じ論理ブロックに分割し、各ブロックを個別にキャッシュします。
SSI挿入を使用したページの例:
<html> <body> <div class="header"> <!--# include virtual="/header.php" --> </div> <div class="main_content"> <!--# include virtual="/main.php" --> </div> <!--# include virtual="/footer.php" --> </body> </html>
<html> <body> <div class="header"> <!--# include virtual="/header.php" --> </div> <div class="main_content"> <!--# include virtual="/main.php" --> </div> <!--# include virtual="/footer.php" --> </body> </html>
ここでは、すべてがうまくいくように思われますが、いくつか残しておきたいことがあります。
問題
- パーソナライズされたブロックは、「Hello、%username%!」など、一部のユーザーの個人データを含むブロックです。 実際、このようなデータが多数存在する可能性があるため、VKontakteで同じプロファイルを使用してください。 許可されたユーザーのブロックと混同しないでください! キャッシュには2番目のインスタンスが2つしかありません(ログインしているユーザー用であり、ログインしているユーザー用ではありません)。最初のインスタンスでは、各ユーザーのビューをキャッシュに保存する必要があります。 このタイプのキーをmemkesh {%block_id%} _ {%PHPSESSID | user_id%}に保存します。 そして、プレゼンテーションレベル、つまり データに加えて、ユーザーごとに繰り返される一連のhtmlコードも格納するため、この場合のキャッシュ(Memcached)のメモリ消費は非常に大きくなります。 大規模なサーバーミームキャッシュファームでは、一部のサーバーが時々落ちることがあり、一貫性のあるハッシュアルゴリズムを使用しても問題が残るという事実については話していません。
- キャッシュのウォームアップには多くの時間がかかります(通常は再起動後、新しいバージョンのリリース後など)。
何が提供されていますか?
また、次のキャッシュメカニズムが提案されています。
- プレゼンテーションを担当するブロックは、すべてのユーザーに一般化されています。 サイトのすべてのユーザーのキャッシュに1つのブロックインスタンスのみを格納するために、パーソナライズされたデータをすべて削除します。 これらのブロックには何が残っていますか? そうです、通常のプレゼンテーションテンプレートが残り、ユーザーに渡されます。各ユーザーは、JavaScriptを使用して、クライアント側でこのテンプレートに自分で入力します。 つまり クライアントは、ページへのリクエストに応じて、論理ブロックで構成されるページを受け取ります。各ブロックは順番にテンプレートになります。 例えば
<html> <本体> <div id = "head_block"> ここに{%personified%}データがあります </ div> <div id = "main_block"> こんにちは{%username%}! </ div> </ body> </ html>
または、たとえば、このように
<html> <本体> <div id = "head_block"> 一部の<div id = "{%personified%}"> </ div>データはこちら </ div> <div id = "main_block"> こんにちは<div id = "{%username%}"> </ div>! </ div> </ body> </ html>
- javascriptでデータを入力するには、どこかから取得する必要があります。 iframe-containerを使用するか、AJAXリクエストを使用してデータを受信します。 好きなように。 つまり 返されるページには不可視のiframeまたはURLを含む非表示の入力が含まれ、これを参照して各ブロックのデータを含むURLのリストを取得します。
その結果、ユーザーはそのようなページを受け取ります
<html> <本体> <div id = "head_block"> 一部の<div id = "{%personified%}"> </ div>データはこちら </ div> <div id = "main_block"> こんにちは<div id = "{%username%}"> </ div>! </ div> <iframe src = "all_blocks_data_urls.php" style = "display:none"> </ iframe> <!-または--> <input type = "hidden" name = "all_blocks_data_urls" value = "all_blocks_data_urls.php" /> </ body> </ html>
- スクリプトall_blocks_data_urls.phpは、このユーザーのキャッシュがあるかどうかを確認する単純なスクリプトです。 次のようになります。
<?php ....... $ key = $ memcached-> get($ user_id.'all_blocks_data_urls '); if($キー){ header( "HTTP / 1.1 304 Not Modified"); 出る } else { //ここでURLを抽出し、ユーザーに送信します } ?>
つまり この場合、キャッシュにデータを保存しません。 各セッションのキーは、適切なバージョンのキャッシュがユーザーによって保存されているかどうかを示すセマフォとして機能します。 適切であれば、304ヘッダーを返し、データが更新されていないことを伝えます。そうでない場合は、URLのリストを更新します。 Lbこれを下に。
- それで、上で書いたように。 ブロックの各データセットはURLで表され、アドレス「all_blocks_data_urls.php」のIFRAMEへの要求によって返されます。 サーバー側では、キーごとにアクセス可能な各ユーザーのセマフォがあります
$ memcached-> get($ user_id.'all_blocks_data_urls ')
、および次の形式のブロック内のデータ用に生成されたURLを含むmemkeshのキーのリスト。
$ user_id .'_ '。$ block_id =>' hash_for_url '
現在、最も興味深いのは、データを受信する各URLがユーザー固有である、たとえば
www.site.com?block_id=1&hash=hash_for_urlが返され、「Expires」という見出しが表示されます。 ブラウザのHTTPヘッダーを通じてデータを永久にキャッシュします。
- ユーザーが何らかの種類の論理ブロック(ソーシャルの教育セクションなど)に関連付けられたデータを更新するとき。 ネットワーク、次に、一般的なキャッシュブロック$ memcached-> delete($ user_id.'all_blocks_data_urls ')のmemkeshのキーをリセットし、このブロックのデータを含むURLを格納するキーを削除します$ memcached-> delete($ user_id .'_' 。$ block_id)。 IFRAMEコンテナへの次のリクエストでは、スクリプトall_blocks_data_urls.phpは304応答を返しませんが、不足しているブロックのURLを再度形成し、memcacheに2つのキーを設定してユーザーにリストを返します($ user_id.'all_blocks_data_urls 'and $ user_id .'_'。 $ block_id)。 さらに、データ自体、つまり データキャッシュはどこにも保存しません! すべてのキャッシュは、ユーザーのブラウザキャッシュレベルで編成されます。
- クライアントがブロックのデータを示すすべてのURLを受信した後。 彼はそれらを要求し始めます、なぜなら これらのURLは、サーバーによってExpiresヘッダーとともに提供され、すべてのデータではないにしても、ほとんどの場合、クライアントはブラウザーキャッシュから受信します。 ところで、JSON、XML、HTMLなどのデータ形式を選択できます。 さまざまな形式のカスタムにカスタマイズすることもできます! 実際、最後に、JavaScriptを使用してテンプレートを処理し、ユーザーデータを入力します。
まとめ
- サーバーのキャッシュに保存するデータ量が大幅に削減される一方で、クライアント側でのhttpヘッダーのキャッシュに基づいて、CSI(クライアント側を含む)と呼ぶブロックキャッシュを取得しました。
- ほとんどの場合、彼らはロジックを表現から分離しました。 概して、このアプローチを使用すると、サイトのさまざまなレイアウトやカスタムレイアウトを非常に簡単に作成できます。 他のJavaScriptライブラリを接続するだけです。 繰り返しますが、異なるプラットフォーム、特にモバイル向けの異なるプレゼンテーションです。
一部の検索エンジンのクローラーはJavaScriptを処理しないため、このキャッシュ方法の欠点にはインデックス作成の問題が含まれます
アプローチと提案に関するあなたの意見を聞くのは興味深いでしょう。