リソースのバージョン管理によるHTTPサーバーの最適化。 実装機能

  1. 最適化の本質
  2. ページの読み込みと強制更新
  3. 自動化が必要
  4. サーバー側の実装
  5. サーバー側の最適化
  6. Googleアプリエンジンを搭載
  7. ソースコード
  8. まとめ




Google App Engine / Pythonの実装例を検討します。





最適化の本質



Yahooのエンジニアは有名な記事で、ファイルのバージョン管理を通じてHTTP処理を最適化するための興味深い手法について書いています。 その本質は...通常、彼らは単にHTMLで書く:



 < img src="image.jpg" >
      
      







キャッシュでimage.jpgを取得した後、ブラウザがHTMLを再度読み取り、同じ画像へのリンクを再び見つけた後。 一般に、ブラウザはサーバー上で更新されたかどうかを個別に理解できないため、サーバーにリクエストを送信する必要があります。



不要なリクエストを避けるために、アドレスでリソースのバージョンを指定して、アドレスを一意にすることができます。



 < img src="image.v250.jpg" >
      
      







したがって、ブラウザは、ファイルバージョン250が今後変更されないこと、および251番も変更されないことを確認できます。 また、No。250がキャッシュにある場合は、サーバーに何の質問もせずに使用できます。 2つのHTTPヘッダーは、ブラウザーにこの自信を与えるのに役立ちます。



 //  ,     Expires: Fri, 30 Oct 2050 14:19:41 GMT //       Cache-Control: max-age=12345678, public
      
      







したがって、ページを何度も表示するには、HTMLをダウンロードするだけで済み、多くのリソースにアクセスする必要はありません。



ページの読み込みと 強制リフレッシュ



現在の形式では、この最適化は次のリンクとCtrl + L、Enterで機能します。 しかし、ユーザーがF5で現在のページを更新すると、ブラウザーはリソースが「邪魔しない」と表示していることを忘れ、「余分な」要求がリソースごとにサーバーに送信されます。 ブラウザーのこの動作を変更することはできませんが、プログラム全体に応じて毎回ファイルを配布することはできませんが、可能であれば「何も変更していないのでキャッシュから取得してください」と答えて追加のロジックを導入します。



ブラウザが「image.v250.jpg」を要求するとき、キャッシュにコピーがある場合、ブラウザはヘッダー「If-Modified-Since:Fri、01 Jan 1990 00:00:00 GMT」を送信します。 初めてこの画像にアクセスしたブラウザは、このようなヘッダーを送信しませ 。 したがって、北は最初に「何も変わっていない」と言って、正直に写真を2番目に与えるべきです。 具体的には、この場合、日付は分析できません-キャッシュ内の画像の存在の事実が重要であり、画像はそこで正しいです(ファイルと一意のURLのバージョン管理のため)。



しかし、そのように、「If-Modified-Since」ヘッダーは、画像がキャッシュ内にある場合でもサーバーに届きません。 ブラウザにこのヘッダーを強制的に送信させるには、(時系列的に)前の回答で、ヘッダー「Last-Modified:Fri、01 Jan 1990 00:00:00 GMT」を指定する必要がありました。 実際には、これはサーバーが常にこのヘッダーを提供することを意味します。 最後のファイル変更の正直な日付を指定することも、過去の日付を指定することもできます。同じ日付がサーバーに戻りますが、結局のところ、それは特に重要ではありません。



実際、この段落で説明する最適化はYahooと直接的な関係はありませんが、不必要なワークロードを回避するために組み合わせて使用​​する必要があります。 そうでない場合、効果は不完全になります。



自動化が必要



この手法は悪くありませんが、実際にはファイルのバージョンを手動で配置することはできません。 GAE / djangoでは、カスタムタグによって問題が解決されます。 コードはテンプレートに記述されています。



 < img src="{% static 'image.jpg' %}" >
      
      







HTMLへの変換:



 < img src="/never-expire/12345678/image.jpg" >
      
      







そして、このようなタグの実装は次のとおりです。



 def static(path): return StaticFilesInfo.get_versioned_resource_path(path) register.simple_tag(static)
      
      







サーバー側の実装



