第 1 章 - Dispatcher の概念、パターン、アンチパターン

概要

この章では、Dispatcher の歴史と仕組みを簡単に紹介し、これが AEM 開発者のコンポーネントの設計方法にどのように影響するかについて説明します。

開発者がインフラストラクチャを重視すべき理由

Dispatcher は、ほぼすべての AEM インストールに不可欠な要素です。 Dispatcher の設定方法とヒントやテクニックを説明するオンライン記事が多数あります。

しかし、これらの細かい情報は常に非常に技術的なレベルから始まります。自分のしたいことを既に把握できている前提で、それを実現する方法の詳細のみを説明しているのです。 Dispatcher でできることとできないことに関して言えば、その​ 内容と理由 ​を説明している概念的なドキュメントは見つかりませんでした。

アンチパターン:付け足しとしての Dispatcher

このような基本情報の欠如は、数々の AEM プロジェクトで見てきた多数のアンチパターンにつながります。

  1. Dispatcher は Apache web サーバーにインストールされるので、その設定は「Unix の神様」のようなプロジェクトメンバーの仕事です。 「普通の Java 開発者」は、それを気にする必要はありません。

  2. Java 開発者は、自分のコードを確実に動作させる必要があります。Dispatcher は後からそれを魔法のように高速化します。 Dispatcher は常に付け足しです。 ただし、これでは使えません。 開発者は、Dispatcher を念頭に置いてコードを設計する必要があります。 そのための基本的な概念を理解しておく必要があります。

「まず動くようにしてから高速化する」が常に正しいとは限らない

プログラミングのアドバイス を聞いたことがあるかもしれません。「最初に機能させる – 次に高速にする」。 完全に間違っているわけではありません。 ただし、コンテキストが正しくないと、誤って解釈され、正しく適用されない傾向があります。

このアドバイスは、実行されない、またはほとんど実行されないコードを早まって最適化してしまうことを防ぐためのものです。そのようなコードは、最適化しても労力に見合うだけの効果が得られない可能性が高いからです。 さらに、最適化によって、コードがより複雑になり、バグを引き起こす可能性があります。 したがって、開発者の方は、コードの各行を細かく最適化することに時間をかけすぎないでください。 適切なデータ構造、アルゴリズム、ライブラリを選択し、プロファイラーによるホットスポット分析を待ち、より綿密な最適化が全体的なパフォーマンスを向上させる場所を確認するだけです。

アーキテクチャの決定とアーティファクト

しかし、「アーキテクチャの」決定に関しては、「まず動作させて、そして高速化する」というアドバイスは完全に間違っています。 アーキテクチャの決定とは何ですか? 簡単に言えば、コストがかかり、難しく、後で変更できない決定です。 「コストがかかる」イコール「不可能」、となることもあります。 例えば、プロジェクトの予算が足りなくなった場合、コストがかかる変更は実装できません。 インフラに関する変更は、そのカテゴリに属する変更の中で、ほとんどの人が真っ先に思い浮かべるものです。 しかし、変更が非常に厄介になり得る、別の種類の「アーキテクチャ上の」アーティファクトもあります。

  1. アプリケーションの「中央」にあり、他の多くの部分が依存するコードです。 これらを変更するには、すべての依存関係を一度に変更し、再テストする必要があります。

  2. 非同期でタイミングに依存するシナリオに関与するアーティファクトで、そうしたシナリオでは入力、ひいてはシステムの動作が非常にランダムに変化する可能性があります。 変更は予測できない影響を及ぼし、テストが困難になる場合があります。

  3. システムのあらゆる部分で使用され、何度も繰り返し使用されるソフトウェアパターン。 ソフトウェアパターンが最適でないと判断された場合は、そのパターンを使用するすべてのアーティファクトを再コーディングする必要があります。

次のことに留意してください。 このページのトップで、Dispatcher は AEM アプリケーションの重要な部分であると述べました。 Web アプリケーションへのアクセスは非常にランダムです。ユーザーは予測不可能な時間にアクセスします。 最終的には、すべてのコンテンツが Dispatcher でキャッシュされる(またはキャッシュされるべき)ことになります。 注意深く読んでいれば、キャッシュは「アーキテクチャ上の」アーティファクトと見なすことができ、そのため開発者や管理者を含むチームの全メンバーが理解しておくべきものであることに気付いたかもしれません。

開発者が実際に Dispatcher を設定する必要があると言っているわけではありません。 コードを Dispatcher でも活用できるようにするには、概念、特にその境界を理解しておく必要があります。

Dispatcher は、コードの速度を魔法のように向上させるものではありません。 開発者は、Dispatcher を考慮してコンポーネントを作成する必要があります。 したがって、開発者はその仕組みを知っておく必要があります。

Dispatcher キャッシュ - 基本原則

キャッシュ HTTP ロードバランサーとしての Dispatcher

Dispatcher とは何ですか?まず、なぜ「Dispatcher」と呼ばれるのですか?

Dispatcher では、

  • 最初にキャッシュが表示されます。

  • リバースプロキシ

  • Apache httpd web サーバー用のモジュールで、Apache の汎用性に AEM 関連の機能を追加し、他のすべての Apache モジュール(後で説明するように、SSL や SSI など)とスムーズに連携します。

Web の初期の頃は、サイトの訪問者数は数百人になると予想されていました。 1 つの Dispatcher を用意し、複数の AEM パブリッシュサーバーへのリクエストの負荷を分散させる構成で、通常はこれで十分でした。これが「Dispatcher」という名前の由来です。 しかし、今では、この構成はあまり頻繁には使用されていません。

この記事の後半で、Dispatcher とパブリッシュ環境を設定する様々な方法について説明します。 まず、http キャッシュの基本を紹介します。

Dispatcher キャッシュの基本機能

Dispatcher キャッシュの基本機能

Dispatcher の基本について、ここで説明します。 Dispatcher は、HTTP リクエストを受信および作成できる、単純なキャッシュリバースプロキシです。 通常のリクエストおよびレスポンスのサイクルは次のようになります。

  1. ユーザーがページをリクエストします。
  2. Dispatcher は、そのページのレンダリング済みバージョンが既に存在するかどうかをチェックします。 これがこのページに対する最初のリクエストであり、Dispatcher がキャッシュされたローカルコピーを見つけられないと仮定します。
  3. Dispatcher がパブリッシュシステムにページをリクエストします。
  4. パブリッシュシステムで、ページが JSP または HTL テンプレートでレンダリングされます。
  5. ページが Dispatcher に返されます。
  6. Dispatcher がページをキャッシュします。
  7. Dispatcher がページをブラウザーに返します。
  8. 同じページが 2 回目に要求された場合、パブリッシュインスタンスで再レンダリングしなくても、Dispatcher キャッシュから直接提供できます。 これにより、ユーザーの待ち時間を短縮し、パブリッシュインスタンスでの CPU サイクルを節約できます。

最後のセクションでは「ページ」の話をしました。 しかし、同じ仕組みは、画像、CSS ファイル、PDF のダウンロードなど、他のリソースにも当てはまります。

データのキャッシュ方法

Dispatcher モジュールは、ホスト側の Apache サーバーが提供する機能を利用します。 HTML ページ、ダウンロード、画像などのリソースは、単純なファイルとして Apache のファイルシステムに保存されます。 非常にシンプルです。

ファイル名は、要求されたリソースの URL に基づいて生成されます。 ファイル /foo/bar.html をリクエストすると、/var/cache/docroot/foo/bar.html などに格納されます。

原則として、すべてのファイルがキャッシュされて Dispatcher に静的に保存されている場合は、Publish 環境のプラグを抜いても、Dispatcher は単純な web サーバーとして機能します。 これは原理を示すための単なる例にすぎません。 現実はより複雑です。 すべてをキャッシュすることはできません。また、レンダリングプロセスの動的性質により、リソースの数が無限になる可能性があるので、キャッシュが完全に「いっぱい」になることはありません。 静的ファイルシステムのモデルは、Dispatcher の機能の概要を生成するのに役立ちます。 また、Dispatcher の制限事項についても説明します。

AEM URL 構造とファイルシステムマッピング

Dispatcher について詳しく理解するために、単純なサンプル URL の構造を再度参照してみましょう。 次の例を見てみましょう。

http://domain.com/path/to/resource/pagename.selectors.html/path/suffix.ext?parameter=value&otherparameter=value#fragment

  • http はプロトコルを示します。

  • domain.com はドメイン名です。

  • path/to/resource は、リソースが CRX に保存され、その後 Apache サーバーのファイルシステムに保存されるパスです。

ここからは、AEM ファイルシステムと Apache ファイルシステムでは少し異なります。

AEM では、次のようになります。

  • pagename はリソースラベルです。

  • selectors は、リソースのレンダリング方法を決定するために Sling で使用される多数のセレクターを表します。 1 つの URL に任意の数のセレクターを含めることができます。 区切り文字はピリオドです。 セレクターセクションは、例えば、「french.mobile.fancy」のようになります。 セレクターには、文字、数字、ダッシュのみを含める必要があります。

  • 「セレクター」の最後の html は、拡張子と呼ばれます。 AEM/Sling では、セレクターによってレンダリングスクリプトが部分的に決定されます。

  • path/suffix.ext は、URL のサフィックスを指定できるパスに似た式です。 リソースのレンダリング方法をさらに制御するために、AEM スクリプトで使用できます。 この部分については後の節で説明します。 現時点では、追加のパラメーターとして使用できることを知っていれば十分です。 サフィックスには拡張子が必要です。

  • ?parameter=value&otherparameter=value は、URL のクエリセクションです。 任意のパラメーターを AEM に渡すために使用されます。 パラメーターを持つ URL はキャッシュできないので、パラメーターは、絶対に必要な場合に限定する必要があります。

  • #fragment URL のフラグメント部分は AEM には渡されず、ブラウザーでのみ使用されます。JavaScript フレームワークで「ルーティングパラメーター」として使用するか、ページ上の特定の部分にジャンプします。

Apache(以下の図を参照)では、

  • pagename.selectors.html は、キャッシュのファイルシステムでファイル名として使用されます。

URL にサフィックス path/suffix.ext が付いている場合、

  • pagename.selectors.html はフォルダーとして作成されます

  • path は pagename.selectors.html フォルダー内のフォルダーです。

  • suffix.ext は path フォルダー内のファイルです。 メモ:サフィックスに拡張子が含まれていない場合、ファイルはキャッシュされません。

Dispatcher から URL を取得した後のファイルシステムのレイアウト

Dispatcher から URL を取得した後のファイルシステムのレイアウト

基本的な制限事項

URL、リソース、ファイル名のマッピングは非常に簡単です。

ただし、お気づきかもしれませんが、いくつかの罠があります。

  1. URL が非常に長くなる場合があります。 ローカルファイルシステム上で /docroot の「パス」部分を追加すると、一部のファイルシステムの制限を簡単に超えてしまう可能性があります。 Windows 上の NTFS で Dispatcher を実行することは、課題となる場合があります。 Linux では安全です。

  2. URL には、特殊文字やウムラウトを含めることができます。 これは、通常、Dispatcher で問題にはなりません。 ただし、URL はアプリケーションの様々な場所で解釈されることに留意してください。 アプリケーションの奇妙な動作について調べた結果、ごくまれに使用される(カスタム)コードの一部で、特殊文字に対するテストが十分に行われていなかったと判明するケースがしばしば見られます。 できれば特殊文字の使用を避けてください。 それができない場合は、徹底的なテストを実施できるよう計画してください。

  3. CRX では、リソースにはサブリソースが含まれます。 例えば、あるページに多数のサブページが含まれるとします。 これはファイルシステムでは表現できません。ファイルシステムにはファイルかフォルダーしか存在しないためです。

拡張子のない URL はキャッシュされない

URL には常に拡張子が必要です。 AEM では拡張子のない URL を提供できます。 このような URL は Dispatcher でキャッシュされません。

例

http://domain.com/home.html は​キャッシュ可能

http://domain.com/home は​キャッシュ不可能

URL にサフィックスが含まれている場合も、同じルールが適用されます。 キャッシュ可能なサフィックスには拡張子が必要です。

例

http://domain.com/home.html/path/suffix.html は​キャッシュ可能

http://domain.com/home.html/path/suffix は​キャッシュ不可能

リソース部分に拡張子がなく、サフィックスに拡張子がある場合はどうでしょうか。 この場合、URL にサフィックスはありません。 次の例を見てみましょう。

例

http://domain.com/home/path/suffix.ext

/home/path/suffix はリソースへのパスなので、URL にサフィックスはありません。

まとめ

常に、パスとサフィックスの両方に拡張子を追加します。 SEO に詳しい人たちは、これが検索結果でのランキングを下げていると主張することがあります。 しかし、キャッシュされていないページは非常に遅くなり、さらに低いランキングになります。

競合するサフィックス URL

2 つの有効な URL があるとします。

http://domain.com/home.html

および

http://domain.com/home.html/suffix.html

これらは、AEM では間違いなく有効です。 (Dispatcher なしの)ローカル開発マシンでは、問題は発生しません。 ほとんどの場合、UAT や負荷テストでも問題が発生することはありません。 AEM で発生する問題は非常に微妙で、ほとんどのテストをすり抜けてしまいます。 これらは、ピーク時になって、その問題に対処する時間が限られていて、おそらくサーバーへのアクセス権も、修正するためのリソースもないようなときに、大きな問題となります。 私たちはこのような状況を経験してきました。

では、何が問題なのでしょうか。

ファイルシステムの home.html には、ファイルまたはフォルダーのどちらかを指定できます。 AEM のように、両方を同時に指定することはできません。

最初に home.html をリクエストした場合、それはファイルとして作成されます。

その後の home.html/suffix.html へのリクエストでは有効な結果が返されますが、ファイル home.html がファイルシステム内の位置を「ブロック」しているので、home.html は 2 回目はフォルダーとして作成できず、home.html/suffix.html はキャッシュされません。

ファイルシステム内のファイルブロック位置により、サブリソースがキャッシュされない

ファイルシステム内のファイルブロック位置により、サブリソースがキャッシュされない

逆に、最初に home.html/suffix.html をリクエストすると、suffix.html はまずフォルダー /home.html にキャッシュされます。 しかし、その後 home.html をリソースとしてリクエストすると、このフォルダーは削除され、ファイル home.html に置き換えられます。

親がリソースとして取得されたときにパス構造を削除する

親がリソースとして取得されたときにパス構造を削除する

したがって、キャッシュされる結果は完全にランダムで、受け取るリクエストの順序に応じて異なります。 さらに難しいのは、通常は Dispatcher が複数あるということです。 また、パフォーマンス、キャッシュのヒット率および動作は、Dispatcher によって異なる場合があります。 Web サイトが応答しない理由を調べたい場合は、キャッシュ順序に問題があるが、正しい Dispatcher をチェックするようにしてください。 もし、たまたまリクエストパターンの条件が良かった Dispatcher だけを見ていると、イシューを見つけることはできません。

競合する URL の回避

サフィックスを持つリソースに別の拡張子を使用する場合は、ファイルシステム内の同じパスに対してフォルダー名とファイル名が「競合」する「URL の競合」を回避できます。

例

  • http://domain.com/home.html

  • http://domain.com/home.dir/suffix.html

