前 [第八章:Ploneを拡張する] | 目次 | 次 [第十章:構造化テキスト]

第九章:Ploneの最適化

この章はより高い性能を引き出すためにPloneを最適化する方法を説明します。大部分のこれらのノウハウはサイトと結びつけることができます。この章は技術的なユーザと開発者のためのものです。

デバッグモード

これは自明なことですが、Ploneをデバッグモードで走らせることは多大なる性能の犠牲を伴います。特にWindowsでは顕著です。Ploneのページにアクセスするとき、デバッグモードで走らせていたら、Ploneはスキンディレクトリの中のイメージ、スクリプト、テンプレートが変更されたかどうかをひとつひとつをすべてチェックします。この影響はすべてのオペレーティングシステムで起きますが、特にWindowsでは著しく悪く起こり、ページの生成は5倍から10倍遅くなります。

Zopeがデバッグモードで走っている時、Refreshプロダクトもコードが変更されたかどうかを見るために各プロダクトをチェックします。これもさらに性能(スピード)を悪化させます。Refreshに関するより詳しい情報はZopeのドキュメンテーションを見てください。

デバッグモードは起動の時に -D というコマンドラインスイッチで有効になったり無効になったりします。ディフォルトではインストーラはデバッグモードでは走りません。より詳しい情報は Chapter 3 of the Zope Book を見てください

簡単な最適化

Credit: Shane Hathaway and Sidnei de Silva

Pythonを -O オプションで起動させると少し最適化されます。生成されるバイトコードの最適化によって大体それは5〜8%の速度の向上になります。

Zopeを -t (使用するスレッドの数)を使って起動すると非常に高いトラフィックのサイトでは多少性能が向上します。ですがメモリーの消費量も上がります。それは大きなサイトでも3スレッドあれば十分なように思えます。Zopeはマルチスレッドで動きますが、ほとんどの入出力依存型の処理ではスレッドを使い切ることがないことを思い出してください。ZopeはSquidのような非対称型のアプリケーションでもあります。

キャッシュサイズの設定を監視します(下記参照)。もしあなたが過大な量のオブジェクトのロードを見ているなら、キャッシュサイズを増やします。ZODBの活動量を測るのに、アクティビティグラフを使います。典型的には1時間で1000以上かそのぐらいのロードを継続的に欲しくはないでしょう。注)plone.orgでは時々30分で5000ロードを記録します。。。ですがこれはまれです。通常はたったの10〜100オブジェクトロードしかありません。

インタープリタで割り込みが発生するまでに処理されるバイトコードの数をsyscheckintervalの中で編集ができます。このシステム属性は最近のバージョンのZopeでは$ZOPE/z2.py の中で500に設定されています。しかし、私はPloneとZPTは平均的なZopeより多くのプロセスを実行しているとこっそり疑っています。現在はPlone.orgではこれを750にしています。

静的なコンテンツのためにApacheのmod_proxyキャッシュを使う

Credit: Alan Runyan, Alexander Limi, Jテクrg Vidar Bryne

静的なコンテンツをApacheから提供するようにすることでPloneの性能は劇的に増大します。Ploneは高速で強力なWebサーバとしてZopeを使っているとはいえ、Apache内にコンテンツをキャッシュした時のような速度とスケーラビリティは全く望めません。plone.orgでは次のようなApacheの構成定義でこれを実現しています。

Apache は次のようにコンパイルされました。

            ./configure ¥

            "--prefix=/usr/local/apache" ¥

            "--with-layout=Apache" ¥

            "--enable-module=so" ¥

            "--enable-module=proxy"

