トップ  > メモ一覧  > カテゴリ「ルーティング」の絞り込み結果 : 4件

4件中 1 〜 4 表示  1 

No.1353 symfonyのルーティングでメモリが肥大化する問題と対処法

symfonyのルーティングでメモリが肥大化する問題と対処法Add Star


こんにちは。小川です。
先日、symfony(v1.2.7)で本番(prod)環境に設定した場合に開発(dev)環境の数倍のメモリが消費されるという状況に陥ってしまいました。原因を追及した結果、ルーティングの設定に問題があることが発覚したので、今日はそのことを書こうと思います。

原因先には述べてあるとおり、ルーティングの設定に問題がありました。symfony1.2ではルーティングのキャッシュということを行っており、そのキャッシュが肥大化してメモリを大量に消費する原因となっていました。
対策としてルーティングのキャッシュを無効にしてキャッシュファイルの読み書きを行わないように設定ファイルを修正したところ、上記の問題は無事に解決しました。

というわけで今回は、

1. ルーティングのキャッシュの仕組み
2. なぜそんなにもキャッシュが肥大化してしまったのか
3. ルーティングのキャッシュを無効にする方法、その他対策

上記の3つをテーマにお話しさせていただきます。

symfony 1.2のルーティングについては以前書いた「symfony 1.2のルーティングまとめ」という記事をご覧ください。
symfonyでは通常、 /モジュール名/アクション名(/パラメータ) という形式のURLをとりますが、このURLとモジュールおよびアクションを結びつける仕組みをルーティングと呼んでいます。例えば、/article /newというURLはarticleモジュールのnewアクション、といったものです。

そしてそれぞれのルーティングには名前がついています。/モジュール名/アクション名(/パラメータ) という形式はdefaultという名前がつけられています。これ以外にもホームページにあたるhomepageや各モジュールのインデックスにあたる default_indexがデフォルトで定義されています。