どちらも完全にキャッシュ可能です。

サフィックスをリクエストする場合は、リソースに専用の拡張子「dir」を選択するか、サフィックス自体を使用しないようにします。 このような手段が有用な場合も稀にあります。 また、これらのケースを正しく実装することは非常に容易です。 次の章では、キャッシュの無効化とフラッシュについて説明します。

キャッシュ不可能なリクエスト

前章の簡単なまとめと、その他の例外についてレビューします。 Dispatcher は、URL がキャッシュ可能として設定されていて、そのリクエストが GET リクエストである場合は、その URL をキャッシュできます。 次のいずれかの例外においては、キャッシュできません。

キャッシュ可能なリクエスト

  • リクエストが Dispatcher の設定でキャッシュ可能に設定されている
  • リクエストがプレーンな GET リクエストである

キャッシュ不可能なリクエストまたは応答

  • 設定(パス、パターン、MIME タイプ)によってキャッシュ対象から除外されたリクエスト
  • 「Dispatcher: no-cache」ヘッダーを返す応答
  • 「Cache-Control: no-cache|private」ヘッダーを返す応答
  • 「Pragma: no-cache」ヘッダーを返す応答
  • クエリパラメーター付きのリクエスト
  • 拡張子のない URL
  • 拡張子のないサフィックス付きの URL
  • 200 以外のステータスコードを返す応答
  • POST リクエスト

キャッシュの無効化とフラッシュ

概要

前章では、Dispatcher がリクエストをキャッシュできない場合の例外を多数挙げました。 しかし、考慮すべき点が他にもあります。Dispatcher でリクエストをキャッシュ​ できる ​場合でも、必ずしもキャッシュ​ すべき ​とは限りません。

要するに、キャッシュ処理は通常は簡単です。 Dispatcher は、応答の結果を保存し、次に同一のリクエストを受信したときにそれを返すだけで済みます。 そうではありませんか? いいえ、違います!

難しいのはキャッシュの​ 無効化 ​または​ フラッシュ ​です。 リソースが変更された場合、Dispatcher はそのリソースを見つけ出し、再度レンダリングする必要があります。

これは一見単純なタスクのようですが、そうではありません。 さらに読み進めると、単一の単純なリソースと、複数のリソースの高度にメッシュ化された構造に依存するページとの間に、いくつかの微妙な違いがあることがわかります。

シンプルなリソースとフラッシュ

AEM システムでは、リクエストに応じて、特別な「サムネイル」セレクターを使用して各画像のサムネイルレンディションを動的に作成するように設定しています。

/content/dam/path/to/image.thumb.png

そしてもちろん、セレクターなしの URL で元の画像を配信するための URL も用意しています。

/content/dam/path/to/image.png

サムネイルと元の画像の両方をダウンロードすると、最終的には次のようになります。

/var/cache/dispatcher/docroot/content/dam/path/to/image.thumb.png

/var/cache/dispatcher/docroot/content/dam/path/to/image.png

(Dispatcher のファイルシステムの場合)。

次に、ユーザーはそのファイルの新しいバージョンをアップロードし、アクティブ化します。 最終的に、無効化リクエストが AEM から Dispatcher に送信されます。

GET /invalidate
invalidate-path:  /content/dam/path/to/image

<no body>

無効化は非常に簡単な、Dispatcher 上の特別な「/invalidate」URL に対する単純な GET リクエストです。 HTTP-body は不要で、「payload」は「invalidate-path」ヘッダーのみです。 また、ヘッダー内の invalidate-path は、AEM が把握しているリソースであり、Dispatcher がキャッシュしているファイル(群)そのものではないことにも注意してください。 AEM はリソースについてのみ認識します。 リソースが要求されると、実行時に拡張機能、セレクターおよびサフィックスが使用されます。 AEM は、リソースで使用されているセレクターに関するブックキーピングを行いません。そのため、リソースをアクティブ化する際に確実に把握しているのは、そのリソースパスだけです。

ここではこれで十分です。 リソースが変更された場合は、そのリソースのすべてのレンディションも変更されたと見なすことができます。 この例では、画像が変更された場合は、新しいサムネイルもレンダリングされます。

Dispatcher は、キャッシュしているリソースとそのすべてのレンディションを安全に削除できます。 例えば、次のような処理を行います。

$ rm /content/dam/path/to/image.*

のように、image.png および image.thumb.png と、そのパターンに一致する他のすべてのレンディションを削除します。

リクエストに応答するのに 1 つのリソースだけを使用する限り、非常にシンプルです。

参照とメッシュコンテンツ

メッシュコンテンツの問題

AEM にアップロードされた画像や他のバイナリファイルとは異なり、HTML ページは単体で使用するものではありません。 多くのページと共に使用され、ハイパーリンクや参照によって互いに密接に繋がっています。 シンプルなリンクは無害ですが、コンテンツ参照に関してはコツが必要です。 ページ上のユビキタスなトップナビゲーションまたはティーザーは、コンテンツ参照です。

コンテンツ参照とそれが問題となる理由

簡単な例を見てみましょう。 ある旅行代理店は、カナダ旅行を促進する web ページを持っています。 このプロモーションは、他の 2 ページ、「ホーム」ページ、「Winter Specials」ページのティーザーセクションで特集されます。

両方のページに同じティーザーが表示されるので、作成者に対して、表示するページごとに、ティーザーを複数回作成するように依頼する必要はありません。 代わりに、ターゲットページ「Canada」では、ページプロパティ内にティーザー用のセクションを確保し、ティーザー用の情報、より正確にはそのティーザー全体をレンダリングする URL を指定します。

<sling:include resource="/content/home/destinations/canada" addSelectors="teaser" />

または

<sling:include resource="/content/home/destinations/canada/jcr:content/teaser" />

AEM 上だけであれば問題なく動作しますが、パブリッシュインスタンスで Dispatcher を使用すると、何か奇妙なことが起こります。

例えば、web サイトを公開したとします。 Canada ページのタイトルは「Canada」です。 訪問者がホームページ(そのページへのティーザー参照を持つ)をリクエストすると、「Canada」ページ上のコンポーネントは次のような内容をレンダリングします。

<div class="teaser">
  <h3>Canada</h3>
  <img …>
</div>

のように、ホームページ​ へ ​レンダリングされます。 ホームページは、Dispatcher によって静的な .html ファイルとして保存されます。このファイルにはティーザーとそのヘッドラインが含まれます。

これで、マーケターは、ティーザーのヘッドラインが実用的でなければならないことを学びました。 したがって、タイトルを「Canada」から「Visit Canada」に変更し、画像も更新します。

編集した「Canada」ページを公開し、以前に公開したホームページを再度開いて、変更内容を確認します。 しかし、何も変化は見られず、。 古いティーザーが表示されています。 「Winter Special」ページを再確認します。 そのページはこれまでにリクエストされたことがないので、Dispatcher で静的にキャッシュされていません。 したがって、このページは Publish によって新しくレンダリングされ、このページには新しい「Visit Canada」ティーザーが含まれるようになりました。

Dispatcher が古くなったインクルードコンテンツをホームページに保存する

Dispatcher が古くなったインクルードコンテンツをホームページに保存する

なぜこうなったのでしょうか。 Dispatcher は、レンダリング時に他のリソースから取り出されたすべてのコンテンツとマークアップを含む、ページの静的バージョンを保存します。

Dispatcher は単なるファイルシステムベースの web サーバーで、高速でありながら、比較的シンプルです。 含まれるリソースが変更されても、それは認識されません。 含めるページをレンダリングした際に存在していたコンテンツを、引き続き保持します。

「Winter Special」ページはまだレンダリングされていないので、Dispatcher には静的バージョンはなく、リクエストに応じてレンダリングされた新しいティーザーが表示されます。

Dispatcher はレンダリング時に扱ったすべてのリソースを追跡し、そのリソースが変更されたときに、そのリソースを使用したすべてのページをフラッシュするだろうと考えるかもしれません。 しかし、Dispatcher はページをレンダリングしません。 レンダリングは公開システムによって実行されます。 Dispatcher は、レンダリングされた .html ファイルに格納されるリソースを把握していません。

まだ納得がいかないという方は、 「何らかの依存関係の追跡を実装する方法があるはずだ」と思われるかもしれません。** 実際、より正確に言うと、以前は​そのような方法がありました。 AEM の遠い祖先である Communiqué 3 では、ページのレンダリングに使用されるセッションに依存関係トラッカーが実装されていました。__

リクエストの際、このセッションを通じて取得された各リソースは、レンダリング中の URL の依存関係として追跡されました。

しかし、依存関係の追跡には、非常にコストがかかりました。 すぐに、依存関係のトラッキング機能を完全にオフにし、1 つの html ページが変更された後にすべての html ページを再レンダリングするほうが、web サイトが速くなることが判明しました。 そのスキームも完璧ではなく、途中で落とし穴や例外が多く見られました。 リクエストのデフォルトセッションを使用してリソースを取得するのではなく、管理者セッションを使用してリクエストをレンダリングするためのヘルパーリソースを取得する場合もありました。 これらの依存関係は通常追跡されず、悩みの種となり、手動でキャッシュをフラッシュしてほしいという要望を運用チームが受けることもありました。 そのような標準的な手順が用意されていれば、運が良いほうでした。 途中には他にも多くの問題がありましたが、思い出話はここまでにしましょう。 2005年に遡ります。 最終的に、この機能は Communiqué 4 でデフォルトで無効となり、後継の CQ5 でも戻されず、CQ5 は AEM になりました。

自動無効化

依存関係の追跡よりも完全フラッシュの方が安価な場合

CQ5 以降、アドビでは、1 ページのみが変更された場合も、基本的にはサイト全体を無効化する処理に完全に依存しています。 この機能は「自動無効化」と呼ばれます。

しかし、何百ものページを破棄して再レンダリングするほうが、適切に依存関係を追跡して部分的に再レンダリングするよりも安価なのはなぜでしょうか。

主な理由は次の 2 つです。

  1. 平均的な web サイトでは、頻繁にリクエストされるのはページのサブセットにすぎません。 したがって、レンダリングされたコンテンツをすべて破棄した場合でも、その直後に実際にリクエストされるのは数十件です。 ページのロングテールのレンダリングは、実際にリクエストされた場合に、時間をかけて分散させることができます。 そのため、実際には、ページのレンダリングにかかる負荷は予想されるほど高くありません。 もちろん、常に例外はあります。空の Dispatcher キャッシュを持つ大規模な web サイトで、均等に分散された負荷を処理するいくつかのテクニックについては、後ほど説明します。

  2. すべてのページは、メインナビゲーションでつながっています。 つまり、最終的にはほとんどのページが互いに依存しています。 つまり、どんなに優れた依存関係トラッカーでも、1 つのページが変更された場合は他のすべてのページを無効にする必要がある、ということがわかります。

まだ疑っている方のために、 最後のポイントを説明しましょう。

前回の例と同じ引数で、リモートページのコンテンツを参照するティーザーを使用します。 今回は、より極端な例として、自動的にレンダリングされるメインナビゲーションを使用します。 ティーザーと同様に、ナビゲーションタイトルは、リンクされたページまたは「リモート」ページからコンテンツ参照として取り出されます。 リモートのナビゲーションタイトルは、現在レンダリングされているページには保存されません。 ナビゲーションは web サイトの各ページでレンダリングされることに注意してください。 したがって、メインナビゲーションを持つすべてのページで、1 つのページのタイトルが何度も繰り返し使用されます。 ナビゲーションタイトルを変更する場合は、ページを参照する各ページではなく、リモートページで 1 度だけ変更します。

つまり、この例では、ナビゲーションはターゲットページの「NavTitle」を使用してナビゲーション内で名前をレンダリングすることで、すべてのページを 1 つにまとめています。 「Iceland」のナビゲーションタイトルは、「Iceland」ページから取り出され、メインナビゲーションを持つ各ページにレンダリングされます。

メインナビゲーションは「NavTitle」を引き出すことで必然的にすべてのページのコンテンツをまとめている

メインナビゲーションは「NavTitle」を引き出すことで必然的にすべてのページのコンテンツをまとめている

アイスランドページの NavTitle を「Iceland」から「Beautiful Iceland」に変更すると、他のすべてのページのメインメニューで、直ちにタイトルが変更されます。 したがって、変更前にレンダリングおよびキャッシュされたページはすべて古くなり、無効にする必要があります。

自動無効化の実装方法:.stat ファイル

何千ものページを含む大規模なサイトがある場合、すべてのページをループして物理的に削除するにはかなりの時間がかかります。 その間に、Dispatcher が意図せず古いコンテンツを提供してしまう可能性があります。 さらに悪いことに、キャッシュファイルへのアクセス中に競合が発生する場合があります。たとえば、削除処理中のページがリクエストされたり、直後のアクティベーションの後に 2 回目の無効化が発生してページが再度削除される、といった状況です。 どれほど混乱するか想像してみてください。 幸いにも、このようなことは起こりません。 Dispatcher は巧妙なテクニックでそれを回避します。数百、数千のファイルを削除する代わりに、ファイルが公開されたときに単純な空のファイルをファイルシステムのルートに配置するので、依存するすべてのファイルが無効と見なされます。 このファイルは「statfile」と呼ばれます。 statfile は空のファイルです。statfile に関して重要なのは作成日のみです。

Dispatcher 内のすべてのファイルのうち、作成日が statfile よりも古いものは、最後のアクティベーション(および無効化)より前にレンダリングされたものなので、「無効」と見なされます。 これらのファイルはまだファイルシステム内に物理的に存在しますが、Dispatcher はそれらを無視します。 「古い」ファイルです。 古いリソースへのリクエストが行われるたびに、Dispatcher は AEM システムに対してページの再レンダリングを要求します。 新しくレンダリングされたページは、新しい作成日でファイルシステムに保存され、再び新しくなります。

.stat ファイルの作成日によって、古いコンテンツと新しいコンテンツが決定される

.stat ファイルの作成日によって、古いコンテンツと新しいコンテンツが決定される

なぜ「.stat」と呼ぶのか疑問に思うかもしれません。 なぜ「.invalidated」ではないのでしょうか。 ファイルシステムにそのファイルが含まれていると、Dispatcher がどのリソースを静的 web サーバーのように​ 静的に ​提供できるかを判断できます。 これらのファイルを動的にレンダリングする必要はなくなります。

この名前の本当の意味は、それほど比喩的なものではありません。 ファイルの変更時刻(とその他のプロパティ)を返す、Unix システム呼び出し stat() から派生しています。

単純な検証と自動検証の混在

しかし、先ほど、単一のリソースは物理的に削除されると説明しました。 ここでは、より新しい statfile が作成されると、これらのリソースは Dispatcher では事実上無効と見なされると説明しています。 では、なぜ最初に物理的に削除するのでしょうか。

答えは簡単です。 通常は両方の方法を並行して使用しますが、それぞれ異なる種類のリソースに対して使用します。 画像などのバイナリ形式のアセットは自己完結型です。 情報をレンダリングする必要があるという意味では、他のリソースとは結び付いていません。

一方、HTML ページは相互依存性が高くなっています。 これらに対して自動無効化を適用します。 これは、Dispatcher のデフォルト設定です。 無効化されたリソースに属するすべてのファイルは、物理的に削除されます。 また、「.html」で終わるファイルは自動無効化されます。