mod_gzipの構成定義です。

            # http://www.remotecommunications.com/apache/mod_gzip/::

            # MOD_GZIP configuration (after loadmodule-part of httpd.conf)

            mod_gzip_on Yes

            mod_gzip_minimum_file_size  1002

            mod_gzip_maximum_file_size  0

            mod_gzip_maximum_inmem_size 60000

            mod_gzip_item_include mime "application/x-httpd-php"

            mod_gzip_item_include mime text/*

            mod_gzip_item_include mime "httpd/unix-directory"

            mod_gzip_dechunk Yes

            mod_gzip_temp_dir "/tmp"

            mod_gzip_keep_workfiles No

            mod_gzip_item_include file "¥.php3$"

            mod_gzip_item_include file "¥.txt$"

            mod_gzip_item_include file "¥.html$"

            mod_gzip_item_exclude file "¥.css$"

            mod_gzip_item_exclude file "¥.js$"

            #Logfile format adjusted to awstats (not implemented on plone.org, though)

            LogFormat "%h %l %u %t ¥"%r¥" %>s %b ¥"%{Referer}i¥" ¥"%{User-Agent}i¥"

            mod_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n

            Out:%{mod_gzip_output_size}n:%{mod_gzip_compression_ratio}npct."

            combined_gzip

バーチャルホストは次のとおりです。

            <VirtualHost *>

            # Virtual Host Monster handling

            ServerName plone.org

            ServerAlias www.plone.org

            ServerAdmin webmaster@plone.org

            ProxyPass / http://localhost:8080/VirtualHostBase/http/plone.org:80/plone.org/VirtualHostRoot/

            ProxyPassReverse / http://localhost:8080/VirtualHostBase/http/plone.org:80/plone.org/VirtualHostRoot/

            #

            # gzip handler

            #

            mod_gzip_item_include handler proxy-server

            mod_gzip_on Yes

            CacheRoot "/tmp/proxy/plone.org"

            CacheSize 5000

            CacheGcInterval 2

            CacheMaxExpire 24

            CacheLastModifiedFactor 0.1

            CacheDefaultExpire 1

            CacheDirLength 2

            #

            # Expire - by request

            #

             ExpiresActive On

             ExpiresByType image/gif A86400

             ExpiresByType image/png A86400

             ExpiresByType image/jpeg A86400

             ExpiresByType text/css A86400

             ExpiresByType text/javascript A86400

             ExpiresByType application/x-javascript A86400

            CustomLog /usr/local/apache/logs/plone-access_log combined

            ErrorLog /usr/local/apache/logs/plone-error_log

            </VirtualHost>

これだけが唯一のアプローチではありませんが、これは有効です。

参考資料: http://www.zope.org/Members/softsign/ZServer_and_Apache_mod_gzip

ZODBのキャッシュサイズを増やす

Credit: Alexander Limi, Shane Hathaway

Zopeのディフォルトでのデータベースキャッシュサイズはとても小さく定義されています。Ploneはキャッシュされるオブジェクトの数の増加によって多大な利益を得ます。ですから、以下の完全に痛みのない最適化を試してみてください。次のようにします。

ここで知っておいてほしいことは、Zopeの異なるエリアとはいえ、このフィールドの有用性に関しては多くの不一致な点があり、Shane HathawayによるとZopeのすべてのバージョンで機能するわけではないようです。控えめな性能の400MHzラップトップではPloneがとても速くよりきびきびとした感じになり、異なる世界を作り出しました。

スロットをキャッシュする

Credit: Alan Runyan

最近、plone.org ではRAMキャッシュマネージャ手を加えたところ、関係者がいうには速度の向上が著しいということです。これはZope/CMF/Ploneでのキャッシュの裏にあるアイデアへのよい概要であり、注意と plone.org で性能を顕著に上げるために私たちがどのようにしたかということです。

注記:スロットについてよく知らなければ、それは第五章で説明されています。

Ploneはマクロへのパス表現を入れておくことができる「スロット」を持っています。これらのスロットと同じ順番で、表現は評価され、その結果のHTMLがページに送られます。left_slotsスロットはコンテンツを左欄に配置し、right_slotsはコンテンツを右欄に配置します。とても単純。パス表現はhere/calendar_slot/macros/calendarBox のような感じになります(これはディフォルトPloneセットアップのright_slotsの中にあります)。python:path(here/calendar_slot/macros/calendarBox)のようなページテンプレートの描画はカレンダー部品を返してきます。パス表現に関するより詳しい情報はZope 2.6 bookで見つけることができます。

ZMIの中でitems to addボックスのドロップダウン見ると、近くにRAM Cache Managerが見つかるでしょう。RAMキャッシュマネージャはデフォルトのZopeツールであり、より詳しい情報は Zope Book で見つかります。 - それらのひとつをルートフォルダに入れるとコンテンツをキャッシュできるように登録するためのテンプレートが使えるようになります。キャッシュされたコンテンツへのキーとして異なるREQUEST変数をもつことができます。例えば、plone.orgでは私たちはcache_calendar RAMキャッシュマネージャを作成し、そのREQUEST変数は次のようなものです。

calendar_slotが描画されるたびに、これらの変数をREQUESTでチェックし、それに合致したHTML表現を出力します。これにより異なる年月設定で複数言語でカレンダーをキャッシュに持つことができます。

これはとても具合がいいです。キャッシュマネージャが出来上がりのHTMLを保持してくれていて、object/templateが変更された時には、キャッシュにあるそれを無効にしてくれます。(あるいは手作業かプログラムで無効にします)。しかしいくつかの問題があります。物事はそんな簡単には運びません。

まず最初に、マクロはキャッシュできません。here/calendar_slot/macros/calendarBoxのような呼び出しがあっても、絶対にキャッシュマネージャに取り上げてもらえません。here/calendar_slotのように明示的でなければなりません。しかしportal_skins/plone_templates/ui_slots/calendar_slot はうまく形成されたHTMLドキュメントです。描画されたものをそのまま単純に含めることはできません。div部分だけが表示されるようにこのテンプレートをカスタマイズする必要があります。それから、Ploneのルート(Ploneフォルダー)でportal_skinsや他のツールを含んだPropertiesタブに行きます。そしてパス表現をhere/calendar_slotに変更します。いくつかの要求をしてみて、RAMキャッシュの統計を見てどのぐらいヒットしているか見ます。そして<html>タグと他のものを取り除くと(なぜなら、描画する時に、それらがテンプレートと一緒についてきてほしくないから)、Plone HTMLはまだうまく形成されていることでしょう。

2番目としてhere/calendar_slotはオブジェクトのコンテクストの中でテンプレートをキャッシュします。ここで獲得「Acquisition」が問題になります。もし plone.org の中のパス表現を描画したら、/のコンテクストで描画されたcalendar_slotを得ます。しかし、例えば http://plone.org/Members/runyaga/ とかを見ていたら、違うディレクトリーである /Members/runyaga のコンテクストで描画されるcalendar_slotを得ることになります。

tal:replace="here/calendar_slot"と言いたいのではなくて、left_slotsのプロパティタブの中でportal/calendar_slotと言いたいのです。ポータルはplone_templates/main_templateの中でglobal portal here/portal_url/getPortalObject;として定義されます。そしてcalendar_slotはいつも/のコンテクストの中で描画されるでしょう。

それからplone.orgに施した特別なセットアップは、

ナビゲーションツリー

            - cache_navtree

            - AUTHENTICATED_USER

            - HTTP_ACCEPT_LANGUAGE

            - URL1

here/navigation_tree_slotをキャッシュすれば、ログインされたユーザの現在選んでいる言語の中のURL1によるエントリー得るだけです。Anonymousはログインしていないユーザーのことです。

ニュースボックス

            - cache_news

            - HTTP_ACCEPT_LANGUAGE

i18nラベルをキャッシュするには、言語ごとにportal/news_slot をキャッシュするだけでよいです。

ページをキャッシュする

Ploneのキャッシュマネージャ

Ploneは二つのキャッシュマネージャを一緒に提供します。

これら両方のキャッシュについてはZope Bookの caching の章で詳しく説明されています。

NOTE: 格好いい解決方法ではないが、calendar_slotを置き換えることで実現することができます。


例えばこれを

...%s/%s?month:int=%d&amp;year:int=%d % (here.absolute_url(),template.getId(),...

次のようにします

... %s/%s?month:int=%d&amp;year:int=%d % (here.portal_url(),index_html/document_view,...

404エラーページをキャッシュする

Credit: Geoff Davis

Windows NTで動くクライアント機をセットアップしている時に、私はログの中のたくさんの404エラーに気がつきました。クライアント機がいろいろなワームによって残されたバックドアを探査されていたことがわかりました。誰かがこの不正なURL、例えば/system.exeとかにアクセスしようとするたびにサーバーは404メッセージを出してしまいます。家のラップトップPC(Windows XP動く850MHz Pentium III)でベンチマークテストをしたところ、不正ページへの要求に毎秒4回、回答されるのがわかりました。

エラーになる要求を受けるたびに、Ploneはstandard_error_message.pyを実行し、default_error_message.ptに制御を渡します。ZODBの中にdefault_404_message.ptという名前のとても簡単なページを作ることで、この404ページの生成を大きくスピードアップすることができます。そしてこのページをRAMキャッシュマネージャを使ってキャッシュし、この新しい404ページをキャッシュします。404が発生した時にこの新しいページを描画するためには、Ploneサイトでエラーを解析するskins/plone_templates/standard_error_message.pyに次の2行を追加する必要があります。

                if error_type=='NotFound'

                    return context.default_404_message()

ab[1]を使いテストすると、変更前は毎秒3.9要求を受けましたが、後では毎秒37要求になりました。大きな視点で見るとクライアント機は毎分数ダースの不正要求を受けているように見えます。もし毎分24の不正要求をこのラップトップに受けているなら、404は処理能力の10%を使い果たすことになります。この技法は何らかの理由で404を大量に生成しなければならない時に間違いなく役に立つことでしょう。

[1] abはApacheに付属するベンチマークツールであり、サイトをストレステストする簡単なツールです。より詳しい情報は ここで入手 できます。

前 [第八章:Ploneを拡張する] | 目次 | 次 [第十章:構造化テキスト]