第 1 章 - Dispatcher の概念、パターン、アンチパターン
概要
この章では、Dispatcher の歴史と仕組みを簡単に紹介し、これが AEM 開発者のコンポーネントの設計方法にどのように影響するかについて説明します。
開発者がインフラストラクチャを重視すべき理由
Dispatcher は、ほぼすべての AEM インストールに不可欠な要素です。Dispatcher の設定方法とヒントやテクニックを説明するオンライン記事が多数あります。
しかし、これらの細かい情報は常に非常に技術的なレベルから始まります。自分のしたいことを既に把握できている前提で、それを実現する方法の詳細のみを説明しているのです。Dispatcher でできることとできないことに関して言えば、その 内容と理由 を説明している概念的なドキュメントは見つかりませんでした。
アンチパターン:付け足しとしての Dispatcher
このような基本情報の欠如は、数々の AEM プロジェクトで見てきた多数のアンチパターンにつながります。
-
Dispatcher は Apache web サーバーにインストールされるので、その設定は「Unix の神様」のようなプロジェクトメンバーの仕事です。「下界の Java 開発者」は、それを気にかける必要はありません。
-
Java 開発者は、自分のコードを確実に動作させる必要があります。Dispatcher は後からそれを魔法のように高速化します。Dispatcher は常に付け足しです。ただし、これでは使えません。開発者は、Dispatcher を念頭に置いてコードを設計し、。 そのための基本的な概念を理解しておく必要があります。
「まず動くようにしてから高速化する」が常に正しいとは限らない
プログラミングに関し、「まず動くようにしてから高速化する」。 といったアドバイスを耳にしたことがある方もいるでしょう。完全に間違っているわけではありません。ただし、コンテキストが正しくないと、誤って解釈され、正しく適用されない傾向があります。
このアドバイスを受けて、開発者は、これは、実行されない、またはほとんど実行されないコードを早めに最適化しないようにする必要があります。ので、最適化にかかった労力を正当化するための十分な影響が、最適化によって得られないためです。さらに、最適化によって、コードがより複雑になり、バグを引き起こす可能性があります。したがって、開発者の方は、コードの各行を細かく最適化することに時間をかけすぎないでください。適切なデータ構造、アルゴリズム、ライブラリを選択し、プロファイラーによるホットスポット分析を待ち、より綿密な最適化が全体的なパフォーマンスを向上させる場所を確認するだけです。
アーキテクチャの決定とアーティファクト
しかし、「アーキテクチャの」決定に関しては、「まず動作させて、そして高速化する」というアドバイスは完全に間違っています。アーキテクチャの決定とは何ですか?簡単に言えば、コストがかかり、難しく、後で変更できない決定です。「コストがかかる」イコール「不可能」、となることもあります。例えば、プロジェクトの予算が足りなくなった場合、コストがかかる変更は実装できません。インフラの変更は、ほとんどの人が頭に思い浮かべる、そのカテゴリの最初の変化です。しかし、非常に変更が面倒な、別の種類の「アーキテクチャの」アーティファクトもあります。
-
アプリケーションの「中央」にあり、他の多くの部分が依存するコードです。これらを変更するには、すべての依存関係を一度に変更し、再テストする必要があります。
-
アーティファクトは、非同期の、タイミングに左右されるシナリオに関係し、入力や、システム動作は非常にランダムに変化する可能性があります。変更は予測できない影響を及ぼし、テストが困難になる場合があります。
-
システムのあらゆる部分で使用され、何度も繰り返し使用されるソフトウェアパターン。ソフトウェアパターンが最適でないと判断された場合は、そのパターンを使用するすべてのアーティファクトのコーディングをしなおす必要があります。
次のことに留意してください。このページのトップで、Dispatcher は AEM アプリケーションの重要な部分であると述べました。Web アプリケーションへのアクセスは非常にランダムです。ユーザーは予測不可能な時間にアクセスします。最後に、すべてのコンテンツが Dispatcher にキャッシュされます(または必要です)。細心の注意を払っていた方であれば、キャッシュは「アーキテクチャの」アーティファクトと見なされ、開発者、管理者などのチームの全メンバーが同様に理解する必要があるとお気づきでしょう。
開発者が実際に Dispatcher を設定する必要があると言っているわけではありません。また、Dispatcher でコードを活用できるように、ユーザーは概念(特に境界)を把握しておく必要があります。
Dispatcher は、コーディングの速度を魔法のように向上させるものではありません。開発者は、Dispatcher を考慮してコンポーネントを作成する必要があります。したがって、開発者はその仕組みを知っておく必要があります。
Dispatcher のキャッシュ - 基本原則
Http のキャッシュとしての Dispatcher - ロードバランサー
Dispatcher とは何ですか?まず、なぜ「Dispatcher」と呼ばれるのですか?
Dispatcher では、
-
最初にキャッシュが表示されます。
-
リバースプロキシ
-
Apache https Web サーバー用のモジュールで、Apache の汎用性に AEM 関連の機能を追加し、他のすべての Apache モジュールとスムーズに連携します(後で説明するように、SSL や SSI などを含みます)。
Web の初期の頃は、サイトの訪問者数は数百人になると予想されていました。1 つの Dispatcher を「ディスパッチ」するか、複数の AEM パブリッシュサーバーに対するリクエストの負荷を分散させる設定で、通常はこれで十分でした。これが、「Dispatcher」の名前の由来です。しかし、今では、この設定はあまり頻繁には使用されていません。
この記事の後半で、Dispatcher とパブリッシュシステムを設定するさまざまな方法について説明します。まず、http キャッシュの基本を紹介します。
Dispatcher キャッシュの基本機能
Dispatcher の基本について、ここで説明します。Dispatcher は、HTTP リクエストを受信および作成できる、単純なキャッシュリバースプロキシです。通常のリクエストおよびレスポンスのサイクルは次のようになります。
- ユーザーがページをリクエストします。
- Dispatcher は、そのページのレンダリングされたバージョンが既に存在する場合にチェックします。これがこのページに対する最初のリクエストであり、Dispatcher がキャッシュされたローカルコピーを見つけられないと仮定します。
- Dispatcher がパブリッシュシステムにページをリクエストします。
- パブリッシュシステムで、ページが JSP または HTL テンプレートでレンダリングされます。
- ページが Dispatcher に返されます。
- Dispatcher がページをキャッシュします。
- Dispatcher がページをブラウザーに返します。
- 同じページが 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 を取得した後のファイルシステムのレイアウト
基本的な制限事項
URL、リソース、ファイル名のマッピングは非常に簡単です。
ただし、お気づきかもしれませんが、いくつかの罠があります。
-
URL が非常に長くなる場合があります。ローカルファイルシステム上で
/docroot
の「パス」部分を追加すると、一部のファイルシステムの制限を簡単に超えてしまう可能性があります。Windows 上の NTFS で Dispatcher を実行することは、困難な作業になる場合があります。Linux では安全です。 -
URL には、特殊文字やウムラウトを含めることができます。これは、通常、Dispatcher で問題にはなりません。ただし、URL はアプリケーションの様々な場所で解釈されることに留意してください。アプリケーションの奇妙な動作について調べた結果、ごくまれに使用される(カスタム)コードの一部で、特殊文字に対するテストが十分に行われていなかったと判明するケースがしばしば見られます。できれば特殊文字の使用を避けてください。避けられない場合は、徹底的なテストを計画してください。
-
CRX では、リソースにはサブリソースが含まれます。例えば、あるページに多数のサブページが含まれるとします。これを 1 つのファイルシステム内で一致させることはできません。ファイルシステムには、ファイルまたはフォルダーのいずれかが含まれるからです。
拡張子のない 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」です。訪問者がホームページ(そのページへのティーザー参照を持つ)を要求すると、「カナダ」ページ上のコンポーネントは
<div class="teaser">
<h3>Canada</h3>
<img …>
</div>
のように、ホームページ へ レンダリングされます。ホームページは、Dispatcher によって静的な.html ファイルとして保存されます。このファイルにはティーザーとヘッドラインが含まれます。
これで、マーケターは、ティーザーのヘッドラインが実用的でなければならないことを学びました。したがって、タイトルを「Canada」から「Visit Canada」に変更し、画像も更新します。
編集した「Canada」ページを公開し、以前に公開したホームページを再度開いて、変更内容を確認します。しかし、何も変化は見られず、。 古いティーザーが表示されています。「Winter Special」ページを再確認します。そのページはこれまでにリクエストされたことがないので、Dispatcher で静的にキャッシュされていません。したがって、このページは「公開」によって新しくレンダリングされ、このページには新しい「Visit Canada」ティーザーが含まれるようになりました。
Dispatcher が古くなったインクルードコンテンツをホームページに保存する
なぜこうなったのでしょうか。Dispatcher は、レンダリング時に他のリソースから取り出されたすべてのコンテンツとマークアップを含む、ページの静的バージョンを保存します。
Dispatcher は単なるファイルシステムベースの web サーバーで、高速でありながら、比較的シンプルです。含まれるリソースが変更されても、それは認識されません。含めるページをレンダリングした際に存在していたたコンテンツを、引き続き保持します。
「Winter Specia」ページはまだレンダリングされていないので、Dispatcher には静的バージョンはなく、リクエストに応じてレンダリングされた新しいティーザーが表示されます。
Dispatcher はレンダリング時に扱ったすべてのリソースを追跡し、そのリソースが変更されたときに、そのリソースを使用したすべてのページをフラッシュするだろうと考えるかもしれません。しかし、Dispatcher はページをレンダリングしません。レンダリングはパブリッシュシステムによって実行されます。Dispatcher は、レンダリングされた .html ファイルに格納されるリソースを把握していません。
まだ納得がいかないという方は、「何らかの依存関係の追跡を実装する方法があるはずだ」と思われるかもしれません。**実際、より正確に言うと、以前は そのような方法がありました。AEM の遠い祖先である Communiqué 3 では、ページのレンダリングに使用されるセッションに依存関係トラッカーが実装されていました。__
リクエストの際、このセッションを通じて取得された各リソースは、レンダリング中の URL の依存関係として追跡されました。
しかし、依存関係の追跡には、非常にコストがかかりました。すぐに、依存関係の追跡機能を完全にオフにし、1 つの html ページが変更された後にすべての html ページを再レンダリングするほうが、web サイトが速くなることが判明しました。そのスキームも完璧ではなく、途中で落とし穴や例外が多く見られました。リクエストのデフォルトセッションを使用してリソースを取得するのではなく、管理者セッションを使用してリクエストをレンダリングするためのヘルパーリソースを取得する場合もありました。これらの依存関係は通常追跡されず、悩みの種となり、手動でキャッシュをフラッシュしてほしいという要望を運用チームが受けることもありました。そのための標準的な手順はありませんでした。途中には他にも多くの問題がありましたが、思い出話はここまでにしましょう。2005年に遡ります。最終的に、この機能は Communiqué 4 でデフォルトで無効となり、後継の CQ5 でも戻されず、CQ5 は AEM になりました。
自動無効化
依存関係の追跡よりも完全フラッシュの方が安価な場合
CQ5 以降、アドビでは、1 ページのみが変更された場合も、基本的にはサイト全体を無効化する処理に完全に依存しています。この機能は「自動無効化」と呼ばれます。
しかし、何百ものページを破棄して再レンダリングするほうが、適切に依存関係を追跡して部分的に再レンダリングするよりも安価なのはなぜでしょうか。
主な理由は次の 2 つです。
-
平均的な web サイトでは、頻繁にリクエストされるのはページのごく一部です。したがって、レンダリングされたコンテンツをすべて破棄した場合でも、その直後に実際にリクエストされるのは数十件です。ページのロングテールのレンダリングは、実際にリクエストされた場合に、時間をかけて分散させることができます。そのため、実際には、ページのレンダリングにかかる負荷は予想されるほど高くありません。もちろん、常に例外はあります。空の Dispatcher キャッシュを持つ大規模な web サイトで、均等に分散された負荷を処理するいくつかのテクニックについては、後ほど説明します。
-
すべてのページは、メインナビゲーションでつながっています。つまり、最終的にはほとんどのページが互いに依存しています。つまり、非常に優れた依存関係トラッカーでも、1 つのページが変更された場合は他のすべてのページを無効にする必要があるという、既知の結果につながります。
まだ疑っている方のために、最後のポイントを説明しましょう。
前回の例と同じ引数で、リモートページのコンテンツを参照するティーザーを使用します。今回は、より極端な例として、自動的にレンダリングされるメインナビゲーションを使用します。ティーザーと同様に、ナビゲーションタイトルは、リンクされたページまたは「リモート」ページからコンテンツ参照として取り出されます。リモートのナビゲーションタイトルは、現在レンダリングされているページには保存されません。ナビゲーションは web サイトの各ページでレンダリングされることに注意してください。したがって、メインナビゲーションを持つすべてのページで、1 つのページのタイトルが何度も繰り返し使用されます。ナビゲーションタイトルを変更する場合は、ページを参照する各ページではなく、リモートページで 1 度だけ変更します。
つまり、この例では、ナビゲーションはターゲットページの「NavTitle」を使用してナビゲーション内で名前をレンダリングすることで、すべてのページを 1 つにまとめています。「Iceland」のナビゲーションタイトルは、「Iceland」ページから取り出され、メインナビゲーションを持つ各ページにレンダリングされます。
メインナビゲーションは「NavTitle」を引き出すことで必然的にすべてのページのコンテンツをまとめている
アイスランドページの NavTitle を「Iceland」から「Beautiful Iceland」に変更すると、他のすべてのページのメインメニューで、直ちにタイトルが変更されます。したがって、変更前にレンダリングおよびキャッシュされたページはすべて古くなり、無効にする必要があります。
自動無効化の実装方法:.stat ファイル
何千ものページを含む大規模なサイトがある場合、すべてのページをループして物理的に削除するにはかなりの時間がかかります。その間に、Dispatcher が意図せず古いコンテンツを提供してしまう可能性があります。さらに悪いことに、キャッシュファイルへのアクセス中に競合が発生する場合があります。削除中のページがリクエストされたり、即時のアクティブ化後に 2 回目の無効化が発生し、ページが再び削除されたりします。どれほど混乱するか想像してみてください。幸いにも、このようなことは起こりません。Dispatcher は巧妙なテクニックでそれを回避します。数百、数千のファイルを削除する代わりに、ファイルが公開されたときに単純な空のファイルをファイルシステムのルートに配置するので、依存するすべてのファイルが無効と見なされます。このファイルは「statfile」と呼ばれます。statfile は空のファイルです。statfile に関して重要なのは作成日のみです。
Dispatcher 内のすべてのファイルのうち、作成日が statfile よりも古いものは、最後のアクティベーション(および無効化)より前にレンダリングされたものなので、「無効」と見なされます。これらのファイルはまだファイルシステム内に物理的に存在しますが、Dispatcher では無視される、。 「古い」ファイルです。古いリソースへのリクエストが行われるたびに、Dispatcher は AEM システムに対してページの再レンダリングを要求します。新しくレンダリングされたページは、新しい作成日でファイルシステムに保存され、再び新しくなります。
.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 つです。
-
エージェントが HTTP ヘッダー
CQ-Action-Scope: ResourceOnly
を送信する場合、この単一の無効化リクエストは自動無効化をトリガーしません。この(https://github.com/cqsupport/webinar-dispatchercache/tree/master/src/refetching-flush-agent/refetch-bundle)コードが、独自のレプリケーションエージェントを作成する良い出発点となるかもしれません。 -
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 のコアコンポーネントであってもそのパターンに基づいています。したがって、開発者としてこのパターンを採用する可能性は高いでしょう。このパターンはカプセル化の点では優れていますが、Dispatcher に対応させるには多くの労力が必要です。問題を軽減するいくつかの方法については、後ほど説明します。
ここで使用するパターンを「スプーラパターン」と呼びます。この問題は Communiqué 3 の初期の頃に遡り、リソース上で呼び出し、バイナリの生データを応答にストリーミングする「スプール」メソッドによります。
元の「スプール」という用語は、実際にはプリンターなどの低速なオフライン周辺機器の共有を指すので、この場合には適していません。しかし、オンラインの世界ではこのように区別できることはほとんどないので、私たちはこの用語を気に入っています。それぞれのパターンには区別できる名前を付けるべきでしょう。これがパターンかアンチパターンかを決めるのはユーザー次第です。
実装
レスポンシブ画像コンポーネントの実装方法を次に示します。
このコンポーネントには 2 つの部分があり、最初の部分は画像の HTML マークアップをレンダリングし、2 番目の部分は参照元の画像のバイナリデータを「スプール」します。これはレスポンシブデザインを備えた最新の web サイトなので、単純な <img src"…">
タグではなく、<picture/>
タグの一連の画像をレンダリングします。デバイスごとに 2 つの異なる画像を DAM にアップロードし、画像コンポーネントから参照します。
このコンポーネントには、3 つのレンダリングスクリプト(JSP、HTL、またはサーブレットとして実装)があり、それぞれ専用のセレクターで対応します。
/respi.jsp
:HTML マークアップをレンダリングするセレクターなし/respi.img.java
:デスクトップバージョンをレンダリングする/respi.img.mobile.java
:モバイルバージョンをレンダリングする。
このコンポーネントは、ホームページの parsys に配置されます。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 にアセットを保存しない場合は、アセット管理が困難になることを意味します。特定のアセットの使用ライセンスがなくなったと仮定します。アセットを使用したコンポーネントを見つけるにはどうすればよいですか?
そうですね。DAM の「M」は、デジタルアセット管理と同様に、「管理」を表します。その機能は明かしたくないでしょう。
まとめ
AEM 開発者の視点から見ると、パターンは非常に洗練されていました。しかし、Dispatcher を式に取り込むと、単純なアプローチでは不十分だと思うかもしれません。
今のところ、これがパターンかアンチパターンになるのかを決めるのは、自分次第です。これまでに説明した、問題を軽減する方法について、既に良いアイデアが浮かんでいる方もいるかもしれません。そうであれば、他のプロジェクトではこれらの問題をどのように解決したかを知りたいと思われることでしょう。
Dispatcher に関する一般的な問題の解決
概要
これが、どのようにキャッシュを利用して実装されたか説明します。複数の方法があります。最適な解決策を選べないこともあります。既に実行中のプロジェクトに参加していて、「キャッシュの問題」を修正するだけの限られた予算しかなく、本格的なリファクタリングを行うには十分ではないかもしれません。または、サンプルの画像コンポーネントよりも複雑な問題に直面するかもしれません。
以下の節では、原則と注意事項の概要について説明します。
繰り返しますが、これは実経験に基づいています。これまで紹介してきたのは実際のパターンであり、アカデミックの演習ではありません。これがアンチパターンを見せる理由です。過去の他の人の間違いから学ことができるのです。
キャッシュキラー
以前に、?ck=398547283745
のようなクエリパラメーターを見たことはありますか?これらはキャッシュキラー(「ck」)と呼ばれます。クエリパラメーターを追加した場合、リソースはキャッシュされません。また、パラメーターの値として乱数を追加した場合(「398547283745」など)、URL は一意になり、AEM システムと画面の間にある他のキャッシュもキャッシュできなくなります。通常の中間にある疑いは、Dispatcher、CDN、またはブラウザーキャッシュの前にある「Vanish」キャッシュです。繰り返しますが、使用は避けてください。リソースをできるだけ長くキャッシュする必要があります。キャッシュは味方です。味方を消さないでください。
自動無効化
短期的には、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 レベルを下げる
手間を省いた管理方法では、通常、「自動無効化を JPG に設定し、statfile-level を 0 に設定します。これは常に、あらゆる種類のキャッシュの問題に役立ちます」。このようなアドバイスはテクニカルフォーラムにあり、無効化の問題に役立ちます。
ここまで、statfile-level については説明してきませんでした。基本的に、自動無効化は同じサブツリー内のファイルに対してのみ機能します。ただし、ページとアセットは通常同じサブツリーに存在しないという問題が発生します。ページは /content/mysite
の下の任意の場所に、アセットは /content/dam
の下にあります。
「statfile レベル」は、サブツリーのルートノードがどの深さにあるかを定義します。上述の例では、レベルは「2」です(1=/content、2=/mysite,dam)
基本的に、statfile レベルを 0 に「下げる」という考え方は、/content ツリー全体を、ページとアセットを同じ自動無効化ドメイン内に存在させるための唯一のサブツリーとして定義することです。つまり、レベル(docroot「/」)の大きなツリーだけを持つことになります。しかし、これにより、完全に無関係なサイトでも、何かが公開されるたびに、サーバー上のすべてのサイトが自動無効化されます。キャッシュ全体のヒット率が大幅に低下するので、長期的に見れば悪いアイデアですが、信じてください。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
-
DAM 内のファイルが変更されると、パブリッシュシステム上のリスナーがトリガーされます。
-
リスナーが無効化要求を Dispatcher に送信します。自動無効化のため、サイトのホームページの下、つまりサイトの statfile レベルでない限り、自動無効化で送信するパスは関係ありません。
-
statfile が更新されます。
-
次回、ホームページがリクエストされると、再レンダリングされます。新しいフィンガープリント(日付)は、画像の lastModified プロパティから追加のセレクターとして取得されます。
-
これにより、新しい画像への参照が暗黙的に作成されます。
-
画像が実際にリクエストされた場合は、新しいレンディションが作成され、Dispatcher に保存されます。
クリーンアップの必要性
やっと、。 ここまで完了しました。ですが、
まだ終わりではありません。
以下のパス
/content/mysite/home/jcr:content/par/respi.img.fp-2018-31-12-23-59.jpg
は、無効化されたいずれのリソースにも関連していません。思い出してください。「ダミー」のリソースのみを無効化し、「ホーム」を無効と判断する自動無効化を利用しました。画像自体が物理的に削除されることはないかもしれません。__したがって、キャッシュはどんどん増えていきます。画像が変更され、アクティベートされると、Dispatcher のファイルシステムで新しいファイル名が取得されます。
キャッシュされたファイルを物理的に削除せず、無期限に保持することには、次の 3 つの問題があります。
-
明らかに、ストレージ容量を無駄にしています。ストレージはここ数年でどんどん安くなっています。しかし、画像の解像度やファイルサイズも、鮮明な画像を求める Retina のようなディスプレイの登場によって、ここ数年で増えています。
-
ハードドライブが安くなっても、「ストレージ」は安くなっていないのかもしれません。(安い)ベアメタル HDD ストレージを持たず、データセンターのプロバイダが提供する NAS 上の仮想ストレージをレンタルする傾向が見られます。この種のストレージは、より信頼性が高く拡張性が高いものの、少しコストが高くなります。古いガベージを保存して浪費したくないと思うでしょう。これはプライマリストレージに限りません。バックアップについても考えてみましょう。標準のバックアップソリューションを使用している場合は、キャッシュディレクトリを除外できない可能性があります。結局、ガベージデータもバックアップしていることになります。
-
さらに悪いことに、特定の画像の使用ライセンスを、必要に応じた限られた期間しか購入していないかもしれません。ライセンスの有効期限が切れた後も引き続き画像を保存すると、著作権侵害と見なされる可能性があります。Web ページで画像を使用しなくなったとしても、Google では画像が検出されます。
最終的に、このようなゴミをコントロールできるよう、例えば、1 週間を超えたすべてのファイルをクリーンアップするハウスキーピングの cronjob を使用することになります。
サービス拒否攻撃に対する 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 で作成します。
したがって、単純な cache-killer としてフィンガープリントを使用する代わりに、画像の 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 コンポーネントによる配信への品質プロパティの追加
画像は JPEG で、JPEG は圧縮できます。JPEG 画像を圧縮する場合、ファイルサイズの画質を取り替えます。圧縮は、「1」から「100」までの数値の「品質」パラメーターとして定義されます。「1」は「小さいが、低品質」を意味し、「100」は「優れた品質で、大きいファイル」を意味します。では、どれが完璧な数値なのでしょうか?
すべての IT と同様に、回答は「状況次第」です。
ここでは、モチーフに依存します。文字、建物の写真、イラスト、スケッチ、商品箱の写真(シャープな輪郭で文字が書かれている)など、コントラストの高いエッジがあるモチーフは、通常、そのカテゴリに含まれます。風景やポートレートのような柔らかい色とコントラストの遷移を持つモチーフは、目に見える品質を損なうことなく、もう少し圧縮することができます。自然写真は通常そのカテゴリに入ります。
また、画像の使用場所に応じて、別のパラメーターを使用することもできます。ティーザーのサムネール(小)は、画面全体のヒーローバナーで使用される同じ画像よりも、圧縮することができます。つまり、画質パラメーターは画像に固有ではなく、画像とコンテキストに固有のものです。そして、作成者の好みに合わせます。
要するに、すべての写真に対する完璧な設定はありません。万能な設定はありません。作成者が決めたものが最適な設定になります。ユーザーは、帯域幅を犠牲にしない程度に、品質に満足するまでコンポーネントのプロパティとして「quality」パラメーターを微調整します。
これで、DAM にバイナリファイルが作成され、品質プロパティを提供するコンポーネントが作成されました。URL はどのように表示されますか?スプールが責任範囲のコンポーネントはどれですか?
ネイティブアプローチ 1:プロパティをクエリパラメーターとして渡す
最後の章で、コンポーネントによってレンダリングされる画像 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:セレクターとして追加情報を渡す
コンポーネントのプロパティをセレクターとして渡す
これは最後の 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 で実行する場合は、次を確認してください。
-
CRX へのアクセスが多すぎてメモリと時間を無駄にすることなく、フィルターを超効率的に実装できる。
-
フィルターが「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 と Publish のフィルタリング
次の手順では、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 を手段として使用する必要はありません。
-
クライアントは、HTML のマークアップが、プライマリリクエストループ内でリクエストされた場合に、そのコンポーネントをレンダリングします。
-
コンポーネントのパスは、マークアップからコンポーネントへの逆参照として機能します。
-
ブラウザーは、この逆参照を使用してバイナリをリクエストします。
-
リクエストがコンポーネントに到達すると、バイナリデータのサイズ変更、圧縮、スプールを行うためのすべてのプロパティが手に入ります。
-
画像は、コンポーネントを通じてクライアントブラウザーに送信されます。
スプーラーのパターンは、結局、それほど悪くはないので、人気が高いです。キャッシュの無効化さえ面倒でなければ…
逆スプーラー - 両方のいいとこ取り?
ここで、。 なぜ両方のいいとこ取りをできないのかという質問が生じます。スプーラーパターンを適切にカプセル化し、リソースベースの URL のキャッシュプロパティを正しくカプセル化することです。
実際のプロジェクトでは見たことがないと認めざるを得ません。ただし、とにかくここで、独自の解決策の出発点として、少し思考実験をしてみましょう。
このパターンを 逆スプーラー と呼びます… すべての優れたキャッシュ無効化プロパティを持つには、逆スプーラーが画像リソースに基づいている必要があります。
ただし、パラメーターを公開しないようにする必要があります。すべてのプロパティは、コンポーネント内でカプセル化する必要があります。ただし、コンポーネントのパスを、プロパティへの不透明な参照として公開できます。
次のように、フォーム内の 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
最終更新日より古い場合、無効とみなされます。
jcr:lastModified
の日付ではありません。statfile の最終変更日(.stat
)は、AEM からの無効化リクエストが Dispatcher で受け取られた日付です。
複数の Dispatcher がある場合は、異常な結果が生じる可能性があります。お使いのブラウザーには、より新しいバージョンの Dispatcher がインストールされている場合があります(複数の Dispatcher がある場合)。または、Dispatcher は、他の Dispatcher によって発行されたブラウザーのバージョンが古いと考え、不必要に新しいコピーを送信する可能性があります。これらの効果は、パフォーマンスや機能要件に大きな影響を与えません。また、ブラウザーが最新バージョンの場合は、時間の経過と共にレベルアウトされます。ただし、ブラウザーのキャッシュ動作を最適化およびデバッグする際に、少し混乱を招くこともありますので、。 その点に留意してください。
/statfilelevel を使用した無効化ドメインの設定
自動無効化と statfile について紹介した際、変更が行われると、すべての ファイルは無効とみなされ、いずれにしてもすべてのファイルは相互依存していると説明しましたが、。
正確には異なります。通常、共通のメインナビゲーションルートを共有するすべてのファイルは相互に依存します。しかし、1 つの AEM インスタンスは、多数の 独立した web サイトをホストできます。共通のナビゲーションは共有しません(実際には、何も共有しません)。
サイト A に変更があるので、サイト B を無効にするのは無駄ではないでしょうか。それはそうですし、。 別の方法を使用することもできます。
Dispatcher は、サイトを相互に分離するシンプルな方法(statfiles-level
)を提供します。
これは、ファイルシステムのどのレベルから、2 つのサブツリーが「独立」と見なされるかを定義する数値です。
statfilelevel が 0 のデフォルトのケースを見てみましょう。
/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 はブランドごとに無効化ドメインを作成し、2 は国ごと、3 は言語ごとになるでしょう。
均質なサイト構造の必要性
statfilelevel は、設定内のすべてのサイトに等しく適用されます。したがって、すべてのサイトが同じ構造に従い、同じレベルで開始する必要があります。
ポートフォリオに、いくつかの小さな市場でのみ販売されているブランドと、世界中で販売されているブランドがあるとします。小さな市場は 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
言語サイト間にナビゲーション可能なリンクがなく、明らかなインクルージョンもなかったので、statfilelevel を「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 つは既にご存じでしょう。statfilelevel を 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 は両方とも、キューによってオーサーから切り離されているので、タイミングが適切でないことがあります。Publish システムでコンテンツが更新される前に、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 つ目は、サーバーが設定した有効期限により保証されます。リソースが期限切れでない場合は、サーバーに再度問い合わせる必要はありません。
ドキュメントの有効期限に達した場合でも、まだ新しいものである可能性があります。有効期限は、ドキュメントが配信される際に設定されます。しかし、多くの場合、新しいコンテンツがいつ利用可能になるかは事前にわからないため、これは控えめな見積もりにすぎません。
ブラウザーのキャッシュ内のドキュメントが、新しいリクエストで配信されるドキュメントと同じかどうかを判断するには、ブラウザーはドキュメントの Last-Modified
日付を使用できます。ブラウザーがサーバーに次のように要求します。
「に 6月10日のバージョンがありますが、更新が必要ですか?」そして、サーバーは次のように応答することができます
リソースを再送せずに「304 - そのバージョンは最新です」と応答するか、
HTTP ヘッダー内に「200 - こちらが最新バージョンです」、および HTTP 本文内のコンテンツは実際の最新のものですと、応答することがあります。
2 番目を機能させるには、Last-Modified
の日付をブラウザーに送信して、更新をリクエストするための参照ポイントをブラウザーに提供してください。
前に、Last-Modified
の日付が Dispatcher によって生成される場合、キャッシュされたファイルとその日付は、ブラウザーによってファイルがリクエストされたときに生成されるため、リクエストごとに異なる可能性があることを説明しました。代わりに、「ETag」を使用します。これらは、日付の代わりに実際のコンテンツを識別する数値(ハッシュコードの生成など)です。
ACS Commons パッケージ の「ETag サポート」ではこのアプローチを使用しています。ただし、これには代償が伴います。ETag はヘッダーとして送信する必要がありますが、ハッシュコードの計算には応答を完全に読み取る必要があるため、応答は配信する前にメインメモリに完全にバッファリングする必要があります。Web サイトでキャッシュされていないリソースが存在する可能性が高い場合、これは待ち時間に悪影響を与える可能性があります。もちろん AEM システムによって消費されるメモリに注意する必要があります。
URL フィンガープリントを使用する場合、非常に長い有効期限を設定できます。フィンガープリントされたリソースは、ブラウザーでいつでもキャッシュできます。新しいバージョンは新しい URL でマークされますが、古いバージョンは更新する必要はありません。
スプーラーのパターンを導入した際に URL フィンガープリントを使用しました。/etc/design
からの静的ファイル(CSS、JS)が変わることはほとんどなく、フィンガープリントとして使用するのに適した候補です。
通常のファイルでは、30 分ごとにHTMLを再確認し、4 時間ごとに画像を再確認するなど、通常は固定スキームを設定します。
ブラウザーのキャッシュは、オーサーシステムで非常に役立ちます。ブラウザーで可能な限りキャッシュして、編集操作を強化したい場合です。残念ながら、最もコストがかかるアセットである HTML のページは、オーサーで頻繁に変更され、キャッシュすることはできません。
AEM の UI を構成する Granite ライブラリは、かなりの期間キャッシュすることができます。また、サイトの静的ファイル(フォント、CSS、JavaScript)をブラウザーにキャッシュすることもできます。/content/dam
の画像も、通常は、ページ上のテキストのコピーと同じ頻度では変更されないので、15 分間キャッシュできます。画像は、AEM ではインタラクティブに編集されません。AEM にアップロードする前に、まずレポートの編集と承認を行う必要があります。したがって、テキストほど頻繁には変更されないことが想定されます。
UI ファイルのキャッシュ、サイトライブラリファイルおよび画像を使用すると、編集モードでのページの再読み込みを大幅に高速化できます。
参照
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
といった URL を使用するには、AEM 上にそのマッピングを実装する必要があります。AEM では、切り捨てられた形式に従ってリンクをレンダリングをする方法を認識しておく必要があるためです。
ただし、AEM のみに依存しないでください。その場合、キャッシュのルートディレクトリに /home.html
のようなパスが保存されます。これは、フィンランド語やドイツ語の「ホーム」、それともカナダの Web サイトのどれを表すでしょうか。また、Dispatcher に /home.html
のファイルがある場合、/content/brand/fi/fi/home
の無効化リクエストを受け取ったときにこのファイルを無効化する必要があることを、Dispatcher はどのように認識するのでしょうか。
ここまで、ドメインごとに別々の docroot を持つプロジェクトを見てきました。デバッグと保守は大変で、私たちも実際に完璧に動作するケースに遭遇したことはありません。
キャッシュを再構築することで、問題を解決できました。すべてのドメインに対して 1 つの 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 でした。検索エンジン最適化では、「メイン」の URL を整えるだけで十分だと考えています。
1 つの共通の docroot にも、良い機能がありました。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 つのオプションがあります。
-
リクエストがキャッシュに到達する前に、Dispatcher の前の SSO(シングルサインオン)ゲートウェイによって、または Apache サーバーのモジュールとしてリソースを保護する。
-
機密リソースをキャッシュから除外し、常に Publish システムからライブで提供する。
-
Dispatcher で権限に影響を受けるキャッシュを使用する。
もちろん、3 つのアプローチを自分で組み合わせ、適用することもできます。
オプション 1。 「SSO」ゲートウェイは、組織によって強制される場合があります。アクセススキームの粒度が非常に粗い場合は、リソースへのアクセスを許可するか拒否するかを決定するために AEM からの情報が必要ない場合があります。
オプション 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 分などに)設定にしたい誘惑にかられる可能性があります。
gracePeriod
はこのようには機能しません。猶予期間は、ドキュメントの無効化が保証されるまでの期間では_なく_、無効化が起こらない時間枠です。このフレーム内で後続の無効化が行われると、その期間が_延長_されます。この期間は無制限に長くなる可能性があります。例を使用して、gracePeriod
が実際にどのように動作するかを説明してみましょう。
例えば、メディアサイトを運営しているユーザーが、編集スタッフが 5 分ごとに定期的にコンテンツを更新するとします。gracePeriod を 5 分に設定したとします。
12:00 に始まる簡単な例を示します。
12:00 - Statfile が 12:00 に設定されています。キャッシュされたすべてのファイルは、12:05 まで有効と見なされます。
12:01 - 無効化が発生します。これにより猶予時間が 12:06 に延長されます。
12:05 - 別の編集者が記事を公開します。猶予時間がさらに gracePeriod だけ延長されて 12:10 になります。
これが繰り返され、コンテンツは決して無効化されません。gracePeriod 内 の各無効化により、実質的に猶予時間が延長されます。gracePeriod
は無効化の嵐を乗り切るように設計されていますが、最終的には無視できなくなります。そのため、永遠に待ち続けなくてもよいよう、gracePeriod
はかなり短く設定してください。
決定論的猶予期間
無効化の嵐を乗り越えるための別のアイデアをご紹介します。ただのアイデアです。実稼動環境では試していませんが、アイデアを共有するのに十分な面白いコンセプトが見つかりました。
通常のレプリケーション間隔が gracePeriod
よりも短い場合、gracePeriod
が予想外に長くなる可能性があります。
別の考え方は、一定の間隔でのみ無効にすることです。この間の時間は常に古いコンテンツを提供することを意味します。無効化は最終的に発生しますが、多数の無効化が 1 つの「一括」無効化に収集されるため、Dispatcher はその間にキャッシュされたコンテンツを提供し、パブリッシュシステムが準備機会を得ることができます。
実装は次のようになります。
無効化の発生後に実行される「カスタム無効化スクリプト」(参照)を使用します。このスクリプトは、statfile's
ファイルの最終変更日を読み取り、次の間隔停止まで切り上げます。Unix シェルコマンド touch --time
で時刻を指定します。
例えば、猶予期間を 30 秒に設定した場合、Dispatcher は statfile の最終変更日を次の 30 秒に丸めます。その間に発生する無効化リクエストは、同じ次の 30 秒間を設定するだけです。
無効化を次の 30 秒まで延期するとヒット率が増加する。
無効化リクエストと次のラウンドの 30 秒スロットの間に発生したキャッシュヒットは古いとみなされます。パブリッシュには更新がありましたが、Dispatcher は依然として古いコンテンツを提供します。
この方法は、後続のリクエストによって期間が決定論的に長くなるのを恐れることなく、より長い猶予期間を定義するのに役立ちます。前に述べたように、単なるアイデアに過ぎず、テストする機会がありませんでした。
参照
自動再取得
サイトには特定のアクセスパターンがあります。高い負荷の着信トラフィックがあり、トラフィックの大部分はページのごく一部に集中しています。トラフィックの 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 のキャッシュディレクトリを調べると、タイムスタンプが付いた一時ファイルが表示されます。これらは、現在バックグラウンドで読み込まれているファイルです。
参照
パブリッシュシステムのシールド
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 の代わりとなるものではないことに注意してください。 これらの技術を「貧乏人のファイアウォール」と呼ぶ人もいますが、これには理由があります。
参照
正規表現と 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 つのグループに追加する場合や、または新しいグループを作成する場合があります。この場合、名前の変更や再番号付けを行う項目の数は、そのグループに制限されます。
dispatcher.any
設定ファイルに含まれる多数のファイルに分割します。ただし、新しいファイルでは新しい名前空間は導入されません。そのため、あるファイルに「001」というルールがあり、別のファイルにも「001」というルールがある場合、エラーが発生します。強い意味を持つ名前を使用する理由は他にもあります。参照
プロトコルの仕様
最後のヒントは実際にはヒントと言えるものではありませんが、お客様に伝える価値はあると思われます。
ほとんどの場合、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