実際に symfony generate:app コマンドでアプリケーションを作った際に、routing.ymlには以下のような定義がされています。

  1. homepage:
  2.   url:   /
  3.   param: { module: default, action: index }
  4.  
  5. default_index:
  6.   url:   /:module
  7.   param: { action: index }
  8.  
  9. default:
  10.   url:   /:module/:action/*

:(コロン)ではじまる項目はリクエストパラメータとして扱われます。先ほど例に挙げた/article/newというURLはdefaultにマッチ し、$request->getParameter('module')としてリクエストオブジェクトからパラメータを取得すればarticleと いう文字列が取れます。moduleとactionは特別なパラメータで、名前の通りモジュール名とアクション名を特定するための値です。
またhomepageやdefault_indexでparamという項目の中でも同じようにmoduleやactionが指定されていますが、これはURLがマッチした際にURLに含まれていなくてもリクエストパラメータとして指定するというものです。

*(アスタリスク)は任意のリクエストパラメータを/(スラッシュ)区切りで記述可能にするもので、 /article/show/year/2009/month/08/day/20 というURLでアクセスが来た場合にarticleモジュールのshowアクションが呼び出され、リクエストオブジェクトからはそれぞれyearが 2009, monthが08, dayが20のように取得することができます。/article/show?year=2009&month=08&day=20 と同じようなものですが、スラッシュ区切りで来ている場合は$_GETには直接はいらず、symfonyが内部でリクエストに定義するということを行うと いう違いがあります。

もし /article/2009/08/20 というURLでarticleモジュールのshowアクションに行くようにし、2009, 08, 20という値をそれぞれyear, month, dayという名前のリクエストパラメータとして指定したい場合は以下のようにrouting.ymlに定義します。

  1. article_show_at_date:
  2.   url:   /article/:year/:month/:day
  3.   param: { action: show }

これでarticle_show_at_dateがルーティングに定義されます。このルーティングをアプリケーション内から呼び出すときにはlink_toやurl_forといったヘルパーや、アクションのredirectメソッドがよく知られていると思います。
例えばurl_forであれば url_for('@article_show_at_date?year=2009&month=08&day=20') のように指定します。実際にurl_forやredirectがURLのパースを行う処理はsfWebControllerのgenUrlメソッドが呼び 出されており、更にその中でsfPatternRoutingクラスのgenerateメソッドを呼び出しており、パース処理が行われています。

このパース処理ですが、routing.ymlを展開した後、各ルーティングを分解して正規表現に変換して1つ1つマッチングを行うなどの処理をして、最終的にURLに変換を行うという流れになります。
この @article_show_at_date?year=2009&month=08&day=20 を /article/2009/08/20 に変換する処理をキャッシュすることがルーティングのキャッシュになります。実装方法は単純で、前者の内部的なURLにコンテキストの情報を付与したもの をキーにしてキャッシュを作成する実装になっています。

キャッシュのキーを具体的に生成してみます。

  1. // ルーティングの名前
  2. string 'article_show_at_date' (length=20)
  3.  
  4. // ルーティングのパラメータ
  5. array
  6.   'module' => string 'default' (length=7)
  7.   'action' => string 'index' (length=5)
  8.   'sf_culture' => string 'en' (length=2)
  9.   'year' => string '2009' (length=4)
  10.   'month' => string '08' (length=2)
  11.   'day' => string '20' (length=2)
  12.  
  13. // コンテキストの情報
  14. array
  15.   'path_info' => string '/' (length=1)
  16.   'prefix' => string '/frontend_dev.php' (length=17)
  17.   'method' => string 'GET' (length=3)
  18.   'format' => null
  19.   'host' => string 'sf-lab.fivestar.localhost' (length=26)
  20.   'is_secure' => boolean false
  21.   'request_uri' => string 'http://sf-lab.fivestar.localhost/frontend_dev.php' (length=50)

内部的に上記の3つの情報をシリアライズしてキーとしています。デフォルトの設定ではこのキーと値(URL)を単一の連想配列にセットして、1つの大きな ルーティング情報を保持している配列を作るということを行っています。この配列はシリアライズしてファイルに書き込まれ、 sfPatternRoutingが生成されるときに読み込まれてまた配列として定義される仕組みになっています。

問題はこの配列がどうして肥大化してしまったのかですが、キャッシュのキーにヒントがあります。実は今回開発していたものはモバイル用のサイトで、コンテ キストのrequest_uriにセッションIDがくっついてくる仕様になっており、異なるセッションIDでリクエストがくるたびにルーティングのキャッ シュを作り直すということが起きていました。セッションIDが変わった瞬間に作成していたルーティングのキャッシュが無意味になるどころか、無駄なルー ティングがものすごい勢いで増え続けるというかなり危険な状態ですね。

一応キャッシュには有効時間が設定されており、デフォルトでは31,556,926秒なのでおおよそ365日、つまり1年というまるであってないような状 態でもありました。これを短く設定するという方法もありましたが、そもそも定義してあるルーティングも大した量はなくそこまで時間がかかるわけでもなく、 アクションキャッシュなどでもある程度代替できるのでキャッシュを無効にして対応することにしました。

ルーティングのキャッシュを無効にする方法はアプリケーションのfactories.ymlで設定することができます。factories.ymlはコン トローラやリクエスト、ストレージなどのクラスの指定やクラスのコンストラクタに渡すパラメータなどを設定するファイルです。細かい設定などはThe Symfony Reference Bookをご確認ください。

このfactories.ymlを読み込むためのsfFactoryConfigHandlerというクラスがあり、このクラスを見てみると if (isset($parameters['cache'])) がtrueであればキャッシュオブジェクトを作成するということを行っています。ここで生成したキャッシュクラスがsfPatternRoutingに セットされることでキャッシュが有効になるという仕組みでした。デフォルトではsfFileCacheクラスがルーティングのキャッシュを行うクラスとし て設定されているようですが、次のように記述を行うことでキャッシュが無効になります。

  1. all:
  2.   routing:
  3.     class: sfPatternRouting
  4.     param:
  5.       # 他の設定
  6.       cache: ~

~(チルダ)はnullと同じ意味を持っているため、routingのparamという項目の中でcacheをnullにすると先ほどのissetがfalseになるため、キャッシュが無効に設定されます。
これでsymfony cache:clearコマンドを実行すると作られていたキャッシュも削除され、設定も反映されます。設定された後にメモリ使用量などを確認したところ正常な数値で稼働しているのが確認できました。


メモリの原因がルーティングのキャッシュにあることは当初全然見当もついておらず、ここまで内部を追っていくのは中々大変な作業でした。
メモリ使用量についてはWebデバッグツールバーには表示されますが本番環境で表示するわけにも行かず、memory_get_peak_usage関数などログに書き出すなどして場所の特定をしていきました。
特定するにあたって、まずはフロントコントローラ(web/index.phpなど)の処理をsfApplicationConfigurationオブ ジェクトの生成、sfContextの生成、dispatchの大きく3つにわけてメモリ使用量をみてみたところ、sfContextの生成時にメモリを 消費しているようです。

sfContextの生成はsfContext::createInstanceメソッドで行われており、さらに中を見ていくとfactories.ymlを読み込む処理でメモリを大量に消費しているというまで判明しました。
このとき少しはまったところがあり、sfContextは通常symfonyが入っているディレクトリのutilディレクトリ内にあるのですが、本番環境 ではsfContextなどのsymfonyのコアクラスをconfig_core_compile.yml.phpという名前でキャッシュされているた め、キャッシュファイルの方を修正する必要があります。

sfPatternRouingなども同様にconfig_core_compile.yml.phpに入っているため、このファイル内で徐々に処理を追っていった結果、最終的にルーティングのキャッシュに原因があるというところに到達することができました。
ちなみに開発環境では本番と同様にキャッシュクラス自体は生成されているのですが、デバッグモードを有効にしているため特にルーティングのキャッシュは行わないようになっています。


ルーティングのキャッシュはかなり盲点だったのですが、デフォルトで有効になっているためアプリケーションによっては今回のように知らず知らずのうちにメモリを大量に消費するという問題に陥ってしまうのではないかと感じました。
キャッシュの設定自体はファイルの他にもsfAPCCacheやsfMemcacheCacheなども利用できますし、ルーティングで lookup_cache_dedicated_keysというパラメータをtrueに設定すると複数のルーティングキャッシュを生成するようになるの で、アプリケーションによって適切な設定は変わってくると思います。

今回はルーティングのキャッシュについて知識がなかったので結果として悪い方向に働いてしまいましたが、チューニングの材料としてはとても有効だと思います。
symfonyは全体的にキャッシュに頼らないとパフォーマンスが出なかったりしますが、キャッシュを適切に設定してあげると目に見えて高速になるのでキャッシュは是非とも理解していきたいです。

引用元

更新:2009/08/21 09:18 カテゴリ: symfony  > ルーティング ▲トップ

No.1055 「default」を指定すべからず

# apps/frontend/config/routing.yml
job:
  class:   sfDoctrineRouteCollection
  options: { model: JobeetJob }
 
job_show_user:
url: /job/:company_slug/:location_slug/:id/:position_slug
class: sfDoctrineRoute
options: { model: JobeetJob, type: object }
param: { module: job, action: show }
requirements:
id: \d+
sf_method: [get]
  # default rules homepage: url: / param: { module: job, action: index }   default_index: url: /:module param: { action: index }   default:
url: /:module/:action/*

※「default:」の記述があるため、「job_show_user:」が半分意味を成さなくなる
※「default」を指定すべからず!

更新:2009/06/17 09:57 カテゴリ: symfony  > ルーティング ▲トップ

No.925 ルーティングを便利に使おう

www.slideshare.net/ebihara/ss-1447653

引用元

えび・symfony勉強会より
更新:2009/05/18 09:40 カテゴリ: symfony  > ルーティング ▲トップ

No.921 設定ファイルの記述とテンプレート内の記述

◆基本
url_for('job/show?id='.$job->getId())
url_for('@default?module=job&action=show&id='.$job->getId())


◆発展
----------------------------------------------------------------
job_show_user:
  url:   /job/:company/:location/:id/:position
  param: { module: job, action: show }
----------------------------------------------------------------
url_for('job/show?id='.$job->getId().'&company='.$job->getCompany().
  '&location='.$job->getLocation().'&position='.$job->getPosition())
url_for(array(
  'module'   => 'job',
  'action'   => 'show',
  'id'       => $job->getId(),
  'company'  => $job->getCompany(),
  'location' => $job->getLocation(),
  'position' => $job->getPosition(),
))


◆sfDoctrineRouteを使う
----------------------------------------------------------------
job_show_user:
  url:     /job/:company_slug/:location_slug/:id/:position_slug
  class:   sfDoctrineRoute
  options: { model: JobeetJob, type: object }
  param:   { module: job, action: show }
  requirements:
    id: \d+
    sf_method: [get]
----------------------------------------------------------------
url_for(array('sf_route' => 'job_show_user', 'sf_subject' => $job))
url_for('job_show_user', $job)
url_for('job_show_user', $job, true);
link_to($job->getPosition(), 'job_show_user', $job, true);

◆アクション内では
$this->redirect($this->generateUrl('job_show_user', $job));

引用元

更新:2009/05/17 23:13 カテゴリ: symfony  > ルーティング ▲トップ
4件中 1 〜 4 表示  1 

FuelPHP

Mac

web開発

プロマネ

マネタイズ

プレゼン

webサービス運用

webサービス

Linux

サーバ管理

MySQL

ソース・開発

svn・git

PHP

HTML・CSS

JavaScript

ツール, ライブラリ

ビジネス

テンプレート

負荷・チューニング

Windows

メール

メール・手紙文例

CodeIgniter

オブジェクト指向

UI・フロントエンド

cloud

マークアップ・テキスト

Flash

デザイン

DBその他

Ruby

PostgreSQL

ユーティリティ・ソフト

Firefox

ハードウェア

Google

symfony

OpenPNE全般

OpenPNE2

Hack(賢コツ)

OpenPNE3

リンク

個人開発

その他

未確認

KVS

ubuntu

Android

負荷試験

オープンソース

社会

便利ツール

マネー

Twig

食品宅配

WEB設計

オーディオ

一般常識

アプリ開発

サイトマップ

うずら技術ブログ

たませんSNS

rss2.0