Dispatcher は、ファイル拡張子によって自動無効化スキームを適用するかどうかを決定します。

自動無効化の対象となるファイル拡張子は設定できます。 理論的には、すべての拡張子を自動無効化に含めることができます。 ただし、これには非常に高い代償が伴うことに注意してください。 古いリソースが意図せずに配信されることはありませんが、過度の無効化によって配信のパフォーマンスが大幅に低下します。

例えば、PNG と JPG が動的にレンダリングされ、そのために他のリソースに依存するスキームを実装するとします。 高解像度の画像を、web 互換の、より低い解像度に再調整する場合があります。 また、圧縮率も変更します。 この例では、解像度と圧縮率は固定された定数ではなく、画像を使用するコンポーネントで設定可能なパラメーターです。 このパラメーターを変更した場合は、画像を無効にする必要があります。

このような場合も問題ありません。自動無効化に画像を追加し、変更があっても常に新しくレンダリングされた画像を使用できることは前述のとおりです。

不要なものと共に大切なコンテンツまで破棄してしまう

これはその通りで、非常に大きな問題です。 前の段落をもう一度読んでください。 「何かが変更されるたびに新たにレンダリングされた画像」とあります。 ご存じのように、良い web サイトは常に更新されています。あちこちで新しいコンテンツを追加したり、誤字を修正したり、ティーザーを調整したりしています。 つまり、すべての画像が頻繁に無効化され、そのたびに再レンダリングする必要が生じます。 このことを軽視してはいけません。 画像データの動的なレンダリングと転送は、ローカル開発マシン上でミリ秒単位で機能します。 本番環境では、1 秒あたり 100 倍以上の頻度でこれを実行する必要があります。

ここで明確にしておきたいのは、HTML ページが変更された場合には JPG を再レンダリングする必要があり、その逆も同様だということです。 自動無効化されるファイルの「バケット」は 1 つしかありません。 全体としてフラッシュされます。 これ以上詳細な構造に分解する手段はありません。

自動無効化の対象がデフォルトで「.html」に設定されているのには、もっともな理由があります。 そのバケットをできるだけ小さく保つことが目標です。 安全策だからといって、すべてを無効化してしまい、有用なものまで一緒に捨ててしまわないようにしてください。

自己完結型のリソースは、そのリソースのパスで提供する必要があります。 それは無効化に大いに役立ちます。 「リソース /a/b/c」が「/x/y/z」から提供されるようなマッピングスキームを作成せず、シンプルにしてください。 コンポーネントは、Dispatcher のデフォルトの自動無効化設定で動作するようにします。 Dispatcher で過度の無効化を使用して、設計不良のコンポーネントを修復しようとしないでください。

自動無効化の例外:ResourceOnly による無効化

Dispatcher の無効化リクエストは、通常、レプリケーションエージェントによってパブリッシュシステムから実行されます。

依存関係について非常に自信がある場合は、独自の無効化レプリケーションエージェントを構築してみてもいいでしょう。