基本的に、この最適化は、静的ファイル(画像、CSS、JavaScript)の処理に便利です。 ただし、App Engineは静的として指定されたファイルを処理し(あまり効率的ではありません 、HTTPヘッダーの変更を許可しません。 したがって、標準の「静的」ディレクトリに加えて、「never-expire」という別のディレクトリが表示されます。



まず、GET要求ハンドラーは、要求されたファイルのバージョンが最新のものと一致することを確認します。 一致しない場合、注文のために新しいアドレスにリダイレクトします。



 # Some previous version of resource requested - redirect to the right version correct_path = StaticFilesInfo.get_resource_path(resource) if self.request.path != correct_path: self.redirect(correct_path) return
      
      







次に、応答ヘッダーを設定します。

-ファイル拡張子に応じたコンテンツタイプ

-すでに説明したように、期限切れ、キャッシュ制御、最終変更。



要求にIf-Modified-Sinceヘッダーが含まれている場合、何もせずにコードを304に設定します-リソースは変更されていません。 それ以外の場合、ファイルの内容は応答本文にコピーされます。



 if 'If-Modified-Since' in self.request.headers: # This flag means the client has its own copy of the resource # and we may not return it. We won't. # Just set the response code to Not Changed. self.response.set_status(304) else: time.sleep(1) # todo: just making resource loading process noticeable abs_file = os.path.join(os.path.split(__file__)[0], WHERE_STATIC_FILES_ARE_STORED, resource) transmit_file(abs_file, self.response.out)
      
      







おそらく、GAEのデータベースがファイルシステムよりも高速である場合、ファイルの内容をデータベースにコピーしてからそこに移動するようにファイルを最初に要求する価値があります。 質問は私に開かれています。



サーバー側の最適化



ファイルのバージョンとして、VCSのバージョンとファイルの最終更新時刻の両方を使用できます-基本的な違いはありません。 私は2番目を選択しましたが、より簡単です:



 os.path.getmtime(file)
      
      







ただし、各要求に対するファイルシステムのポーリングはあまり良くないようです-I / Oは常に遅いです。 したがって、最初の要求で(すべての)静的ファイルの現在のバージョンに関する情報を収集し、その情報をmemcacheに入れることができます。 出力は次のようなハッシュです。



 { 'cover.jpg': 123456, 'style.css': 234567 }
      
      







最新バージョンを見つけるためにカスタムタグで使用されます。 当然、memcacheに問題が発生した場合に備えて、シングルトンのようなものが必要です。



 class StaticFilesInfo(): @classmethod def __get_static_files_info(cls): info = memcache.get(cls.__name__) if info is None: info = cls.__grab_info() time = MEMCACHE_TIME_PRODUCTION if is_production() else MEMCACHE_TIME_DEV_SERVER memcache.set(cls.__name__, info, time) return info @classmethod def __grab_info(cls): """ Obtain info about all files in managed 'static' directory. This is done rarely. """ dir = os.path.join(os.path.split(__file__)[0], WHERE_STATIC_FILES_ARE_STORED) hash = {} for file in os.listdir(dir): abs_file = os.path.join(dir, file) hash[file] = int(os.path.getmtime(abs_file)) return hash
      
      







Google App Engineの機能



すべての静的ファイルに関する情報を収集できますが、デザイナーが画像を変更した場合はどうなりますか? サーバーは、ファイルのキャッシュバージョンを更新する時間をどのように認識しますか? 一般的な場合、私は本当に想像していません-ファイルシステムへの変更をリッスンするデーモンを起動するか、展開後にスクリプトを実行することを忘れないでください。



ただし、App Engineは特殊なケースです。 このシステムでは、開発はローカルマシンで実行され、その後、完成したコード(および静的ファイル)がサーバーにデプロイ(デプロイ)されます。 そして、重要なことは、サーバー上のファイルを変更できないことです(次の展開まで)。 つまり、バージョンを一度だけ読むだけで十分であり、バージョンの変更を気にする必要がなくなります。



唯一のことは、ローカル開発ではファイルが大幅に変更される可能性があることです。この場合、別の方法で行動しないと、ブラウザは例えば開発者に古いバージョンの画像を表示し、不便です。 ただし、この場合、パフォーマンスはそれほど重要ではないため、データをmemcacheに数秒間置くことも、まったく入れないこともできます。



完成したサンプルのソースコード



code.google.com/p/investigations/source/browse/#svn%2Ftrunk%2Fnever-expire-http-resources



svn checkout surveys.googlecode.com/svn/trunk/never-expire-http-resources surveys



まとめ



私はまだappspotにアップロードしていませんが、ローカルですべてが機能し、飛びます。 人々は、クライアントとサーバーの最適化を利用して、愚かにも200 OKとは答えません:)



UPD。 コメントでは、静的ファイルについても標準の静的ファイルで同じ効果が得られると書いています(そして確認します)。 つまり、このような「手動」コードは静的処理にはほとんど適していません。GAEはこれをより適切に処理できます。 ただし、このアプローチは動的に作成されたリソースの処理には役立つ場合があります 。 このコンテキストでは、実装のためにETagがLast-Modifiedよりも便利な場合があります。



All Articles