このガイドで詳細を説明するのは難しいですが、少なくともいくつかのヒントをお伝えします。

  1. 自分が何をしているかを十分に理解しておくことが重要です。 無効化を正しく行うことは非常に困難です。 それが、古いコンテンツが配信されるのを防ぐため、自動無効化が非常に厳密である理由の 1 つです。

  2. エージェントが HTTP ヘッダー CQ-Action-Scope: ResourceOnly を送信する場合、この単一の無効化リクエストは自動無効化をトリガーしません。 この(https://github.com/cqsupport/webinar-dispatchercache/tree/master/src/refetching-flush-agent/refetch-bundle)コードが、独自のレプリケーションエージェントを作成する良い出発点となるかもしれません。

  3. ResourceOnly は、自動無効化を防ぐだけです。 必要な依存関係の解決と無効化を実際に行うには、自分で無効化リクエストをトリガーする必要があります。 パッケージの Dispatcher フラッシュルール(https://adobe-consulting-services.github.io/acs-aem-commons/features/dispatcher-flush-rules/index.html)を確認すると、実際にどのようになるのかについてのヒントが得られるでしょう。

依存関係解決スキームを構築することはお勧めしません。 労力のわりに得られるものが少なすぎます。そして、前述のとおり、間違えてしまう可能性も高すぎます。

代わりに行うべきなのは、他のリソースに依存せず、自動無効化なしで無効化できるリソースを特定することです。 ただし、その場合、カスタムレプリケーションエージェントを使用する必要はありません。 Dispatcher 設定で、これらのリソースを自動無効化から除外するカスタムルールを作成するだけです。

先ほど説明したように、メインナビゲーションやティーザーは、依存関係のソースです。 ナビゲーションとティーザーを非同期で読み込むか、Apache の SSI スクリプトを使用して組み込むと、その依存関係を追跡する必要がなくなります。 コンポーネントの非同期読み込みについては、このドキュメントの後半の「Sling Dynamic Includes」で説明します。

ポップアップウィンドウや、ライトボックスに読み込まれるコンテンツについても同じことが言えます。 これらもまた、ナビゲーション(つまり「依存関係」)を持つことはほとんどなく、単一のリソースとして無効にすることができます。

Dispatcher を念頭に置いたコンポーネントの構築

実際の例での Dispatcher の仕組みの適用

前章では、Dispatcher の基本的な仕組み、全体的な動作、および制限事項について説明しました。

次に、これらの仕組みを、プロジェクトの要件に含まれる可能性の高いタイプのコンポーネントに適用します。 今回は、実際でもいずれ直面する問題を示すために、意図的にコンポーネントを選んでいます。 これから紹介する検討事項が、すべてのコンポーネントに必要なわけではありません。 しかし、そのようなコンポーネントを構築する必要性があるときに、その結果をよく理解していれば、対処法がわかります。

スプールコンポーネント(アンチ)パターン

レスポンシブ画像コンポーネント

相互に接続されたバイナリを持つコンポーネントの一般的なパターン(アンチパターン)を説明します。 「レスポンシブ画像」用のコンポーネント「respi」を作成します。 このコンポーネントは、表示される画像を、表示されるデバイスに適応できるようにする必要があります。 デスクトップやタブレットではフル解像度の画像を表示し、スマートフォンでは狭い範囲で切り抜いた小さなバージョン、または完全に異なるモチーフを表示します(レスポンシブの世界では「アートディレクション」と呼ばれます)。

アセットは AEM の DAM 領域にアップロードされ、レスポンシブ画像コンポーネント内でのみ参照されます。__

respi コンポーネントは、マークアップのレンダリングとバイナリ画像データの配信の両方を処理します。

ここでの実装方法は、多くのプロジェクトでよく見られる一般的なパターンであり、AEM のコアコンポーネントの 1 つもこのパターンに基づいています。 したがって、開発者としてこのパターンを採用する可能性は高いでしょう。 このパターンはカプセル化の点では優れていますが、Dispatcher に対応させるには多くの労力が必要です。 問題を軽減するいくつかの方法については、後ほど説明します。

ここで使用するパターンを「スプーラパターン」と呼びます。この問題は Communiqué 3 の初期の頃に遡り、リソース上で呼び出し、バイナリの生データを応答にストリーミングする「スプール」メソッドによります。

元の「スプール」という用語は、実際にはプリンターなどの低速なオフライン周辺機器の共有を指すので、この場合には適していません。 しかし、オンラインの世界ではこのように区別できることはほとんどないので、私たちはこの用語を気に入っています。 それぞれのパターンには区別できる名前を付けるべきでしょう。 これがパターンかアンチパターンかを決めるのはユーザー次第です。

実装

レスポンシブ画像コンポーネントの実装方法を次に示します。

このコンポーネントには 2 つの部分があり、最初の部分は画像の HTML マークアップをレンダリングし、2 番目の部分は参照元の画像のバイナリデータを「スプール」します。 これはレスポンシブデザインを備えた最新の web サイトなので、単純な <img src"…"> タグではなく、<picture/> タグの一連の画像をレンダリングします。 デバイスごとに 2 つの異なる画像を DAM にアップロードし、画像コンポーネントから参照します。

このコンポーネントには、3 つのレンダリングスクリプト(JSP、HTL、またはサーブレットとして実装)があり、それぞれ専用のセレクターで対応します。

  1. /respi.jsp:HTML マークアップをレンダリングするセレクターなし
  2. /respi.img.java:デスクトップバージョンをレンダリングする
  3. /respi.img.mobile.java:モバイルバージョンをレンダリングする。

このコンポーネントは、ホームページの parsys に配置されます。 CRX で作成される構造を次に示します。

CRX でのレスポンシブ画像のリソース構造

CRX でのレスポンシブ画像のリソース構造

コンポーネントのマークアップは、次のようにレンダリングされます。

  #GET /content/home.html

  <html>

  …

  <div class="responsive-image>

  <picture>
    <source src="/content/home/jcr:content/par/respi.img.mobile.jpg" …/>
    <source src="/content/home/jcr:content/par/respi.img.jpg …/>

    …

  </picture>
  </div>
  …

そして、うまくカプセル化されたコンポーネントが完成しました。

レスポンシブ画像コンポーネントの動作

次に、ユーザーが Dispatcher を介してページとアセットをリクエストします。 これにより、以下に示すように、Dispatcher ファイルシステムにファイルが格納されます。

カプセル化されたレスポンシブ画像コンポーネントのキャッシュされた構造

カプセル化されたレスポンシブ画像コンポーネントのキャッシュされた構造

ユーザーが 2 つの花の画像の新しいバージョンを DAM にアップロードし、アクティブ化するとします。 AEM は、以下の無効化リクエストを送信します

/content/dam/flower.jpg

および

/content/dam/flower-mobile.jpg

Dispatcher に送信します。 しかし、これらのリクエストに効果はありません。 コンテンツは、コンポーネントのサブ構造の下のファイルとしてキャッシュされています。 これらのファイルは古くなっていますが、リクエストに応じて引き続き配信されます。

構造の不一致によりコンテンツが古くなる

構造の不一致によりコンテンツが古くなる

このアプローチには別の注意事項があります。 同じ flower.jpg を複数のページで使用するとします。 次に、同じアセットが複数の URL またはファイルの下にキャッシュされます。

/content/home/products/jcr:content/par/respi.img.jpg

/content/home/offers/jcr:content/par/respi.img.jpg

/content/home/specials/jcr:content/par/respi.img.jpg

…

新しいページとキャッシュされていないページがリクエストされるたびに、そのアセットは異なる URL で AEM から取得されます。 Dispatcher のキャッシュ機能もブラウザーのキャッシュ機能もないため、配信を高速化することはできません。

スプーラパターンが有効な場所

シンプルな形式でもこのパターンが有用になる自然な例外が 1 つあります。バイナリが DAM ではなく、コンポーネント自体に保存されている場合です。 ただし、これが役立つのは web サイトで 1 回だけ使用した画像に対してのみで、DAM にアセット(Assets)を保存しない場合は、アセット管理が困難になることを意味します。 特定のアセットの使用ライセンスの有効期限が切れたと想像してみてください。 アセットを使用したコンポーネントを見つけるにはどうすればよいですか?

そうですね。 DAM の「M」は、「Digital Asset Management(デジタルアセット管理)」の「Management(管理)」を表します。 その機能は明かしたくないでしょう。

まとめ

AEM 開発者の視点から見ると、パターンは非常に洗練されていました。 しかし、Dispatcher を式に取り込むと、単純なアプローチでは不十分だと思うかもしれません。

今のところ、これがパターンなのかアンチパターンなのかを判断するかどうかは、皆さんに委ねます。 これまでに説明した問題をどのように軽減するかについて、すでに良いアイデアが思い浮かんでいる方もいるかもしれません。 いるかもしれません。 そうであれば、他のプロジェクトではこれらの問題をどのように解決したかを知りたいと思われることでしょう。

Dispatcher に関する一般的な問題の解決

概要

これを、よりキャッシュに優しい形でどのように実装できたかについて説明しましょう。 複数の方法があります。 最適な解決策を選べないこともあります。 既に実行中のプロジェクトに参加していて、「キャッシュの問題」を修正するだけの限られた予算しかなく、本格的なリファクタリングを行うには十分ではないかもしれません。 または、サンプルの画像コンポーネントよりも複雑な問題に直面するかもしれません。

以下の節では、原則と注意事項の概要について説明します。

繰り返しますが、これは実際の経験に基づいています。 これらのパターンはすでに実際の現場で確認されているものであり、単なる学術的な演習ではありません。 そのため、いくつかのアンチパターンも紹介し、他の人がすでに犯した間違いから学ぶ機会を提供しています。

キャッシュキラー

WARNING
これはアンチパターンです。 使用しないでください。 ください。

以前に、?ck=398547283745 のようなクエリパラメーターを見たことはありますか? これらはキャッシュキラー(「ck」)と呼ばれます。 クエリパラメーターを追加した場合、リソースはキャッシュされません。 また、パラメーターの値として乱数を追加した場合(「398547283745」など)、URL は一意になり、AEM システムと画面の間にある他のキャッシュもキャッシュできなくなります。 通常の中間にある疑いは、Dispatcher、CDN、またはブラウザーキャッシュの前にある「Vanish」キャッシュです。 繰り返しますが、使用は避けてください。 リソースは、できるだけ多く、できるだけ長くキャッシュされるようにすべきです。 キャッシュは味方です。 味方を消さないでください。

自動無効化

WARNING
これはアンチパターンです。 Experience Manager Assets などのデジタルアセットには使用しないでください。 Dispatcher のデフォルト設定(「.html」ファイルのみの自動無効化)を維持するようにしてください。

短期的には、Dispatcher の自動無効化設定に「.jpg」および「.png」を追加できます。 つまり、無効化が発生した場合は常に、すべての「.jpg」、「.png」および「.html」を再レンダリングする必要があります。

このパターンは、ビジネスオーナーが自分の変更がライブサイトにすぐに反映されないと不満を言った場合に、非常に簡単に実装できます。 しかし、これは、より高度なソリューションを考え出すための時間稼ぎに過ぎません。

パフォーマンスへの大きな影響を把握しておいてください。 ニュースポータルなど、頻繁な変更を伴う高負荷 web サイトの場合、web サイトの速度が大幅に低下し、安定性に影響を与える可能性もあります。

URL フィンガープリント

URL フィンガープリントは、キャッシュキラーのように見えます。 実際はそうではありません。 乱数ではなく、リソースの内容を特徴付ける値です。 これは、リソースのコンテンツのハッシュにしたり、さらに単純に、リソースがアップロード、編集または更新されたときのタイムスタンプにしたりできます。

UNIX タイムスタンプは、実際の実装には十分です。 わかりやすくするために、このチュートリアルでは、より読みやすい形式 2018 31.12 23:59 or fp-2018-31-12-23-59 を使用しています。

フィンガープリントをクエリパラメーターとして使用することはできません。クエリパラメーターを持つ URL はキャッシュできないからです。 フィンガープリントには、セレクターまたはサフィックスを使用できます。

ファイル /content/dam/flower.jpgの日付はjcr:lastModifiedで、2018年12月31日23:59とします。 フィンガープリントを含む URL は /content/home/jcr:content/par/respi.fp-2018-31-12-23-59.jpg です。

参照元のリソース(flower.jpg)ファイルが変更されない限り、URL は安定しています。 したがって、無期限のキャッシュが可能なので、これはキャッシュキラーではありません。

この URL はレスポンシブ画像コンポーネントで作成および提供する必要があることに注意してください。 標準の AEM 機能ではありません。

これが基本的な概念です。 ただし、見落としがちな詳細がいくつかあります。

この例では、コンポーネントは23:59にレンダリングされ、キャッシュされました。 00:00に画像が変更されました。 コンポーネントはマークアップに新しいフィンガープリント URL を生成することが考えられます。__

すべきだと思うかもしれませんが… 画像のバイナリのみが変更され、インクルーディングページがタッチされていないため、HTML マークアップの再レンダリングは必要ありません。 したがって、Dispatcher はページに古いフィンガープリント、つまり画像の古いバージョンを提供します。

画像コンポーネントが参照画像より新しく、新しいフィンガープリントがレンダリングされていない。

画像コンポーネントが参照画像より新しく、新しいフィンガープリントがレンダリングされていない。

もしホームページ(またはそのサイトの他のページ)を再アクティブ化すると、statfile が更新され、Dispatcher は home.html を古いと見なし、画像コンポーネントの新しいフィンガープリントを使用して再レンダリングするでしょう。

しかし、ホームページは公開していませんよね。 そして、なぜ変更しなかったページをアクティブ化する必要があるのでしょうか。 また、ページをアクティブ化するための十分な権限がない場合や、承認ワークフローが長くて時間がかかりすぎるために、短時間ではそれを実行できないこともあります。 どうすればいいのでしょうか。

怠惰な管理者のツール - statfile レベルを下げる

WARNING
これはアンチパターンです。 短期間だけ使用して時間を稼ぎ、その間により高度なソリューションを考え出してください。

手間を省いた管理方法では、通常、「自動無効化を JPG に設定し、statfile-level を 0 に設定します。これは常に、あらゆる種類のキャッシュの問題に役立ちます」。 このようなアドバイスはテクニカルフォーラムにあり、無効化の問題に役立ちます。

ここまで、statfile-level については説明していませんでした。 基本的に、自動無効化は同じサブツリー内のファイルに対してのみ機能します。 ただし、ページとアセットは通常同じサブツリーに存在しないという問題が発生します。 ページは /content/mysite の下の任意の場所に、アセットは /content/dam の下にあります。

「statfile レベル」は、サブツリーのルートノードがどの深さにあるかを定義します。 上述の例では、レベルは「2」です(1=/content、2=/mysite/dam)

基本的に、statfile レベルを 0 に「下げる」という考え方は、/content ツリー全体を、ページとアセットを同じ自動無効化ドメイン内に存在させるための唯一のサブツリーとして定義することです。 つまり、docroot(「/」)のレベルには、大きなツリーが 1 つだけ存在することになります。 しかし、これにより、完全に無関係なサイトでも、何かが公開されるたびに、サーバー上のすべてのサイトが自動無効化されます。 キャッシュ全体のヒット率が大幅に低下するので、長期的に見れば悪いアイデアですが、信じてください。 AEM サーバーにキャッシュなしでも動作するだけの十分な処理能力があることを期待するしかありません。

より深い statfile レベルのメリットについては、後ほど理解していただけます。

カスタム無効化エージェントの実装

ともあれ、「.jpg」または「.png」が変更されたときに、HTML ページを無効にして新しい URL で再レンダリングできるように、Dispatcher に何らかの方法で指示する必要があります。

プロジェクトで見てきたのは、例えば、パブリッシュシステム上の特別なレプリケーションエージェントです。このエージェントは、サイトの画像が公開されるたびに、そのサイトの無効化要求を送信します。

ここで、命名規則を使用してアセットのパスからサイトのパスを引き出すことができれば、大いに役立ちます。

一般的には、サイトとアセットのパスを次のように一致させるのが適切です。

例

/content/dam/site-a
/content/dam/site-b

/content/site-a
/content/site-b

このようにして、/content/dam/site-a に変更があったときに、カスタムの Dispatcher フラッシュエージェントは /content/site-a に無効化リクエストを容易に送信することができます。

実際には、同じサイト内の同じ「サブツリー」にある限り、Dispatcher にどちらのパスを無効にするよう指示するかは問題ではありません。 実際のリソースパスを使用する必要もありません。 「仮想」にすることもできます。

GET /dispatcher-invalidate
Invalidate-path /content/mysite/dummy

  1. DAM 内のファイルが変更されると、パブリッシュシステム上のリスナーがトリガーされます。

  2. リスナーが無効化要求を Dispatcher に送信します。 自動無効化のため、サイトのホームページの下、つまりサイトの statfile レベルでない限り、自動無効化で送信するパスは関係ありません。

  3. statfile が更新されます。

  4. 次回、ホームページがリクエストされると、再レンダリングされます。 新しいフィンガープリント(日付)は、画像の lastModified プロパティから追加のセレクターとして取得されます。

  5. これにより、新しい画像への参照が暗黙的に作成されます。

  6. 画像が実際にリクエストされた場合は、新しいレンディションが作成され、Dispatcher に保存されます。

クリーンアップの必要性

やっと、。 ここまで完了しました。 ですが、

まだ終わりではありません。

以下のパス

/content/mysite/home/jcr:content/par/respi.img.fp-2018-31-12-23-59.jpg

は、無効化されたいずれのリソースにも関連していません。 思い出してください。 「ダミー」のリソースのみを無効化し、「ホーム」を無効と判断する自動無効化を利用しました。 画像自体が物理的に削除されることはないかもしれません。__ したがって、キャッシュはどんどん増えていきます。 画像が変更されてアクティベートされると、Dispatcher のファイルシステム上で新しいファイル名が付けられます。

キャッシュされたファイルを物理的に削除せず、無期限に保持することには、次の 3 つの問題があります。

  1. 明らかに、ストレージ容量を無駄にしています。 ストレージはここ数年でどんどん安くなっています。 しかし、画像の解像度やファイルサイズも、鮮明な画像を求める Retina のようなディスプレイの登場によって、ここ数年で増えています。

  2. ハードドライブが安くなっても、「ストレージ」は安くなっていないのかもしれません。 (安い)ベアメタル HDD ストレージを持たず、データセンターのプロバイダが提供する NAS 上の仮想ストレージをレンタルする傾向が見られます。 この種のストレージは、より信頼性が高く拡張性が高いものの、少しコストが高くなります。 古いガベージを保存して浪費したくないと思うでしょう。 これはプライマリストレージに限りません。バックアップについても考えてみましょう。 標準のバックアップソリューションを使用している場合は、キャッシュディレクトリを除外できない可能性があります。 結局、ガベージデータもバックアップしていることになります。

  3. さらに悪いことに、特定の画像の使用ライセンスを、必要に応じた限られた期間しか購入していないかもしれません。 ライセンスの有効期限が切れた後も引き続き画像を保存すると、著作権侵害と見なされる可能性があります。 Web ページで画像を使用しなくなったとしても、Google には依然としてその画像が見つけられます。

最終的には、このようなゴミをコントロールできるよう、例えば 1 週間より古いすべてのファイルをクリーンアップするハウスキーピング用の cron ジョブを用意することになるでしょう。

サービス拒否攻撃に URL フィンガープリントを悪用すること

しかし、ちょっと待ってください。この解決法には別の欠陥があります。

ここでは、セレクターをパラメーターとしてある種乱用しています。fp-2018-31-12-23-59 は、いわゆる「cache-killer」として動的に生成されます。 しかし、退屈した子どもや、暴走した検索エンジンのクローラーがページのリクエストを開始することがあるかもしれません。

/content/mysite/home/jcr:content/par/img.fp-0000-00-00-00-00.jpg
/content/mysite/home/jcr:content/par/img.fp-0000-00-00-00-01.jpg
/content/mysite/home/jcr:content/par/img.fp-0000-00-00-00-02.jpg

…

各リクエストは Dispatcher をバイパスし、パブリッシュインスタンスで読み込みが発生します。 そのうえ、それに応じたファイルを Dispatcher で作成します。

だから…単純なキャッシュキラーとして指紋を使用する代わりに、画像のjcr:lastModified日付を確認し、予想される日付ではない場合は404を返す必要があります。 パブリッシュシステムでは、当初防ごうとしていた、時間がかかり、CPU サイクルを多く費やすという問題が発生します。

高頻度リリースでの URL フィンガープリントの注意事項

フィンガープリントスキーマは、DAM からのアセットに対してのみならず、JS ファイルと CSS ファイル、および関連リソースに対しても使用できます。

バージョン管理された Clientlibs は、この方法を使用するモジュールです。

しかし、URL をコンテンツに結び付ける URL フィンガープリントでは、別の問題に直面する可能性があります。 URL(変更日の更新)を変更せずにコンテンツを変更することはできません。 そもそもフィンガープリントは、まさにそのために設計されています。 ただし、新しい CSS および JS ファイルを使用した新しいリリースを展開し、新しいフィンガープリントを使用した新しい URL をロールアウトするケースを考えてみてください。 すべての HTML ページには、古いフィンガープリント付き URL への参照が残っています。 したがって、新しいリリースを確実に動作させるには、すべての HTML ページを一度に無効化し、新しくフィンガープリントされたファイルを参照して強制的に再レンダリングさせる必要があります。 同じライブラリに依存する複数のサイトがある場合は、かなり多くの再レンダリングある可能性があり、そして、ここでは、statfiles を使用することはできません。 ロールアウト後にパブリッシュシステムの読み込みピークを確認するよう備えてください。 キャッシュウォーミングを使用したブルー/グリーンのデプロイメントや、Dispatcher の前にある TTL ベースのキャッシュを検討してもよいでしょう。可能性は無限です。

短い改行

非常に多くの詳細を考慮する必要があることおがわかりました。 そして、それは容易に理解、テスト、デバッグすることができません。 すべては、優れたソリューションに見えるために必要なものです。 とはいえ、これは優れた設計ですが、AEM だけの観点から見た場合に限られます。 Dispatcher と一緒に使用すると、厄介になります。

それでも、画像が異なるページで複数回使用される場合、その画像はそれぞれのページ配下にキャッシュされるため、ある基本的な問題は解決されません。 そこには、キャッシュのシナジーはあまり見られません。

一般的に、URL フィンガープリンティングは持っていると便利なツールですが、既存のいくつかの問題を解決する一方で新たな問題を引き起こす可能性があるため、注意して適用する必要があります。

これを説明するために章が長くなりましたが、。 私たちは、このようなパターンを何度も経験してきたため、長所と短所を含めた全体像を伝える必要があると考えました。 URL フィンガープリントはスプーラーパターンに固有の問題のいくつかを解決しますが、実装には多くの手間がかかるので、より簡単な他の解決策も考慮する必要があります。 URL が提供されたリソースパスに基づいているか、および中間コンポーネントの有無を常に確認することをお勧めします。 次の章では、この点について説明します。

実行時の依存関係の解決

実行時の依存関係の解決は、1 つのプロジェクトで検討されてきた概念です。 しかし、よく検討すると非常に複雑になるため、実装しないことにしました。

基本的な考え方を次に示します。

Dispatcher は、リソースの依存関係を認識しません。 セマンティクスをほとんど使用しない、多くの単一ファイルに過ぎません。

また、AEM は依存関係もほとんど把握していません。 適切なセマンティクスまたは「依存関係トラッカー」が不足しています。

AEM は参照の一部を認識しています。 参照先のページやアセットを削除または移動しようとした場合に、この知識を使用して警告が表示されます。 これは、アセットを削除する際に内部検索にクエリを実行することで行います。 コンテンツの参照には、非常に特殊な形式があります。 これらは、「/content」で始まるパス式です。 したがって、フルテキストのインデックスを簡単に作成し、必要に応じてクエリを実行できます。

この例では、パブリッシュシステム上にカスタマイズされたレプリケーションエージェントが必要です。このエージェントは、パスが変更されたときに特定のパスを検索するトリガーを設定します。

例えば、次にようになります。

/content/dam/flower.jpg

公開環境で変更されています。 エージェントは「/content/dam/flower.jpg」の検索を実行し、これらの画像を参照するすべてのページを検索するでしょう。

その後、Dispatcher に多数の無効化リクエストを送信できます。 アセットを含む各ページに対して 1 つ発行する可能性があります。

理論上は、それで機能するはずです。 対象となるのは第 1 レベルの依存関係に対してのみです。 例えば、ページで使用されるエクスペリエンスフラグメントに画像を使用する場合など、複数レベルの依存関係には、このスキームは適していません。 実際、私たちもアプローチは複雑すぎると考えていますし、実行時に問題が発生する可能性もあります。 一般に、イベントハンドラーで費用のかかる計算を行わないことが最善のアドバイスです。 特に検索の処理コストが高くなる可能性があります。

まとめ

スプーラーパターンについて、実装で使用するタイミングと使用しないタイミングを判断できるよう、十分に説明できていることを願っています。

Dispatcher に関する問題の回避

リソースベースの URL

依存関係の問題を解決する、さらに洗練された方法は、依存関係を全く持たないことです。 前の例で行ったように、あるリソースを使用して別のリソースをプロキシする場合に発生する、人工的な依存関係は回避してください。 リソースは、できるだけ頻繁に「孤立した」エンティティとして捉えるようにします。

この例は簡単に解決できます。

コンポーネントではなく、画像にバインドされたサーブレットで画像をスプールします。

コンポーネントではなく、画像にバインドされたサーブレットで画像をスプールします。

アセットの元のリソースパスを使用して、データをレンダリングします。 元の画像をそのままレンダリングする必要がある場合は、アセットのレンダリングに AEM のデフォルトレンダラーを使用するだけです。

特定のコンポーネントに対して特別な処理が必要な場合は、そのパスとセレクターに専用のサーブレットを登録し、そのコンポーネントに代わって変換処理を実行します。 ここでは、その例として「.respi」を使用しました。 セレクターを例に挙げました。 グローバル URL スペース(/content/dam など)に書き込み、名前の競合を避けるために適切な命名規則を使用します。

ところで、コードの一貫性に関する問題は一切発生していません。 このサーブレットは、コンポーネントの Sling モデルと同じ Java パッケージで定義できます。

グローバルスペースでは、次のような追加のセレクターを使用することもできます。

/content/dam/flower.respi.thumbnail.jpg

非常に簡単ですよね。 では、なぜ人々はスプーラーのような複雑なパターンを思いつくのでしょうか。

内部コンテンツ参照を避けるという問題を解決できます。これは、外部コンポーネントが内部リソースのレンダリングにほとんど値や情報を追加せず、単一リソースの表現を制御する静的セレクターのセットで容易にエンコードできるからです。

ただし、リソースベースの URL を使用して容易に解決できないケースのクラスが 1 つあります。 このタイプのケースを「パラメータインジェクティングコンポーネント」と呼び、次の章で説明します。

パラメータインジェクティングコンポーネント

概要

最後の章のスプーラーは、リソースの単純なラッパーに過ぎませんでした。 問題を解決するよりも、問題を引き起こしました。

単純なセレクターを使用してそのラッピングを容易に置き換え、そのようなリクエストを処理するための対応するサーブレットを追加することができます。

しかし、「respi」コンポーネントが単なるプロキシ以外のものである場合はどうなるのでしょう。 コンポーネントが真にコンポーネントのレンダリングに関与している場合はどうなりますか?

少し変わったゲームのコンポーネントである「respi」コンポーネントについて、小さな拡張を紹介します。 また、新しい課題に取り組み、それらがどこに不足しているかを示すために、まずいくつかの素朴な解決策を紹介します。

Respi2 コンポーネント

respi2 コンポーネントは、respi コンポーネントと同様に、レスポンシブ画像を表示するコンポーネントです。 しかし、少しだけアドオンがあります。

CRX 構造:respi2 コンポーネントによる配信への品質プロパティの追加

CRX 構造:respi2 コンポーネントによる配信への品質プロパティの追加

画像は JPEG で、JPEG は圧縮できます。 JPEG 画像を圧縮する場合は、画質とファイルサイズをトレードオフすることになります。 圧縮は、「1」から「100」までの数値の「品質」パラメーターとして定義されます。 「1」は「小さいが、低品質」を意味し、「100」は「優れた品質で、大きいファイル」を意味します。 では、どれが完璧な数値なのでしょうか?

すべての IT と同様に、回答は「状況次第」です。

ここでは、モチーフに依存します。 文字、建物の写真、イラスト、スケッチ、商品箱の写真(シャープな輪郭で文字が書かれている)など、コントラストの高いエッジがあるモチーフは、通常、そのカテゴリに含まれます。 風景やポートレートのような柔らかい色とコントラストの遷移を持つモチーフは、目に見える品質を損なうことなく、もう少し圧縮することができます。 自然写真は通常そのカテゴリに入ります。

また、画像の使用場所に応じて、別のパラメーターを使用することもできます。 ティーザーの小さなサムネイルは、画面全体のヒーローバナーで使用される同じ画像よりも、強い圧縮に耐えられます。 つまり、画質パラメーターは画像に固有ではなく、画像とコンテキストに固有のものです。 そして、作成者の好みに合わせます。

要するに、すべての写真に対する完璧な設定はありません。 万能な設定はありません。 作成者が決めたものが最適な設定になります。 開発者は、帯域幅を犠牲にしない範囲で品質に満足できるまで、コンポーネントのプロパティとしての「quality」パラメーターを微調整します。

現在、DAM にはバイナリファイルがあり、品質プロパティを提供するコンポーネントがあります。 URL はどのような形になるべきでしょうか? スプール処理を担当するコンポーネントはどれですか?

ナイーブなアプローチ 1:プロパティをクエリパラメーターとして渡す

WARNING
これはアンチパターンです。 決して使用しないで。

前の章では、コンポーネントによってレンダリングされる画像 URL は次のようになっていました。

/content/dam/flower.respi.jpg

欠けているのは品質の値だけです。 このコンポーネントは、作成者が入力したプロパティを把握します。このプロパティは、マークアップがレンダリングされる際に、flower.respi2.jpg?quality=60 のようにクエリパラメーターとして画像レンダリングサーブレットに容易に渡すことができます。

  <div class="respi2">
  <picture>
    <source src="/content/dam/flower.respi2.jpg?quality=60" …/>
    …
  </picture>
  </div>
  …

これはあまりいい考えではありません。 思い出してください。 クエリパラメーターを含むリクエストはキャッシュできません。

安易なアプローチ 2:セレクターとして追加情報を渡す

WARNING
これはアンチパターンになるかもしれません。 慎重に使用してください。

コンポーネントのプロパティをセレクターとして渡す

コンポーネントのプロパティをセレクターとして渡す

これは最後の URL をわずかに変更したバリエーションです。 今回はセレクターを使用してプロパティをサーブレットに渡し、その結果をキャッシュできるようにします。

/content/dam/flower.respi.q-60.jpg

この方が良いのですが、最後の章の好ましくないスクリプトキッドが、このようなパターンを探し出しているのを覚えていますか? おそらく、値をループしながら、どれくらいまでできるか試してみるでしょう。

  /content/dam/flower.respi.q-60.jpg
  /content/dam/flower.respi.q-61.jpg
  /content/dam/flower.respi.q-62.jpg
  /content/dam/flower.respi.q-63.jpg
  …

これも再びキャッシュをバイパスし、パブリッシュシステムに負荷を発生させます。 あまり良い考えではないかもしれません。 これは、小さなパラメーターのサブセットのみをフィルタリングすることで軽減できます。 q-20, q-40, q-60, q-80, q-100 のみを許可します。

セレクター使用時の無効なリクエストのフィルタリング

セレクターの数を減らしたのは良いスタートでした。 経験則として、有効なパラメーターの数は必ず絶対最小値に制限する必要があります。 これを賢く行えば、基盤となる AEM システムに関する深い知識がなくても、静的なフィルターのセットを使用して AEM の外部で web アプリケーションファイアウォールを利用してシステムを保護することもできます。

Allow: /content/dam/(-\_/a-z0-9)+/(-\_a-z0-9)+
       \.respi\.q-(20|40|60|80|100)\.jpg

Web アプリケーションファイアウォールがない場合は、Dispatcher または AEM 自体でフィルタリングする必要があります。 AEM で実行する場合は、次を確認してください。

  1. CRX へのアクセスを過度に行ってメモリや時間を無駄にすることなく、フィルターは非常に効率的に実装されています。

  2. フィルターは「404 – Not found」というエラーメッセージで応答します。

再び最後のポイントを強調させてください。 HTTP 会話は次のようになります。

  GET /content/dam/flower.respi.q-41.jpg

  Response: 404 – Not found
  << empty response body >>

また、無効なパラメーターをフィルタリングしたが、無効なパラメーターが使用されると有効なフォールバックレンダリングを返した実装も確認しました。 例えば、20~100 のパラメーターのみを使用できるとします。 間の値は、有効な値にマッピングされます。 すると、

q-41, q-42, q-43, …

は常に q-40 と同じ画像を返します。

  GET /content/dam/flower.respi.q-41.jpg

  Response: 200 – OK
  << flower.jpg with quality = 40 >>

そのアプローチは全く役に立ちません。 これらのリクエストは、実際には有効なリクエストです。 処理能力を消費し、Dispatcher のキャッシュディレクトリの領域を占有します。

よりよいのは 301 – Moved permanently を返すことです。

  GET /content/dam/flower.respi.q-41.jpg

  Response: 301 – Moved permanently
  Location: /content/dam/flower.respi.q-40.jpg

ここでは、AEM がブラウザーに対して指示を出しています。 「q-41 は持っていませんがが、 q-40 については聞いてください」と伝えています

これにより、会話にリクエスト応答ループが追加され、オーバーヘッドが少し増加しますが、q-41 でフル処理を行うよりも安価です。 また、q-40 の下に既にキャッシュされているファイルを活用できます。 ただし、302 応答は Dispatcher ではキャッシュされず、ここで説明しているのは AEM 側で実行されるロジックです。 何度も繰り返し実行されるロジックです。 よって、スリムに、かつ高速化するのが適しています。

私たちは、404 レスポンスが一番気に入っています。 何が起こっているかが明確です。 また、ログファイルを分析する際に、web サイト上でエラーを検出するのに役立ちます。 301 は故意に発生させることもできますが、404 は常に分析し、排除する必要があります。

セキュリティ - 実行

リクエスト数のフィルタリング

最適なフィルタリング場所

最後の章の終わりに、既知のセレクターに対して受信トラフィックをフィルタリングする必要性を指摘しました。 すると、実際にどこでリクエストをフィルタリングすべきかという質問が残ります。

これは、場合によって違います。 早ければ早いほど良いでしょう。

Web アプリケーションファイアウォール

Web セキュリティ向けに設計された web アプリケーションファイアウォールアプライアンスまたは「WAF」をお持ちの場合は、これらの機能を絶対に活用する必要があります。 しかし、コンテンツアプリケーションに関する限定的な知識しか持っていない人々が WAF を運用し、有効なリクエストを除外してしまったり、あまりにも多くの有害なリクエストを通過させてしまうことがあります。 WAF の運用者が異なるシフトやリリーススケジュールを持つ別の部門に所属していて、コミュニケーションが直属のチームメイトほど緊密ではなく、変更が常に時間内に反映されるとは限らない、ということもあります。こうなると、最終的には開発とコンテンツベロシティが低下してしまいます。

最終的には、いくつかの一般的なルールや、もっと厳しくできるのではないかと感じるブロックリストを抱えることになるかもしれません。

Dispatcher と公開のフィルタリング

次の手順では、Apache コアや Dispatcher に URL フィルターのルールを追加します。

ここでは、URL へのアクセスのみが可能です。 パターンベースのフィルターに制限されます。 コンテンツベースのフィルタリング(正しいタイムスタンプが付いたファイルのみを許可するなど)をより詳細に設定する必要がある場合や、一部のフィルタリングをオーサー環境側で制御したい場合は、カスタムのサーブレットフィルターを作成することになります。

モニタリングとデバッグ

実際には、各レベルにセキュリティが設定されます。 しかし、リクエストがどのレベルでフィルターされているかを確認する手段は確保してください。 Publish システム、Dispatcher、WAF 上のログファイルに直接アクセスできるようにしておき、チェーン内のどのフィルターがリクエストをブロックしているかを特定できるようにします。

セレクターとセレクターの増殖

前の章で「セレクターパラメーター」を使用するアプローチは、すばやく簡単で、新しいコンポーネントの開発時間を短縮できますが、制限があります。

「品質」プロパティの設定は単純な例にすぎません。 ただし例えば、サーブレットがより柔軟に動作できるように、「幅」を指定するパラメーターも受け取る必要があるとします。

セレクター値の数を減らすことで、有効な URL の数を減らすことができます。 幅についても同じようにできます。

品質 = q-20、q-40、q-60、q-80、q-100

幅 = w-100、w-200、w-400、w-800、w-1000、w-1200

しかし、すべての組み合わせが有効な URL になりました。

/content/dam/flower.respi.q-40.w-200.jpg
/content/dam/flower.respi.q-60.w-400.jpg
…

これで、1 つのリソースに対して 5x6=30 の有効な URL が得られます。 プロパティを追加するたびに、複雑さが増します。 また、適切な値に減らすことができないプロパティがある可能性があります。

このように、このアプローチにも限界はあります。

誤りによる API の公開

何が起こっているのでしょうか? 注意深く見ると、静的にレンダリングされた web サイトから高度に動的な web サイトへと、徐々に変化していくことがわかります。 そして、実際には作成者のみが使用することを意図していた画像レンダリング API が、お客様のブラウザーに誤って表示されてしまうことがあります。

画像の品質とサイズの設定は、作成者がページを編集して行う必要があります。 サーブレットによって同じ機能が公開されていることは、機能として利点と捉えることもできますが、サービス拒否攻撃のベクトルと見なされる可能性もあります。 実際の内容は、状況に応じて異なります。 Web サイトがビジネスにとってどの程度重要ですか? サーバーの負荷はどれくらいですか? ヘッドルームはどのくらい残っていますか? 実装に必要な予算はどれくらいですか? これらの要因のバランスを取らなければなりません。 賛否両論を認識する必要があります。

スプーラーパターン - 再検討と再評価

スプーラーが API を公開しない仕組み

前の章では、スプーラーパターンの評価を少し下げてしまいました。 それを修復する時です。

スプーラーパターンは、前の章で説明した API を公開する際の問題を防ぎます。 プロパティは保存され、コンポーネント内にカプセル化されます。 これらのプロパティにアクセスするために必要なのは、コンポーネントへのパスだけです。 マークアップとバイナリレンダリングの間でパラメーターを送信する際に、URL を手段として使用する必要はありません。

  1. コンポーネントがプライマリリクエストループ内でリクエストされると、クライアントはその HTML マークアップをレンダリングします。

  2. コンポーネントのパスは、マークアップからコンポーネントへの逆参照として機能します。

  3. ブラウザーは、この逆参照を使用してバイナリをリクエストします。

  4. リクエストがコンポーネントに到達すると、バイナリデータのサイズ変更、圧縮、スプールを行うためのすべてのプロパティが手に入ります。

  5. 画像は、コンポーネントを通じてクライアントブラウザーに送信されます。

スプーラーパターンは、結局それほど悪いものではないため、広く使われています。 キャッシュの無効化さえ面倒でなければ…

逆スプーラー - 両方のいいとこ取り?

ここで、。 なぜ両方のいいとこ取りをできないのかという質問が生じます。 スプーラーパターンの優れたカプセル化と、リソースベースの URL の優れたキャッシュプロパティでしょうか。

実際のプロジェクトでは見たことがないと認めざるを得ません。 ただし、とにかくここで、独自の解決策の出発点として、少し思考実験をしてみましょう。

このパターンを​ 反転スプーラー ​と呼びます。Inverted Spoolerは、すべての便利なキャッシュ無効化プロパティを持つために、画像リソースに基づいている必要があります。

ただし、パラメーターを公開しないようにする必要があります。 すべてのプロパティは、コンポーネント内でカプセル化する必要があります。 ただし、コンポーネントのパスを、プロパティへの不透明な参照として公開できます。

その結果、URL は次のような形式になります。

/content/dam/flower.respi3.content-mysite-home-jcrcontent-par-respi.jpg

/content/dam/flower は、画像のリソースへのパスです。

.respi3 は、画像を配信する正しいサーブレットを選択するためのセレクターです

.content-mysite-home-jcrcontent-par-respi は追加のセレクターです。 画像変換に必要なプロパティを格納するコンポーネントへのパスをエンコードします。 セレクターで使用できる文字の種類は、パスの場合よりも限定されています。 ここでのエンコーディングスキームは、単なる例に過ぎません。 「/」を「-」に置き換えます。 パス自体に「-」を含めることも考慮されません。 実際の例では、より高度なエンコーディングスキームを推奨します。 Base64 は問題ありません。 ただし、デバッグが少し難しくなります。

.jpg はファイルのサフィックスです

まとめ

スプーラーの説明は、想定よりも長くなり、複雑になってしまいました。 申し訳ありません。 しかし、Dispatcher の世界で何がうまく機能し、何がうまく機能しないのかについての直感を養えるように、良い面と悪い面の両方を数多く取り上げて説明することが必要だと考えました。

Statfile と Statfile-Level

基本知識

はじめに

statfile については前に簡単に説明しました。 自動無効化に関連しています。

自動無効化が設定されている Dispatcher のファイルシステム内のすべてのキャッシュファイルは、最終更新日が statfile's 最終更新日より古い場合、無効とみなされます。

NOTE
最終変更日とは、キャッシュされたファイルがクライアントのブラウザーから要求され、最終的にファイルシステムに作成された日付です。 リソースの jcr:lastModified の日付ではありません。

statfile の最終変更日(.stat)は、AEM からの無効化リクエストが Dispatcher で受け取られた日付です。

複数の Dispatcher がある場合は、異常な結果が生じる可能性があります。 お使いのブラウザーには、より新しいバージョンの Dispatcher がインストールされている場合があります(複数の Dispatcher がある場合)。 または、Dispatcher は、他の Dispatcher によって発行されたブラウザーのバージョンが古いと考え、不必要に新しいコピーを送信する可能性があります。 これらの効果は、パフォーマンスや機能要件に大きな影響を与えません。 また、ブラウザーが最新バージョンの場合は、時間の経過と共にレベルアウトされます。 ただし、ブラウザーのキャッシュ動作を最適化およびデバッグする際には、少し混乱を招くことがあります。 その点に留意してください。

/statfileslevel を使用した無効化ドメインの設定

自動無効化と statfile について紹介した際、変更が行われると、すべての​ファイルは無効とみなされ、いずれにしてもすべてのファイルは相互依存していると説明しましたが、。

正確には異なります。 通常、共通のメインナビゲーションルートを共有するすべてのファイルは相互に依存します。 しかし、1 つの AEM インスタンスは、多数の 独立した web サイトをホストできます。 共通のナビゲーションは共有しません(実際には、何も共有しません)。

サイト A に変更があるので、サイト B を無効にするのは無駄ではないでしょうか。 それはそうですし、。 必ずしもそのようである必要はありません。

Dispatcher は、サイトを相互に分離するシンプルな方法(statfiles-level)を提供します。

これは、ファイルシステムのどのレベルから、2 つのサブツリーが「独立」と見なされるかを定義する数値です。

statfileslevel が 0 のデフォルトのケースを見てみましょう。

/statfileslevel "0":.stat を docroot に作成します。 無効化ドメインは、すべてのサイトを含むインストール全体に広がります

/statfileslevel "0":​.stat ファイルが docroot に作成されます。 無効化ドメインは、すべてのサイトを含むインストール全体にわたります。

どのファイルが無効化されても、Dispatcher の docroot の最上部にある .stat ファイルは常に更新されます。 したがって、/content/site-b/home を無効にすると、/content/site-a にあるすべてのファイルも、docroot の .stat ファイルより古くなるので無効になります。 site-b を無効にするときに、これが必要でないことは明らかです。

この例では、statfileslevel を 1 に設定したほうがいいでしょう。

これで、/content/site-b/home や /content/site-b より下のその他のリソースを公開し、無効化すると、.stat ファイルは /content/site-b/ に作成されます。

/content/site-a/ より下のコンテンツは影響を受けません。 このコンテンツは、/content/site-a/ にある .stat ファイルと比較されます。 2 つの異なる無効化ドメインを作成しました。

statfilelevel を「1」に設定すると異なる無効化ドメインが作成される

statfilelevel を「1」に設定すると異なる無効化ドメインが作成される

大規模なインストールは、通常、もう少し複雑で深い構造になっています。 一般的なスキームは、ブランド、国、言語ごとにサイトを構築することです。 その場合は、statfilelevel をさらに高く設定できます。 1​はブランドごとに無効化ドメインを作成し、2​は国ごと、3​は言語ごとになるでしょう。

均質なサイト構造の必要性

statfileslevel は、設定内のすべてのサイトに等しく適用されます。 したがって、すべてのサイトが同じ構造に従い、同じレベルで開始する必要があります。

ポートフォリオに、いくつかの小さな市場でのみ販売されているブランドと、世界中で販売されているブランドがあるとします。 小さな市場は 1 つの現地の言語しか使われないのに対して、グローバル市場では、複数の言語が話される国もあります。

  /content/tiny-local-brand/finland/home
  /content/tiny-local-brand/finland/products
  /content/tiny-local-brand/finland/about
                              ^
                          /statfileslevel "2"
  …

  /content/tiny-local-brand/norway
  …

  /content/shiny-global-brand/canada/en
  /content/shiny-global-brand/canada/fr
  /content/shiny-global-brand/switzerland/fr
  /content/shiny-global-brand/switzerland/de
  /content/shiny-global-brand/switzerland/it
                                          ^
                                /statfileslevel "3"
  ..

前者の場合は​ 2 ​の statfileslevel が必要で、後者の場合は​ 3 ​が必要となるでしょう。

理想的な状況ではありません。 3​に設定すると、サブブランチ /home、/products および /about 間の小さなサイト内では自動無効化が機能しません。

2​に設定すると、大きなサイトで /canada/en と /canada/fr は依存関係にあると宣言することになりますが、それらが依存関係にない可能性があります。 したがって、/en を無効化するたびに /fr も無効化されます。 これにより、キャッシュのヒット率が若干低下しますが、キャッシュされた古いコンテンツを配信するよりも適しています。

もちろん、すべてのサイトのルートを同じ深さにするのが最善のソリューションです。

/content/tiny-local-brand/finland/fi/home
/content/tiny-local-brand/finland/fi/products
/content/tiny-local-brand/finland/fi/about
…
/content/tiny-local-brand/norway/no/home
                                 ^
                        /statfileslevel "3"

サイト間リンク

それでは、どちらが正しいレベルでしょうか。 これは、サイト間の依存関係の数によって異なります。 ページをレンダリングするために解決するインクルージョンは、「ハード依存関係」と見なされます。 このガイドの冒頭で​ ティーザー ​コンポーネントを紹介したときに、そのような​ インクルージョン ​について説明しました。

ハイパーリンク​は、よりソフトな依存関係の形式です。 1 つの web サイト内にハイパーリンクが存在する可能性が非常に高く、複数の web サイト間にリンクが存在する可能性も低くはありません。 単純なハイパーリンクは、通常、Web サイト間に依存関係を作成しません。 サイトから Facebook に設定した外部リンクを考えてみましょう。Facebook 上で何か変更があった場合にページをレンダリングする必要はありません。逆の場合も同様です。

依存関係は、リンクされたリソース(ナビゲーションタイトルなど)からコンテンツを読み取るときに発生します。 外部リンクの場合と同様に、ローカルに入力したナビゲーションタイトルだけに依存し、ターゲットページから引き出さないようにすれば、このような依存関係を回避できます。

予期しない依存関係

しかし、設定の一部では、(おそらく独立した)サイトが集まる場合もあります。 あるプロジェクトで実際に遭遇したシナリオを見てみましょう。

お客様のサイト構造は、前章で示したものと同様でした。

/content/brand/country/language

例:

/content/shiny-brand/switzerland/fr
/content/shiny-brand/switzerland/de

/content/shiny-brand/france/fr

/content/shiny-brand/germany/de

各国には独自のドメインがありました。

www.shiny-brand.ch

www.shiny-brand.fr

www.shiny-brand.de

言語サイト間にナビゲーション可能なリンクがなく、明らかなインクルージョンもなかったので、statfileslevel を 3 に設定しました。

すべてのサイトは基本的に同じコンテンツを提供していました。 唯一の大きな違いは言語でした。

Google などの検索エンジンでは、異なる URL 上に同じコンテンツを持つことを「欺瞞的」と考えます。 ユーザーは、同じコンテンツを提供するファームを作成して、ランキングを高めたり、より頻繁にリストされようとするかもしれません。 検索エンジンはこのような試みを検知し、単に同じコンテンツを使い回しているページの評価を、かえって低くします。

各ページのヘッダーセクションで各関連ページに <link rel="alternate"> タグを設定して、実際に同じコンテンツが含まれるページが複数あり、システムを「操作」する意図がないことを透明化し、ランキングが下がるのを防ぐことができます(ページのローカライズ版について Google に知らせる」を参照)。

# URL: www.shiny-brand.fr/fr/home/produits.html

<head>

  <link rel="alternate"
        hreflang="fr-ch"
        href="http://www.shiny-brand.ch/fr/home/produits.html">
  <link rel="alternate"
        hreflang="de-ch"
        href="http://www.shiny-brand.ch/de/home/produkte.html">
  <link rel="alternate"
        hreflang="de-de"
        href="http://www.shiny-brand.de/de/home/produkte.html">

</head>

----

# URL www.shiny-brand.de/de/home/produkte.html

<head>

  <link rel="alternate"
        hreflang="fr-fr"
        href="http://www.shiny-brand.fr/fr/home/produits.html">
  <link rel="alternate"
        hreflang="fr-ch"
        href="http://www.shiny-brand.ch/fr/home/produits.html">
  <link rel="alternate"
        hreflang="de-ch"
         href="http://www.shiny-brand.ch/de/home/produits.html">

</head>

すべてをインターリンク

すべてをインターリンク

SEO の専門家の中には、ある言語で検索順位の高い web サイトから、別の言語の同じ web サイトへ評判や「リンクジュース」が移る可能性がある、と主張する人もいます。

このスキームによって多数のリンクが生成されると同時に、いくつかの問題も発生しました。 n 言語の p に必要なリンクの数は、p x (n2-n) です。各ページは、それ自体 (-n) を除く他のページ (n x n) にリンクしています。 このスキームは各ページに適用されます。 20 ページの 4 言語の小さなサイトがある場合、それぞれは 240 リンクになります。

まず、編集者がこれらのリンクを手動で管理する必要はありません。これらのリンクはシステムによって自動的に生成されます。

2 つ目に、これらは正確である必要があります。 新しい「相対」が検出された場合は、同じコンテンツ(ただし異なる言語)を持つ他のすべてのページからリンクします。

このプロジェクトでは、新しい相対ページが頻繁に表示されていました。 しかし、「代替」リンクとしては具体化しませんでした。 例えば、de-de/produkte ページはドイツ語の web サイトに公開されましたが、他のサイトにはすぐには表示されませんでした。

その理由として、アドビの設定では、サイトは独立しているはずでした。 ドイツ語 web サイト上で変更が行われても、フランス語の web サイト上の無効化はトリガーされませんでした。

その問題を解決する方法の 1 つは既にご存じでしょう。 statfileslevel を 2 に下げるだけで、無効化ドメインの範囲を広げることができます。 もちろん、これは、特にパブリケーションの場合にキャッシュのヒット率を減らすので、無効化が頻繁に発生します。

この場合は、さらに複雑でした。

同じコンテンツであっても、実際のブランド名は国ごとに異なっていました。

shiny-brand は、フランスでは marque-brillant、ドイツでは blitzmarke と呼ばれていました。

/content/marque-brillant/france/fr
/content/shiny-brand/switzerland/fr
/content/shiny-brand/switzerland/de
/content/blitzmarke/germany/de
…

これは、statfiles レベルを 1 に設定することを意味しますが、無効化ドメインが大きくなりすぎました。

サイトの構造を変更していれば、この問題は解決していたはずです。 すべてのブランドを 1 つの共通ルートの下に統合します。 しかし、当時は処理能力が十分でなかったため、こうしてもレベル 2 にしかならなかったでしょう。

アドビではレベル 3 を採用し、「代替」リンクが常に最新とは限らないという代償を払うことになりました。 この問題を軽減するために、Dispatcher 上で「reaper」cron-job を実行し、1 週間を経過したファイルをクリーンアップするようにしていました。 そのため、最終的には、ある時点で、すべてのページが再レンダリングされました。 しかしそれは、個々のプロジェクトで個別に決定が必要なトレードオフです。

まとめ

Dispatcher の一般的な動作方法に関する基本原則をいくつか説明し、正しく動作させるためにもう少し実装に手間をかける必要がある場合や、トレードオフを行うことになる場合の例をいくつか示しました。

Dispatcher での設定方法の詳細については、この章では触れませんでした。 ユーザーには、コンソールに進む前に、まず基本的な概念や問題を理解していただきたいと考えました。 また、実際の設定作業は十分に文書化されています。基本的な概念を理解していれば、様々なスイッチの用途がわかるはずです。

Dispatcher のヒントとテクニック

このブックの第 1 部を、何らかの状況で役立つ可能性のあるヒントやテクニックのランダムなコレクションで締めくくります。 以前と同様に、具体的なソリューションではなくアイデアを提示し、そのアイデアやコンセプトを理解できるようにするとともに、実際の設定をより詳細に説明している記事へのリンクを紹介します。

正しい無効化のタイミング

標準の状態で AEM オーサーと公開環境をインストールした場合、トポロジは少し変わったものになります。 オーサーは、コンテンツを公開システムに送信すると同時に、無効化リクエストを Dispatcher に送信します。 公開システムと Dispatcher は両方とも、キューによってオーサーから切り離されているので、タイミングが適切でないことがあります。 コンテンツが公開システム上で更新される前に、Dispatcher がオーサーから無効化リクエストを受け取ることがあります。

その間にクライアントがそのコンテンツをリクエストした場合、Dispatcher は古い(期限切れの)コンテンツをリクエストして保存します。

より信頼性の高い設定では、コンテンツを受信した​ 後 ​で、パブリッシュシステムから無効化リクエストを送信します。 「パブリッシュインスタンスからの Dispatcher キャッシュの無効化」の記事では詳細を説明しています。

参照

helpx.adobe.com - パブリッシングインスタンスからのDispatcher キャッシュの無効化

HTTP ヘッダーおよびヘッダーのキャッシュ

以前は、Dispatcher は単にプレーンファイルをファイルシステムに保存していました。 HTTP ヘッダーをお客様に配信する必要がある場合は、ファイルまたは場所から得たわずかな情報に基づいて Apache を構成し、配信を行いました。 これは、HTTP ヘッダーに大きく依存した web アプリケーションを、AEM に実装した場合に特に面倒でした。 AEM のみのインスタンスではすべて正常に動作しましたが、Dispatcher を使用した場合は機能しませんでした。

通常は、リソースのパスとサフィックスから得られる情報を使用して、mod_headers を使用して、Apache サーバー内のリソースに欠落しているヘッダーを再適用し始めます。 しかし、それだけでは必ずしも十分ではありませんでした。

特に面倒だったのは、Dispatcher を使用した場合でも、ブラウザーへの最初の​ キャッシュされていない ​応答は、全範囲のヘッダーを含むパブリッシュシステムから送信されるのに対し、後続の応答は、限られたヘッダーのセットを含む Dispatcher によって生成されたということです。

Dispatcher 4.1.11 以降では、Dispatcher は、パブリッシュシステムによって生成されたヘッダーを格納できます。

これにより、Dispatcher でのヘッダーロジックを重複させる必要がなくなり、HTTP と AEM の表現力を最大限に発揮できます。

参照

個々のキャッシュの例外

一般的に、すべてのページと画像をキャッシュする場合がありますが、場合によっては例外を作成することもできます。 例えば、Captcha を表示する PNG 画像(各リクエストで変更されると想定される)をキャッシュせずに、PNG 画像をキャッシュするとします。 Dispatcher は Captcha を Captcha として認識しない場合がありますが、AEM は確かに認識します。 応答と共に対応するヘッダーを送信することで、Dispatcher に対してこの 1 つのリクエストをキャッシュしないようにリクエストできます。

  response.setHeader("Dispatcher", "no-cache");

  response.setHeader("Cache-Control: no-cache");

  response.setHeader("Cache-Control: private");

  response.setHeader("Pragma: no-cache");

Cache-Control と Pragma は、CDN などの上位キャッシュレイヤーに伝播され、解釈される公式の HTTP ヘッダーです。 Dispatcher ヘッダーは、Dispatcher がキャッシュしないというヒントにすぎません。 これを使用して、上位のキャッシュレイヤーでキャッシュを許可しながら、Dispatcher にキャッシュしないように伝えることができます。 実際のところ、それが役に立つ可能性があるケースを見つけるのは難しいです。 しかし、どこかに、いくつかあるのは確かです。

参照

ブラウザーのキャッシュ

最も速い HTTP 応答は、ブラウザー自体が提供する応答です。 この場合、リクエストと応答が高負荷の web サーバーにネットワーク経由で移動する必要がありません。

リソースに有効期限を設定すると、ブラウザーがサーバーに新しいバージョンのファイルをリクエストするタイミングを決めるのに役立ちます。

通常、これを静的に行うには、Apache の mod_expires を使用するか、より個別の制御が必要な場合は AEM から提供される Cache-Control ヘッダーと Expires ヘッダーを保存します。

ブラウザーにキャッシュされたドキュメントの最新性には、3 つのレベルがあります。

  1. 最新の保証:ブラウザーは、キャッシュされたドキュメントを使用できます。

  2. 古い可能性がある:ブラウザーは、まず、キャッシュされたドキュメントが最新かどうかをサーバーに問い合わせる必要があります。

  3. 古い:ブラウザーは、サーバーに新しいバージョンをリクエストする必要があります。

1 つ目は、サーバーが設定した有効期限により保証されます。 リソースが期限切れでない場合は、サーバーに再度問い合わせる必要はありません。

ドキュメントの有効期限に達した場合でも、まだ新しいものである可能性があります。 有効期限は、ドキュメントが配信される際に設定されます。 しかし、多くの場合、新しいコンテンツがいつ利用可能になるかは事前にわからないため、これは控えめな見積もりにすぎません。

ブラウザーのキャッシュ内のドキュメントが、新しいリクエストで配信されるドキュメントと同じかどうかを判断するには、ブラウザーはドキュメントの Last-Modified 日付を使用できます。 ブラウザーがサーバーに次のように要求します。

「6月10日のバージョンがあります…更新が必要ですか?」 そしてサーバーは

リソースを再送せずに「304 - そのバージョンは最新です」と応答するか、

HTTP ヘッダー内に「200 - こちらが最新バージョンです」、および HTTP 本文内のコンテンツは実際の最新のものですと、応答することがあります。

2 番目を機能させるには、Last-Modified の日付をブラウザーに送信して、更新をリクエストするための参照ポイントをブラウザーに提供してください。

前に、Last-Modified の日付が Dispatcher によって生成される場合、キャッシュされたファイルとその日付は、ブラウザーによってファイルがリクエストされたときに生成されるため、リクエストごとに異なる可能性があることを説明しました。 代替手段として「ETag(エンティティタグ)」を使用する方法があります。ETag は、日付の代わりに実際のコンテンツを識別するための識別子(ハッシュコードの生成など)です。

ACS Commons パッケージ​の「ETag サポート」ではこのアプローチを使用しています。 ただし、これには代償が伴います。ETag はヘッダーとして送信する必要がありますが、ハッシュコードの計算には応答を完全に読み取る必要があるため、応答は配信する前にメインメモリーに完全にバッファリングする必要があります。 Web サイトでキャッシュされていないリソースが存在する可能性が高い場合、これは待ち時間に悪影響を与える可能性があります。もちろん AEM システムによって消費されるメモリに注意する必要があります。

URL フィンガープリントを使用する場合、非常に長い有効期限を設定できます。 フィンガープリントされたリソースは、ブラウザーで永続的にキャッシュできます。 新しいバージョンは新しい URL でマークされますが、古いバージョンは更新する必要はありません。

スプーラーのパターンを導入した際に URL フィンガープリントを使用しました。 /etc/design からの静的ファイル(CSS、JS)が変わることはほとんどなく、フィンガープリントとして使用するのに適した候補です。

通常のファイルでは、HTML は 30 分ごと、画像は 4 時間ごとに再チェックするなど、固定のスキームを設定します。

ブラウザーのキャッシュは、オーサーシステムで非常に役立ちます。 編集エクスペリエンスを向上させるために、ブラウザーで可能な限りキャッシュすることが重要です。 残念ながら、最もコストがかかるアセットである HTML ページは、オーサー上で頻繁に変更されることが想定されているため、キャッシュすることはできません。

AEM の UI を構成する Granite ライブラリは、かなりの期間キャッシュすることができます。 また、サイトの静的ファイル(フォント、CSS、JavaScript)をブラウザーにキャッシュすることもできます。 /content/dam の画像も、通常は、ページ上のテキストのコピーと同じ頻度では変更されないので、15 分間キャッシュできます。 画像は、AEM ではインタラクティブに編集されません。 AEM にアップロードする前に、まず画像の編集と承認を行う必要があります。 したがって、テキストほど頻繁には変更されないことが想定されます。

UI ファイル、サイトのライブラリファイル、画像をキャッシュすることで、編集モードでのページの再読み込みを大幅に高速化できます。

参照

*developer.mozilla.org - キャッシュ

URL の切り捨て

リソースは、次の場所に保存されます。

/content/brand/country/language/…

もちろん、これは顧客に表示される URL ではありません。 審美性、可読性、SEO の理由により、既にドメイン名で表されている部分を切り詰める必要が生じる場合があります。

ドメインがある場合

www.shiny-brand.fi

通常、ブランド名や国名をパスに入れる必要はありません。 例えば、

www.shiny-brand.fi/content/shiny-brand/finland/fi/home.html

のような URL の代わに、

www.shiny-brand.fi/home.html

そのマッピングは AEM 上で実装する必要があります。AEM は、その切り詰められた形式に従ってリンクをどのようにレンダリングするかを認識している必要があるためです。

ただし、AEM のみに依存しないでください。 その場合、キャッシュのルートディレクトリに /home.html のようなパスが保存されます。 これは、フィンランド語サイトやドイツ語サイト、あるいはカナダの web サイトのどれを表しているのでしょうか。 また、Dispatcher に /home.html のファイルがある場合、/content/brand/fi/fi/home の無効化リクエストを受け取ったときにこのファイルを無効化する必要があることを、Dispatcher はどのように認識するのでしょうか。

ここまで、ドメインごとに別々の docroot を持つプロジェクトを見てきました。 デバッグと保守は大変で、私たちも実際に完璧に動作するケースに遭遇したことはありません。

キャッシュを再構築することで、問題を解決できました。 すべてのドメインに単一のdocrootがあり、サーバー上のすべてのファイルが/contentで開始されたため、無効化リクエストを1:1で処理できました。

切り捨ても非常に容易でした。 AEM で /etc/map の設定に従い、切り捨てられたリンクを生成しました。

/home.html のリクエストが Dispatcher にアクセスしている場合、まず最初にパスを内部的に拡張する書き換えルールが適用されます。

このルールは、各 vhost の設定内で静的に設定されていました。 簡潔に言うと、ルールは次のようなものでした。

  # vhost www.shiny-brand.fi

  RewriteRule "^(.\*\.html)" "/content/shiny-brand/finland/fi/$1"

ファイルシステムでは、オーサーとパブリッシュでも見つかるプレーンな /content ベースのパスとして表示されるようになりました。これは、多くのデバッグに役立ちました。 正しい無効化については言うまでもなく、それはもはや問題ではありませんでした。

ブラウザーの URL スロットに表示される URL、つまり、「表示可能」なURL に対してのみ行いました。 例えば、画像の URL は純粋な「/content」の URL でした。 検索エンジン最適化(SEO)では、「メイン」の URL を整えるだけで十分だと考えます。

1 つの共通の docroot には、もう 1 つ便利な利点もありました。 Dispatcher で問題が発生した場合は、次のコマンドを実行することでキャッシュ全体をクリーンアップできます。

rm -rf /cache/dispatcher/*

(高負荷のピーク時には避けたい作業です)。

参照

エラー処理

AEM クラスでは、Sling でエラーハンドラーをプログラムする方法を学びます。 これは、通常のテンプレートを記述する場合とはそれほど異なりません。 ユーザーは JSP または HTL のいずれかでテンプレートを書くだけですよね。

はい - ただし、これは AEM の部分のみです。 注意 - Dispatcher は 404 – not found または 500 – internal server error の応答をキャッシュしません。

これらのページを(失敗した)リクエストごとに動的にレンダリングする場合、Publish システムに不要な高負荷がかかります。

エラーが発生した場合、エラーページ全体をレンダリングするのではなく、装飾やロジックを含まない、非常に単純化された小さなページ(静的バージョンであっても)のみをレンダリングすると便利です。

もちろん、これはお客様が見たものとは異なります。 Dispatcher で、ErrorDocuments を次のように登録しました。

ErrorDocument 404 "/content/shiny-brand/fi/fi/edocs/error-404.html"
ErrorDocument 500 "/content/shiny-brand/fi/fi/edocs/error-500.html"

これで、AEMシステムは、何か問題が発生したことを Dispatcher に通知するだけで、Dispatcher はエラードキュメントの素晴らしく美しいバージョンを配信できます。

ここでは 2 つのことに留意する必要があります。

まず、error-404.html は常に同じページです。 「『produkten』の検索では結果が得られませんでした」のような個別のメッセージは表示されません。 これは問題ありません。

2 つ目は、内部サーバーエラーが発生した場合、または AEM システムの停止が発生した場合、AEM にエラーページのレンダリングを要求する方法がないことです。 ErrorDocument ディレクティブで定義されている必要な後続のリクエストでもうまく行きません。 wget を介して定義された場所から定期的にエラー ページを取得し、ErrorDocuments ディレクティブで定義された静的ファイルの場所に保存する cron ジョブを実行して、この問題を回避しました。

参照

セキュリティ保護されたコンテンツのキャッシュ

Dispatcher は、デフォルトではリソースを配信する際に権限を確認しません。 公開 Web サイトを高速化するために、このように意図的に実装されています。 ログインで一部のリソースを保護する場合は、基本的に 3 つのオプションがあります。

  1. リクエストがキャッシュに到達する前に、Dispatcher の前の SSO(シングルサインオン)ゲートウェイによって、または Apache サーバーのモジュールとしてリソースを保護する。

  2. 機密リソースをキャッシュから除外し、常に Publish システムからライブで提供する。

  3. Dispatcher で権限に応じたキャッシュを使用する。

もちろん、3 つのアプローチを自分で組み合わせ、適用することもできます。

オプション 1。 「SSO」ゲートウェイは、組織によって強制される場合があります。 アクセススキームの粒度が非常に粗い場合は、リソースへのアクセスを許可するか拒否するかを決定するために AEM からの情報が必要ない場合があります。

NOTE
このパターンでは、各リクエストを​_インターセプト_​し、実際の​_認可_、つまり Dispatcher へのリクエストを許可または拒否する​_ゲートウェイ_​が必要です。 SSO システムがユーザーの ID を確立するだけの​_authenticator_​である場合は、オプション 3 を実装する必要があります。 SSO システムのハンドブックで「SAML」や「OAuth」のような用語を見た場合、これはオプション 3 を実装する必要がある強力な指標です。

オプション 2。 一般に、「キャッシュしない」のは悪い考えです。 キャッシュしない場合、トラフィックの量と、除外される機密リソースの数が少なくなるようにしてください。 または、Publish システムにメモリ内キャッシュがインストールされていて、Publish システムが結果として発生する負荷を処理できることを確認します。このシリーズの第 3 部で詳細を説明します。

オプション 3。 「権限に影響を受けるキャッシュ」は、興味深いアプローチです。 Dispatcher はリソースをキャッシュしていますが、配信前に AEM システムに配信してよいかどうかを確認します。 これにより、Dispatcher から Publish への追加の要求が作成されますが、既にキャッシュされている場合は、通常、Publish システムがページの再レンダリングを防ぐことができます。 ただし、このアプローチには、いくつかのカスタム実装が必要です。 詳しくは、権限に影響を受けるキャッシュを参照してください。

参照

猶予期間の設定

ツリーのアクティベーションや、コンテンツを最新の状態に保つ必要があるなど、短期間で頻繁に無効化を行っている場合、キャッシュを常にフラッシュしており、訪問者がほぼ常に空のキャッシュにアクセスしている可能性があります。

次の図は、単一ページにアクセスする際に考えられるタイミングを示しています。 もちろん、リクエストされるページの数が多くなると、問題はさらに大きくなります。

頻繁にアクティブ化すると、ほとんどの場合、無効なキャッシュが発生します

頻繁にアクティブ化すると、ほとんどの場合、無効なキャッシュが発生します

「キャッシュ無効化の嵐」と呼ばれることもあるこの問題を軽減するには、statfile の解釈をそれほど厳密にしないことができます。

自動無効化に grace period を使用するように Dispatcher を設定できます。 これにより、statfiles 変更日に内部的に時間が追加されます。

例えば、statfileの変更時間が今日12:00で、gracePeriodの変更時間が2分に設定されているとします。 その後、すべての自動無効化されたファイルは、12:01と12:02の時点で有効であると見なされます。 12:02以降に再レンダリングされます。

参照設定では、gracePeriod として 2 分を提案していますが、それには十分な理由があります。 「2 分なんて 大したことはない、。 「コンテンツが表示されるまで 10 分くらいなら簡単に待てます……」。 そして、コンテンツの表示に 10 分以上かかると想定して、待機時間を長く(10 分などに)設定にしたい誘惑にかられる可能性があります。

WARNING
gracePeriod はこのようには機能しません。 猶予期間は、ドキュメントの無効化が保証されるまでの期間では​_なく_、無効化が起こらない時間枠です。 このフレーム内で後続の無効化が行われると、その期間が​_延長_​されます。この期間は無制限に長くなる可能性があります。

例を使用して、gracePeriod が実際にどのように動作するかを説明してみましょう。

例えば、メディアサイトを運営しているユーザーが、編集スタッフが 5 分ごとに定期的にコンテンツを更新するとします。 gracePeriod を 5 分に設定したとします。

12:00に簡単な例から始めます。

12:00 - Statfileは12:00に設定されます。 キャッシュされたファイルはすべて、12:05まで有効と見なされます。

12:01 – 無効化が発生します。 これにより、グレートタイムが12:06に延長されます

12:05 – 別の編集者が彼の記事を公開します – 別の猶予期間によって猶予時間を12:10に延長します。

これが繰り返され、コンテンツは決して無効化されません。 gracePeriod 内​の各無効化により、実質的に猶予時間が延長されます。 gracePeriod は無効化の嵐を乗り切るように設計されていますが、最終的には無視できなくなります。そのため、永遠に待ち続けなくてもよいよう、gracePeriod はかなり短く設定してください。

決定論的猶予期間

無効化の嵐を乗り越えるための別のアイデアをご紹介します。 ただのアイデアです。 本番環境では試していませんが、皆さんと共有する価値があると考えた興味深いコンセプトです。

通常のレプリケーション間隔が gracePeriod よりも短い場合、gracePeriod が予想外に長くなる可能性があります。

別の考え方は、一定の間隔でのみ無効にすることです。 この間の時間は常に古いコンテンツを提供することを意味します。 無効化は最終的に発生しますが、多数の無効化が 1 つの「一括」無効化に収集されるため、Dispatcher はその間にキャッシュされたコンテンツを提供し、パブリッシュシステムが準備機会を得ることができます。

実装は次のようになります。

無効化の発生後に実行される「カスタム無効化スクリプト」(参照)を使用します。 このスクリプトは、statfile's ファイルの最終変更日を読み取り、次の間隔停止まで切り上げます。 Unix シェルコマンド touch --time で時刻を指定します。

例えば、猶予期間を 30 秒に設定した場合、Dispatcher は statfile の最終更新日時を次の 30 秒に丸めます。 その間に発生する無効化リクエストは、同じ次の 30 秒間を設定するだけです。

無効化を次の 30 秒まで延期するとヒット率が増加する。

無効化を次の 30 秒まで延期するとヒット率が増加する。

無効化リクエストと次のラウンドの 30 秒スロットの間に発生したキャッシュヒットは古いとみなされます。パブリッシュには更新がありましたが、Dispatcher は依然として古いコンテンツを提供します。

この方法を使用すると、後続のリクエストによって猶予期間が予測不能に延長されてしまう心配をせずに、より長い猶予期間を定義できます。 前に述べたように、単なるアイデアに過ぎず、テストする機会がありませんでした。

参照

helpx.adobe.com - Dispatcher Configuration

自動再取得

お客様のサイトには、非常に特有のアクセスパターンがあります。 高い負荷の着信トラフィックがあり、トラフィックの大部分はページのごく一部に集中しています。 トラフィックの 90%は、ホームページ、キャンペーンのランディングページ、および最も注目される製品詳細ページに集中しています。 また、新しいサイトを運営する場合は、古い記事と比較して新しい記事の方がトラフィック数が多くなります。

これらのページは頻繁にリクエストされるので、Dispatcher にキャッシュされている可能性が非常に高くなっています。

任意の無効化リクエストが Dispatcher に送信され、最も人気のあるページを含むすべてのページが無効化されます。

その後、これらのページは非常に人気が高いため、様々なブラウザーから新しいリクエストが届きます。 例として、ホームページを見てみましょう。

キャッシュが無効になったので、同時に入ってくるホームページへのすべてのリクエストがパブリッシュシステムに転送され、高い負荷が発生します。

空のキャッシュ上の同じリソースに対する並列リクエスト:リクエストはパブリッシュに転送される

空のキャッシュ上の同じリソースに対する並列リクエスト:リクエストはパブリッシュに転送される

自動再取得を使用すると、これをある程度まで軽減できます。 無効化されたページのほとんどは、自動無効化後も引き続き、Dispatcher 上に物理的に保存されます。 古いと​ 見なされている ​だけに過ぎません。 自動再取得​とは、古いコンテンツを再フェッチするために公開システムに対して 1 つ​のリクエストを開始しながら、これらの古いページを数秒間引き続き提供することを意味します。

バックグラウンドでの再取得中に古いコンテンツを配信する

バックグラウンドでの再取得中に古いコンテンツを配信する

再取得を有効にするには、自動無効化後に再取得するリソースを Dispatcher に伝える必要があります。 アクティベートしたページは、他のすべてのページ(人気のあるページを含む)も自動的に無効化されます。

再フェッチするということは、実際にはDispatcherに対してそれぞれ(!)を伝えることを意味します 最も人気のあるものを再フェッチし、最も人気のあるものを再フェッチする無効化リクエスト。

これは、無効化リクエスト本文にリソース URL(パスだけでなく実際の URL)のリストを配置することで実現されます。

POST /dispatcher/invalidate.cache HTTP/1.1

CQ-Action: Activate
CQ-Handle: /content/my-brand/home/path/to/some/resource
Content-Type: Text/Plain
Content-Length: 207

/content/my-brand/home.html
/content/my-brand/campaigns/landing-page-1.html
/content/my-brand/campaigns/landing-page-2.html
/content/my-brand/products/product-1.html
/content/my-brand/products/product-2.html

Dispatcher がこのようなリクエストを確認すると、通常どおり自動無効化がトリガーされ、即座にキューに入れられて、パブリッシュシステムから新しいコンテンツを再取得します。

リクエスト本文を使用するので、HTTP 標準に従って content-type と content-length も設定する必要があります。

また、Dispatcher は、内部的に対応する URL にマークを付けるので、これらのリソースが、自動無効化で無効と見なされる場合でも、直接配信できることを認識します。

リストに表示されるすべての URL が 1 つずつリクエストされます。 したがって、パブリッシュシステムの負荷が高すぎることを気にする必要はありません。 しかし、このリストに入れる URL が多すぎるのは避ける必要があります。 最後に、古いコンテンツを長時間提供しないように、キューは最終的に一定の時間で処理する必要があります。 最も頻繁にアクセスされる 10 ページを含めます。

Dispatcher のキャッシュディレクトリを調べると、タイムスタンプが付いた一時ファイルが表示されます。 これらは、現在バックグラウンドで読み込まれているファイルです。

参照

helpx.adobe.com - AEMからのキャッシュされたページの無効化

パブリッシュシステムのシールド

Dispatcher は、メンテナンス目的のみのリクエストからパブリッシュシステムを保護することで、セキュリティを少し強化します。 例えば、/crx/de または /system/console の URL を一般公開したくないとします。

Web アプリケーションファイアウォール(WAF)をシステムにインストールしても、問題はありません。 予算がかなり増えます。すべてのプロジェクトに金銭的余裕があるわけでも、WAF を運用し、維持する余裕がある状況にあるわけではありません。

よく目にするのは、より脆弱なリソースへのアクセスを防ぐ、Dispatcher 設定に含まれる Apache の書き換えルールのセットです。

ただし、別の方法を検討することもできます。

Dispatcher の設定に従い、Dispatcher モジュールは、次の特定のディレクトリにバインドされます。

<Directory />
  SetHandler dispatcher-handler
  …
</Directory>

しかし、後でフィルターを実行する必要がある場合、ハンドラーを docroot 全体にバインドするのはなぜでしょうか。

まず、ハンドラーのバインディングを絞り込むことができます。 SetHandler は、ハンドラーをディレクトリにバインドするだけで、ハンドラーを URL または URL-pattern にバインドできます。

<LocationMatch "^(/content|/etc/design|/dispatcher/invalidate.cache)/.\*">
  SetHandler dispatcher-handler
</LocationMatch>

<LocationMatch "^/dispatcher/invalidate.cache">
  SetHandler dispatcher-handler
</LocationMatch>

…

その場合は、必ず dispatcher-handler を Dispatcher の無効化 URL にバインドすることを忘れてはいけません。そうしないと、AEMから Dispatcher に無効化リクエストを送信できなくなります。

Dispatcher をフィルターとして使用するもう 1 つの方法は、dispatcher.any でフィルターディレクティブを設定することです。

/filter {
  /0001  { /glob "\*" /type "deny" }
  /0002  { /type "allow"  /url "/content\*"  }

他のディレクティブでなくあるディレクティブを使用するよう命令するつもりはありませんが、すべてのディレクティブを適切な組み合わせで使用することをお勧めします。

しかし、できるだけ早く、必要に応じて、可能な限り簡単な方法で URL スペースをチェーン内で絞り込むことを検討することを提案します。 これらの技術は、非常に機密性の高い Web サイト上の WAF の代わりとなるものではないことに注意してください。 これらの技術を「貧乏人のファイアウォール」と呼ぶ人もいますが、これには理由があります。

参照

apache.org- sethandler ディレクティブ

helpx.adobe.com - コンテンツフィルターへのアクセスの設定

正規表現と globs を使用したフィルタリング

初期の頃は、Dispatcher 設定でフィルターを定義するために、「globs」(シンプルなプレースホルダー)のみを使用できました。

幸いにも、これは後のバージョンの Dispatcher で変更されました。 POSIX の正規表現も使用でき、リクエストの様々な部分にアクセスしてフィルターを定義できます。 Dispatcher を使い始めたばかりのユーザーにとって、これは当たり前のことかもしれません。 しかし、globs だけに慣れている方にとっては、この点は驚きであり、見落としがちです。 それに、globs と正規表現の構文は非常に似ています。 同じ処理を行う 2 つのバージョンを比較してみましょう。

# Version A

/filter {
  /0001  { /glob "\*" /type "deny" }
  /0002  { /type "allow"  /url "/content\*"  }

# Version B

/filter {
  /0001  { /glob "\*" /type "deny" }
  /0002  { /type "allow"  /url '/content.\*'  }

違いがわかりますか?

バージョン B では一重引用符「'」を使用して​ 正規表現パターン ​をマークします。 「任意の文字」は、「.*」で表現されます。

一方、グロビングパターンは二重引用符「"」を使用し、「*」などの単純なプレースホルダーのみ使用できます。__

違いを知っていれば、些細なことですが、知らなければ、引用符を簡単に混同してしまい、貴重な時間を設定のデバッグに費やすことになります。 注意してください。

「設定内の '/url' はわかるが、フィルターにある '/glob' は何だろう」と思われる方もいるかもしれません。

このディレクティブは、メソッドとパスを含むリクエスト文字列全体を表します。 例えば

"GET /content/foo/bar.html HTTP/1.1"

これは、パターンの比較対象となる文字列です。 初心者は、最初の部分である method(GET、POSTなど)を忘れがちです。 つまり、

/0002 { /glob "/content/\*" /type "allow" }

パターンは常に失敗します。これは、「/content」が リクエストの「GET…」に一致しないからです。

したがって、globs を使いたいときには、

/0002 { /glob "GET /content/\*" /type "allow" }

とするのが正解です。

最初の拒否ルールについては、

/0001 { /glob "\*" /type "deny" }

で問題ありません。 しかし、その後の許可ルールでは、リクエストの個々の部分を使うほうが、より明確で表現力があり、はるかに安全です。

/method
/url
/path
/selector
/extension
/suffix

以下のようにします。

/005  {

  /type "allow"
  /method "GET"
  /extension '(css|gif|ico|js|png|swf|jpe?g)' }

注意:ルール上で正規表現と glob 式を混在させることができます。

最後に、各定義の前にある /005 のような「行番号」についてですが、

全く意味はありません。 ルールに任意の分母を選択できます。 数字を使用すれば、スキームについて考えるのに多くの労力はかかりませんが、順序が重要であることに注意してください。

以下のように何百ものルールがある場合、

/001
/002
/003
…
/100
…

/001 と /002 の間に 1 つ挿入した場合、後続の番号はどうなるでしょうか。 後続の数字が増えるでしょうか。 中間の数字が挿入されるでしょうか。

/001
/001a
/002
/003
…
/100
…

また、/003 と /001 の順序を並べ替えた場合はどうなるでしょうか。名前や ID を変更することになるのでしょうか。

/003
/002
/001
…
/100
…

番号付けは、最初は単純な選択に見えますが、長期的には限界があります。 率直に言うと、数字を識別子として選択することは、そもそも好ましくないプログラミングスタイルです。

私たちは別のアプローチを提案したいと考えています。ほとんどの場合、個々のフィルタールールごとに意味のある識別子を考え出すことはできないでしょう。 しかし、それらはおそらくより大きな目的を果たすので、その目的に応じて何らかの方法でグループ化することができます。 例えば、「基本的な設定」、「アプリケーション固有の例外」、「グローバルな例外」、「セキュリティ」などです。

その後、ルールに適切な名前を付けてグループ化することで、設定ファイルを読む同僚に対して、ファイル内での位置づけや見通しを示すことができます。

  # basic setup:

  /filter {

    # basic setup

    /basic_01  { /glob "\*"             /type "deny"  }
    /basic_02  { /glob "/content/\*"    /type "allow" }
    /basic_03  { /glob "/etc/design/\*" /type "allow" }

    /basic_04  { /extension '(json|xml)'  /type "deny"  }
    …

    # login

    /login_01 { /glob "/api/myapp/login/\*" /type "allow" }
    /login_02 { … }

    # global exceptions

    /global_01 { /method "POST" /url '.\*contact-form.html' }

新しいルールを 1 つのグループに追加する場合や、または新しいグループを作成する場合があります。 この場合、名前の変更や再番号付けを行う項目の数は、そのグループに制限されます。

WARNING
より高度な設定では、フィルタリングルールをメイン dispatcher.any 設定ファイルに含まれる多数のファイルに分割します。 ただし、新しいファイルでは新しい名前空間は導入されません。 そのため、あるファイルに「001」というルールがあり、別のファイルにも「001」というルールがある場合、エラーが発生します。 強い意味を持つ名前を使用する理由は他にもあります。

参照

helpx.adobe.com - グロブプロパティのパターンのデザイン

プロトコルの仕様

最後のヒントは実際にはヒントと言えるものではありませんが、それでもお客様にお伝えする価値があると考えました。

ほとんどの場合、AEM と Dispatcher は追加の設定を行わなくてもそのまま動作します。 したがって、独自のアプリケーションを上に構築するための無効化プロトコルに関する包括的な Dispatcher プロトコル仕様は見つかりません。 情報は公開されていますが、いくつかのリソースにまたがって少し散在しています。

ここでは、ある程度ここでギャップを埋めてみます。 無効化リクエストは次のようになります。

POST /dispatcher/invalidate.cache HTTP/1.1
CQ-Action: <action>
CQ-Handle: <path-pattern>
[CQ-Action-Scope]
[Content-Type: Text/Plain]
[Content-Length: <bytes in request body>]

<newline>

<refetch-url-1>
<refetch-url-2>

…

<refetch-url-n>

POST /dispatcher/invalidate.cache HTTP/1.1:最初の行は Dispatcher コントロールエンドポイントの URL で、変更されない可能性が高くなります。

CQ-Action: <action>:実行されるべきこと。 <action> は次のいずれかです。

  • Activate: /path-pattern.* を削除する
  • Deactive:削除 /path-pattern.*
    /path-pattern/*を削除します
  • Delete:削除 /path-pattern.*
    /path-pattern/*を削除します
  • Test:「ok」を返すだけで、何もしない

CQ-Handle: <path-pattern>:無効にする content-resource のパス。 メモ:<path-pattern> は、実際には「パターン」ではなく「パス」です。

CQ-Action-Scope: ResourceOnly:オプション:このヘッダーを設定すると、.stat ファイルには変更を加えません。

[Content-Type: Text/Plain]
[Content-Length: <bytes in request body>]

自動リフェッチ URL のリストを定義する場合は、これらのヘッダーを設定します。 <bytes in request body> は、HTTP 本文内の文字数です

<newline>:リクエスト本文がある場合は、空の行でヘッダーと区切る必要があります。

<refetch-url-1>
<refetch-url-2>
…
<refetch-url-n>

無効化後すぐに再取得する URL をリストします。

その他のリソース

Dispatcher キャッシュの概要と概要:https://helpx.adobe.com/jp/experience-manager/dispatcher/using/dispatcher.html

Dispatcher ドキュメントにすべてのディレクティブについての説明:https://helpx.adobe.com/jp/experience-manager/dispatcher/using/dispatcher-configuration.html

よくある質問の一部:https://helpx.adobe.com/jp/experience-manager/using/dispatcher-faq.html

Dispatcher の最適化に関するウェビナーの記録(強く推奨): https://my.adobeconnect.com/p7th2gf8k43?proto=true

プレゼンテーション「コンテンツ無効化の過小評価された力」、「adaptTo()」会議、2018年ポツダム(https://adapt.to/2018/en/schedule/the-underappreciated-power-of-content-invalidation.html)

AEM からのキャッシュされたページの無効化:https://helpx.adobe.com/jp/experience-manager/dispatcher/using/page-invalidate.html

次の手順

recommendation-more-help
experience-manager-learn-help-dispatcher-tutorial