Apache Traffic ServerでWordpressのページをキャッシュするのを試してみた

  • 18
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

CentOS 6上でepelからyumでインストールできるApache Traffic Server v5.3.0を試してみました (*1)。Ansibleのプレーブックを https://github.com/hnakamur/trafficserver-ansible-playbook に置いています。記事執筆時点のバージョンは https://github.com/hnakamur/trafficserver-ansible-playbook/tree/8efa6790d29bd8e62e488ceb7486a435e327d795 です。

*1: 当初epelからyumでtrafficserverをインストールしていたのですが、Cache Inspectorが複数のURLを分割する処理にバグ(後述します)があったため、 修正版をCopr上のhnakamur/apache-traffic-serverレポジトリからインストールするようにしました。その後このレポジトリのrpmはApache Traffic Serverのバージョンを5.x系の最新の5.3.2に上げました。

Apache Traffic Serverとは

Apache Traffic Serverはホームページの説明によると、速くてスケーラブルで拡張可能なHTTP/1.1準拠のキャッシュ機能付きプロキシサーバです。OverviewのProvenのところを見ると、フォワードプロキシとリバースプロキシとしてyahoo.comで1日あたり400TBのトラフィックを処理しているそうです。

Newsのところに書かれていますが、2015年9月21日にリリースされたv6.0.0ではHTTP/2もサポートしています。設定方法はhttp2 - Apache Traffic Server で HTTP/2 を有効にする方法 - Qiitaという記事が参考になりそうです。

Traffic Server - Wikipedia, the free encyclopediaによると元々Inktomiが開発してInktomi Traffic Serverという商用製品だったのが、Yahoo!が2003年にInktomiを買収し、2009年にTraffic ServerのソースコードをApache Incubator projectに寄贈してオープンソースになったということです。

ですので、名前にApacheとついていますがApache HTTP Serverとは全く別物です。Choosing A Proxy Server - Apachecon 201429枚目のスライドを見るとApache Traffic Server (ATS)はイベントドリブンのI/Oをマルチスレッドで行うアーキテクチャとなっています。

Varnishと違ってプロセス再起動してもキャッシュが消えない

キャッシュサーバとしてはApache Traffic Serverの他にVarnishも有名です。The Varnish Users Guide — Varnish version 4.1.0 documentationStorage Backendsを見るとVarnishには以下の4つのストレージバックエンドが用意されています。

  • malloc
  • file
  • persistent (deprecated)
  • Varnish Massive Storage Engine (MSE) in Varnish Plus only

このうちmallocとfileはvarnishdのプロセスを再起動するとキャッシュの内容が消えてしまいます。persistentはdeprecatedなので使わないほうが良さそうです。Varnish Massive Storage EngineはVarnish Plusという有償製品でのみ利用可能で、私は使ったことがないのでプロセス再起動時にキャッシュが消えるかは不明です。

Apache Traffic Serverではプロセスを再起動してもキャッシュが消えずに残ります。

ソースコード

githubのapache/trafficserverにミラーがありました。71.1%がC++、22.2%がCで書かれているそうです。ちょっと覗いてみた感じでは、boostなどのテンプレートライブラリは使用しておらず、15年以上C++を書いてない私でもなんとか読めそうな感じでした。

今回の構成

Vagrant + VirtualBoxで構築した仮想マシンに192.168.33.131というIPアドレスを設定しています。

Apache Traffic Serverを80番ポート、オリジンサーバとして使うWordpress用のApache HTTP Serverを8081番ポートで稼働させ、Apache Traffic ServerからApache HTTP Serverへリバースプロキシする構成としました。

WordpressのセットアップはRemi's RPM repositoryのphp70パッケージ、MySQL :: Download MySQL Yum Repositoryのmysql-community-serverパッケージとCommand line interface for WordPress | WP-CLIを使いました。

Wordpressのコンテンツはjawordpressorg/theme-test-data-jaのWordpressテストデータ日本語版をベースにall-free-download.comからダウンロードしたPublic domainの写真の投稿を追加したものを利用しています。

設定ファイル

/etc/trafficserver/*.config に設定ファイルが複数あります。設定方法の概要はConfiguring Traffic Serverに説明があります。

各ファイルの設定項目についてはConfiguration Filesに説明があります。

なお、ドキュメントのページの左下のv:latestをクリックしてVersionsの5.3.xをクリックするとバージョン5.3.xのドキュメントを見られると期待したのですが、SORRY This page does not exist yet.となってしまったので、ドキュメントは最新版を見ることにします。

オリジンサーバへのプロキシ設定

リバースプロキシの設定はReverse Proxy and HTTP Redirectsに説明があります。

今回の構成では以下の設定としました。

/etc/trafficserver/records.config
CONFIG proxy.config.reverse_proxy.enabled INT 1
/etc/trafficserver/remap.configs
map http://192.168.33.131/ http://localhost:8081/
reverse_map http://localhost:8081/ http://192.168.33.131/

キャッシュストレージの設定

Cache Storagestorage.configにキャッシュストレージについての説明があります。

生のディスクパーティションを使うほうがパフォーマンスは良いそうなのですが、今回は手軽にファイルをストレージとして使う設定にしました。キャッシュがあふれるケースを試したいのでなるべく小さいサイズにしたいのですが、サイズは最低128MBが必要と書いてあったので128Mにしました。

/etc/trafficserver/storage.config
/var/cache/trafficserver 128M

RAMキャッシュ

Changing the Size of the RAM Cacheを見ると、よく参照される小さなオブジェクトにはRAMキャッシュが使われるそうです。

RAMキャッシュのサイズは明示的に指定することも出来ますが、 -1 にすればディスクストレージのサイズに応じて自動で設定されます。1GBのディスクストレージにつき約1MBになるそうです。

/etc/trafficserver/records.config
CONFIG proxy.config.cache.ram_cache.size INT -1

proxy-config-cache-ram-cache-cutoffという設定項目で、RAMキャッシュに格納するオブジェクトの最大サイズを指定することも出来ます。デフォルト値は4MB (4194304)となっています。

/etc/trafficserver/records.config
CONFIG proxy.config.cache.ram_cache_cutoff INT 4194304

ログ形式の設定

ログ出力のドキュメントの目次がEvent and Error Monitoringにあります。

標準のログ形式についてはLog Formatsに説明があります。以下の4つの形式があるそうです。

一番情報量の多いNetscape Extended-2形式を使う設定例を以下に示します。

/etc/trafficserver/records.config
# Use the Netscape Extended-2 format
#   http://trafficserver.readthedocs.org/en/5.3.x/admin/working-log-files.en.html
CONFIG proxy.config.log.extended2_log_enabled INT 1
CONFIG proxy.config.log.extended2_log_is_ascii INT 1
CONFIG proxy.config.log.extended2_log_name STRING proxy.log

LTSV形式でのログ出力設定

カスタムのログ形式を使うための設定方法がCustom Formatsに書かれています。

今回は以下の設定にしました。

/etc/trafficserver/records.config
# Enable the custom logging
#   http://trafficserver.readthedocs.org/en/5.3.x/admin/working-log-files.en.html#using-the-custom-format
CONFIG proxy.config.log.custom_logs_enabled INT 1
/etc/trafficserver/logs_xml.config
<LogFormat>
  <Name = "ltsv"/>
  <Format = "host:%<chi>        user:%<caun>    time:%<cqtn>    req:%<cqtx>     s1:%<pssc>      c1:%<pscl>      s2:%<sssc>      c2:%<sscl>      b1:%<cqbl>      b2:%<pqbl>      h1:%<cqhl>      h2:%<pshl>      h3:%<pqhl>      h4:%<sshl>      xt:%<tts>       route:%<phr>    pfs:%<cfsc>     ss:%<pfsc>      crc:%<crc> chm:%<chm>   cwr:%<cwr>      ua:%<{User-Agent}cqh>   referer:%<{Referer}cqh>"/>
</LogFormat>

<LogObject>
  <Format = "ltsv"/>
  <Filename = "proxy.ltsv.log"/>
</LogObject>

Netscape Extended-2にカスタムのログ形式でNetsape Extended-2形式と同じログを出力する設定が書いてあるので、それをベースにLTSV形式にし、User-AgentやRefererなどいくつか項目を追加しています。

キャッシュリザルトコード: crc

上記の crc:%<crc> の項目がキャッシュのヒットなどの状態を表します。crcに項目の説明があり、Cache Result Codesに取り得る値の説明があります。

実際に試していたら TCP_MEM_HIT というここに載っていない値が出ることもありました。v5.3.xとv6.0.xの違いなのか、ドキュメントが不完全なのかは未調査です。

また、crcの他にchmcwrといった項目も有るようです。

デバッグログを有効にして出力対象にcache_inspectorタグを追加

後述のCache Inspectorのバグ調査のため、デバッグログを有効にして出力対象にcache_inspectorタグを追加する必要がありました。

Using Debug Tagsによると、 proxy.config.diags.debug.enabled1 にしてproxy.config.diags.debug.tagsに出力したいタグを指定するとデバッグログが出力されるそうです。

yumでインストールした状態の設定ファイルは以下のようになっていました。

/etc/trafficserver/records.config
CONFIG proxy.config.diags.debug.enabled INT 0
CONFIG proxy.config.diags.debug.tags STRING http.*|dns.*

そこで以下のように設定を変更しました。

/etc/trafficserver/records.config
CONFIG proxy.config.diags.debug.enabled INT 1
CONFIG proxy.config.diags.debug.tags STRING http.*|dns.*|cache_inspector.*

Using Debug Tagsに説明がありますが、上記の設定方法の場合デバッグログの出力先は /var/log/trafficserver/traffic.out になります。

ログをUnix名前付きパイプに出力

今回は試していませんが、ASCII Log PipesによるとログをUnix名前付きパイプに出力することもできるそうです。

Cache Inspectorなど各種HTTP UIエンドポイントを使うための設定

Inspecting the CacheにCache Inspectorというユーティリティを使うための設定について説明があります。

例えば以下のように書いて http://yourhost.com/myCI/ にアクセスするとCache Inspectorが表示されるようになります。 @src_ip で指定した172.28.56.1-172.28.56.254のIPアドレスからのみアクセス可能です。

/etc/trafficserver/records.config
CONFIG proxy.config.http_ui_enabled INT 1
/etc/trafficserver/remap.config
map http://yourhost.com/myCI/ http://{cache} @action=allow @src_ip=172.28.56.1-172.28.56.254

Cache Inspectorを開くと以下の5つのリンクが表示されており、リンクをたどると各機能を使うためのフォームが表示されます。これらの機能については後述します。

  • Lookup url
  • Delete url
  • Regex lookup
  • Regex delete
  • Regex invalidate

proxy.config.http_ui_enabledの説明を見ると http://{cache} 以外のUIエンドポイントもあります。

http://{http} エンドポイントを使うための設定 proxy.config.http.enable_http_infoとアクセス元IPアドレス制限を一括で行うためのNamed Filtersを参考にして、今回は以下の様な設定にしました。

/etc/trafficserver/records.config
CONFIG proxy.config.http_ui_enabled INT 3
CONFIG proxy.config.http.enable_http_info INT 1
/etc/trafficserver/remap.config
.definefilter local_only @action=allow @src_ip=192.168.33.1 @src_ip=192.168.33.131 @src_ip=127.0.0.1
.activatefilter local_only
# NOTE: These mapping must be placed before the mapping for http://192.168.33.131/.
# NOTE: The trailing slashes for /_ats_stat/cache/ is needed for the cache inspector to work properly.
map http://192.168.33.131/_ats_stat/cache-internal/ http://{cache-internal}
map http://192.168.33.131/_ats_stat/cache/ http://{cache}
map http://192.168.33.131/_ats_stat/hostdb/ http://{hostdb}
map http://192.168.33.131/_ats_stat/http/ http://{http}
map http://192.168.33.131/_ats_stat/net/ http://{net}
map http://192.168.33.131/_ats_stat/stat/ http://{stat}
map http://192.168.33.131/_ats_stat/test/ http://{test}
.deactivatefilter local_only

なお、上記の設定 /_ats_stat/cache/ の最後のスラッシュが無いとCache Inspectorのページにある Lookup url などのリンクをクリックした時に404 Not Foundとなってしまったのでご注意ください。これについては[TS-1438] Cache inspector should add trailing slash to CI URL - ASF JIRAというイシューがありました。

他のエンドポイントでは最後のスラッシュは不要かも知れませんが、一貫性が無いと混乱するのでスラッシュ有りに統一しています。

ちなみにApache Traffic ServerのUIエンドポイントはどれもCSS無しの必要最低限なページ構成になっています。

キャッシュ破棄

全てのキャッシュをクリア

Clearing the Cacheに説明がありました。サーバを一旦停止する必要があります。以下のように実行します。

sudo service trafficserver stop
sudo traffic_server -Cclear
sudo service trafficserver start

httpのカスタムメソッドPURGEで個別のキャッシュオブジェクトを削除

Removing an Object From the Cacheに説明があります。

HTTPのカスタムメソッド PURGE を使ってオブジェクトをキャッシュから削除することができます。デフォルトでは localhost からのアクセスのみ許可します。これはip_allow.configで変更可能のようです(今回は試していません)。

Removing an Object From the Cacheには $ curl -vX PURGE --resolve example.com:80:127.0.0.1 http://example.com/remove_me.jpg という例が書いてありますが、cURL - How To Useによると --resolve はcurlのバージョン7.21.3で追加されたそうで、CentOS 6のcurl 7.19.7では使えませんでした。そこで、 -H オプションでHostヘッダを指定するようにしたら削除に成功しました。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、以下のように実行すると PURGE できました。

# curl -vX PURGE -H 'Host: 192.168.33.131' http://127.0.0.1/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg
* About to connect() to 127.0.0.1 port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> PURGE /wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Accept: */*
> Host: 192.168.33.131
>
< HTTP/1.1 200 OK
< Date: Thu, 31 Dec 2015 08:06:46 GMT
< Connection: keep-alive
< Server: ATS/5.3.0
< Content-Length: 0
<
* Connection #0 to host 127.0.0.1 left intact
* Closing connection #0

なお、キャッシュが無い状態で再度上記のリクエストを送ると404 Not Foundになりました。

Cache Inspector Utilityを使ったキャッシュの削除

Using the Cache Inspector Utilityに説明があります。

Lookup URLフォームの検索結果からの単一URL削除

Cache Inspector (今回の構成では http://192.168.33.131/_ats_stat/cache/ )のLookup urlのリンクをクリックし、URLを入力して[Lookup]ボタンを押すと検索結果が表示されます。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、Lookup urlのフォームで http://192.168.33.131/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg と入力して[Lookup]ボタンを押すと結果が表示されます。

検索結果のActionの行の[Delete URL]ボタンを押すとキャッシュを削除できます。

URLを入力して[Lookup]ボタンを押したときに、キャッシュに存在しない場合はCache Lookup Failed, or missing in clusterと表示されました。

Delete urlフォームからの複数URL一括削除

Cache InspectorのDelete urlのリンクをクリックするとCache Deleteというページが表示されます。
Type the list urls that you want to delete in the box below. The urls MUST be separated by new lines との説明があります。その下のテキストエリアに複数のURLを1行に1つずつ入力し[Delete]ボタンを押すと指定した複数URLのキャッシュを一括削除できます。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、
http://192.168.33.131/_ats_stat/cache/delete_url_form

http://192.168.33.131/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg
http://192.168.33.131/wp-includes/js/wp-emoji-release.min.js?ver=4.4

のように入力して[Delete]ボタンを押すと、結果のページには以下のように表示されました。

http://192.168.33.131/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg http://192.168.33.131/wp-includes/js/wp-emoji-release.min.js?ver=4.4 Delete succeeded
http://192.168.33.131/wp-includes/js/wp-emoji-release.min.js?ver=4.4 Delete succeeded

Regex lookupフォームの結果画面からの削除

Cache InspectorのRegex lookupのリンクをクリックするとCache Regex Lookupというページが表示されます。
Type the list of regular expressions that you want to lookup in the box below. The regular expressions MUST be separated by new lines との説明があります。テキストエリアに複数のURLの正規表現を1行に1つずつ入力し[Lookup]ボタンを押すと、キャッシュを検索できそうなのですが、試してみるとうまく行きませんでした。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、

http://.*\.js
http://.*\.jpg

のように入力して[Lookup]ボタンを押すと、結果ページに以下のようにチェックボックスとヒットしたURLが並んだテーブルが表示されました。

http://localhost:8081/wp-includes/js/jquery/jquery.js?ver=1.11.3
http://localhost:8081/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1
http://localhost:8081/wp-content/themes/twentysixteen/js/skip-link-focus-fix.js?ver=20150825
http://localhost:8081/wp-content/themes/twentysixteen/js/functions.js?ver=20150825
http://localhost:8081/wp-includes/js/comment-reply.min.js?ver=4.4
http://localhost:8081/wp-includes/js/wp-embed.min.js?ver=4.4
http://localhost:8081/wp-includes/js/wp-emoji-release.min.js?ver=4.4

キャッシュを削除したいURLのチェックボックスをオンにして、テーブルの下の[Delete]ボタンを押せばキャッシュが削除できそうな感じです。

ところが1行目と2行目にチェックをつけて[Delete]ボタンを押してみると、以下のように失敗してしまいました。

http://localhost:8081/wp-includes/js/jquery/jquery.js?ver=1.11.3 Delete failed
http://localhost:8081/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1 Delete failed

Chromeの開発ツールで確認すると以下の様なリクエストになっていました。

Request URL:http://192.168.33.131/_ats_stat/cache/delete_url?url=http%3A%2F%2Flocalhost%3A8081%2Fwp-includes%2Fjs%2Fwp-emoji-release.min.js%3Fver%3D4.4%0D%0Ahttp%3A%2F%2Flocalhost%3A8081%2Fwp-includes%2Fjs%2Fjquery%2Fjquery-migrate.min.js%3Fver%3D1.2.1%0D%0A
Request Method:GET

以下のように url パラメータ内の localhost%3A8081192.168.33.131 にしてcurlから実行すれば削除成功しました。

$ curl -v http://192.168.33.131/_ats_stat/cache/delete_url?url=http%3A%2F%2F192.168.33.131%2Fwp-includes%2Fjs%2Fjquery%2Fjquery.js%3Fver%3D1.11.3%0D%0Ahttp%3A%2F%2F192.168.33.131%2Fwp-includes%2Fjs%2Fjquery%2Fjquery-migrate.min.js%3Fver%3D1.2.1%0D%0A
*   Trying 192.168.33.131...
* Connected to 192.168.33.131 (192.168.33.131) port 80 (#0)
> GET /_ats_stat/cache/delete_url?url=http%3A%2F%2F192.168.33.131%2Fwp-includes%2Fjs%2Fjquery%2Fjquery.js%3Fver%3D1.11.3%0D%0Ahttp%3A%2F%2F192.168.33.131%2Fwp-includes%2Fjs%2Fjquery%2Fjquery-migrate.min.js%3Fver%3D1.2.1%0D%0A HTTP/1.1
> Host: 192.168.33.131
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 31 Dec 2015 21:01:14 GMT
< Connection: close
< Server: ATS/5.3.0
< Content-Length: 428
< Content-Type: text/html
<
<HTML>
<HEAD><TITLE>Delete URL</TITLE>
<BODY BGCOLOR="#ffffff" FGCOLOR="#00ff00">
<H1>Delete URL</H1>
<B><TABLE border=1>
<TR><TD>http://192.168.33.131/wp-includes/js/jquery/jquery.js?ver=1.11.3</TD><td>Delete <font color=green>succeeded</font></td></tr>
<TR><TD>http://192.168.33.131/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1</TD><td>Delete <font color=green>succeeded</font></td></tr>
</TABLE></B>
</BODY>
</HTML>
* Closing connection 0

Regex deleteフォームでURLの正規表現を指定してキャッシュ削除

Cache InspectorのRegex deleteのリンクをクリックするとCache Regex deleteというページが表示されます。
Type the list of regular expressions that you want to delete in the box below. The regular expressions MUST be separated by new lines との説明があります。テキストエリアに複数のURLの正規表現を1行に1つずつ入力し[Delete]ボタンを押すと、キャッシュを削除できます。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、

http://.*\.js
http://.*\.jpg

のように入力して[Delete]ボタンを押すと、結果ページに以下のように表示されました。

http://localhost:8081/wp-includes/js/jquery/jquery.js?ver=1.11.3 deleted
http://localhost:8081/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg deleted
http://localhost:8081/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1 deleted
http://localhost:8081/wp-content/themes/twentysixteen/js/skip-link-focus-fix.js?ver=20150825 deleted
http://localhost:8081/wp-content/themes/twentysixteen/js/functions.js?ver=20150825 deleted
http://localhost:8081/wp-includes/js/comment-reply.min.js?ver=4.4 deleted
http://localhost:8081/wp-includes/js/wp-embed.min.js?ver=4.4 deleted
http://localhost:8081/wp-includes/js/wp-emoji-release.min.js?ver=4.4 deleted

Regex invalidateフォームでURLの正規表現を指定してキャッシュ無効化

Using the Cache Inspector UtilityのRegex Invalidateの説明を読むと、指定した正規表現にマッチしたオジェクトを新鮮でない (stale) 状態にし、次にリクエストが来た時にオリジンサーバにキャッシュオブジェクトが有効で新鮮かを確認して、必要に応じてオリジンからコンテンツを取得してキャッシュを更新してから返すそうです。

Cache InspectorのRegex invalidateのリンクをクリックするとCache Regex Invalidateというページが表示されます。
Type the list of regular expressions that you want to invalidate in the box below. The regular expressions MUST be separated by new lines との説明があります。テキストエリアに複数のURLの正規表現を1行に1つずつ入力し[Invalidate]ボタンを押すと、キャッシュを無効化できます。

例として http://192.168.33.131/?p=2328 をブラウザで表示した後、

http://.*\.js
http://.*\.jpg

のように入力して[Invalidate]ボタンを押すと、結果ページに以下のように表示されました。

http://localhost:8081/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1 Invalidate
http://localhost:8081/wp-includes/js/jquery/jquery.js?ver=1.11.3 Invalidate
http://localhost:8081/wp-content/themes/twentysixteen/js/skip-link-focus-fix.js?ver=20150825 Invalidate
http://localhost:8081/wp-content/themes/twentysixteen/js/functions.js?ver=20150825 Invalidate
http://localhost:8081/wp-includes/js/wp-embed.min.js?ver=4.4 Invalidate
http://localhost:8081/wp-includes/js/comment-reply.min.js?ver=4.4 Invalidate
http://localhost:8081/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg Invalidate
http://localhost:8081/wp-includes/js/wp-emoji-release.min.js?ver=4.4 Invalidate

その後、 http://192.168.33.131/?p=2328 を表示していたChromeのタブを Shift+Command+R でリロードしてみました。 /var/log/trafficserver/proxy.ltsv.log に出力されたログの一部を引用します(markdownのコードとして書くと行が長くて見にくいのであえて引用にしています。引用だとタブが空白のように見えてしまいますがご了承ください)。

host:192.168.33.1 user:- time:01/Jan/2016:06:12:06 +0900 req:GET http://localhost:8081/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1 HTTP/1.1 s1:200 c1:7200 s2:304 c2:0 b1:0 b2:0 h1:865 h2:262 h3:1055 h4:135 xt:0 route:DIRECT pfs:FINss:FIN crc:TCP_REFRESH_HIT chm:MISS_PRE_EXPIRED cwr:- ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36 referer:http://192.168.33.131/?p=2328
host:192.168.33.1 user:- time:01/Jan/2016:06:12:06 +0900 req:GET http://localhost:8081/wp-includes/js/jquery/jquery.js?ver=1.11.3 HTTP/1.1 s1:200 c1:95977 s2:304 c2:0 b1:0 b2:0 h1:854 h2:264 h3:1045 h4:136 xt:0 route:DIRECT pfs:FINss:FIN crc:TCP_REFRESH_HIT chm:MISS_PRE_EXPIRED cwr:- ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36 referer:http://192.168.33.131/?p=2328
host:192.168.33.1 user:- time:01/Jan/2016:06:12:06 +0900 req:GET http://localhost:8081/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg HTTP/1.1 s1:200 c1:490166 s2:304 c2:0 b1:0 b2:0 h1:898 h2:260 h3:1089 h4:136 xt:0 route:DIRECT pfs:FIN ss:FIN crc:TCP_REFRESH_HIT chm:MISS_PRE_EXPIRED cwr:- ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36 referer:http://192.168.33.131/?p=2328

crcTCP_REFRESH_HIT となっていて The object was in the cache, but it was stale. という状態だったことが確認できました。

ただ、 http://192.168.33.131/?p=2328 をChromeで Shift+Command+R でリロードして、別のタブでCache Regex Invalidateのページで無効化するというのを、何度か試していると結果が空の場合もありました。この件は未調査です。

Cache Inspectorでの複数URLの分離バグ

Cache Inspectorの以下の4つのフォームにはテキストエリアがあり、1行に1つずつURLを書いて複数指定可能となっています。

  • Delete url
  • Regex lookup
  • Regex delete
  • Regex invalidate

ただし、複数行の分割処理に不具合があるため、各行から最後の行まで連なった正規表現として認識されてしまっていました。

例えばブラウザで http://192.168.33.131/?p=2328 を表示した後、Regex invalidateのフォームで

http://.*\.js
http://.*\.jpg

のように入力して[Invalidate]ボタンを押してもjsファイルは無効化されず、jpgファイルのキャッシュのみが無効化されました。

http://localhost:8081/wp-content/uploads/2015/12/wisconsin_summer_farm-1200x900.jpg Invalidate

Regex invalidateフォームで[Invalidate]ボタンを押した時のリクエストをChromeの開発ツールで確認すると以下のようなリクエストになっていました。

Request URL:http://192.168.33.131/_ats_stat/cache/invalidate_regex?url=http%3A%2F%2F.*%5C.js%0D%0Ahttp%3A%2F%2F.*%5C.jpg
Request Method:GET

キャッシュを無効化するという状態を変更するリクエストに GET というhttpメソッドを使うとはイマイチだなと思いますが、それは置いておいてソースコードを見てみました。

https://github.com/apache/trafficserver/blob/413dd51d5dc17bf388805071efdb8f882014b847/iocore/cache/CachePages.cc#L190-L191

  } else if (STREQ_PREFIX(path, "invalidate_regex")) {
    SET_CONTINUATION_HANDLER(theshowcache, &ShowCache::invalidate_regex);

ShowCache::invalidate_regex の定義とそこから呼び出している ShowCache::handleCacheScanCallbackhttps://github.com/apache/trafficserver/blob/413dd51d5dc17bf388805071efdb8f882014b847/iocore/cache/CachePages.cc#L566-L606 あたりにあります。

int
ShowCache::invalidate_regex(int event, Event *e)
{
  CHECK_SHOW(begin("Regex Invalidate"));
  CHECK_SHOW(show("<B><TABLE border=1>\n"));
  scan_flag = scan_type_invalidate; // invalidate
  SET_HANDLER(&ShowCache::handleCacheScanCallback);
  cacheProcessor.scan(this);
  return EVENT_DONE;
}


int
ShowCache::handleCacheScanCallback(int event, Event *e)
{
  switch (event) {
  case CACHE_EVENT_SCAN: {
    cache_vc = (CacheVC *)e;
    return EVENT_CONT;
  }
  case CACHE_EVENT_SCAN_OBJECT: {
    HTTPInfo *alt = (HTTPInfo *)e;
    char xx[501], m[501];
    int ib = 0, xd = 0, ml = 0;

    alt->request_get()->url_print(xx, 500, &ib, &xd);
    xx[ib] = '\0';

    const char *mm = alt->request_get()->method_get(&ml);

    memcpy(m, mm, ml);
    m[ml] = 0;

    int res = CACHE_SCAN_RESULT_CONTINUE;

    for (unsigned s = 0; show_cache_urlstrs[s][0] != '\0'; s++) {
      const char *error;
      int erroffset;
      pcre *preq = pcre_compile(show_cache_urlstrs[s], 0, &error, &erroffset, NULL);

      Debug("cache_inspector", "matching url '%s' '%s' with regex '%s'\n", m, xx, show_cache_urlstrs[s]);

show_cache_urlstrs が行ごとに分割された正規表現のようです。

複数行の分割処理は https://github.com/apache/trafficserver/blob/413dd51d5dc17bf388805071efdb8f882014b847/iocore/cache/CachePages.cc#L113-L132 にありました。

      // initialize url array
      show_cache_urlstrs = new char[nstrings + 1][500];
      memset(show_cache_urlstrs, '\0', (nstrings + 1) * 500 * sizeof(char));

      char *q, *t;
      p = strstr(unescapedQuery, "url=");
      if (p) {
        p += 4; // 4 ==> strlen("url=")
        t = strchr(p, '&');
        if (!t)
          t = (char *)unescapedQuery + strlen(unescapedQuery);
        for (int s = 0; p < t; s++) {
          show_cache_urlstrs[s][0] = '\0';
          q = strstr(p, "%0D%0A" /* \r\n */); // we used this in the JS to separate urls
          if (!q)
            q = t;
          ink_strlcpy(show_cache_urlstrs[s], p, sizeof(show_cache_urlstrs[s]));
          p = q + 6; // +6 ==> strlen(%0D%0A)
        }
      }

      Debug("cache_inspector", "there were %d url(s) passed in", nstrings == 1 ? 1 : nstrings - 1);

ink_strlcpyの第3引数が間違っていました。 sizeof(show_cache_urlstrs[s]) と指定しているために常に文字列の最後までコピーされてしまっていました。

cache_inspector のデバッグログを見てみると以下のように URL 1 が1行目と2行目が連なった状態になっています。

/var/log/trafficserver/traffic.out
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) query params: 'url=http%3A%2F%2F.*%5C.js%0D%0Ahttp%3A%2F%2F.*%5C.jpg' len 33 [unescaped]
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) query params: 'url=http://.*\.js
http://.*\.jpg' len 33 [escaped]
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) there were 1 url(s) passed in
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) URL 1: 'http%3A%2F%2F.*%5C.js%0D%0Ahttp%3A%2F%2F.*%5C.jpg'
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) URL 1: 'http://.*\.js
http://.*\.jpg'
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) URL 2: 'http%3A%2F%2F.*%5C.jpg'
[Dec 31 20:49:25.677] Server {0x2b45e18837a0} DEBUG: (cache_inspector) URL 2: 'http://.*\.jpg'

sizeof(show_cache_urlstrs[s])q ? q - p + 1: sizeof(show_cache_urlstrs[s]) に修正すれば正常に動作するようになりました。

この問題は[TS-4106] Cache Inspector fails to split multiline URLs - ASF JIRAにイシューを作成して報告済みです。

2016-01-13追記: このバグはmasterでは修正済みです。
その後プルリクを送ってmasterにマージされました。
https://github.com/apache/trafficserver/commit/a70e9473cabba89861965d5ab775dbe4f20c720b

Cache Inspectorのテキストエリアの各行は最大499文字

https://github.com/apache/trafficserver/blob/413dd51d5dc17bf388805071efdb8f882014b847/iocore/cache/CachePages.cc#L114 のようにテキストエリアの各行の内容を保持する領域を show_cache_urlstrs = new char[nstrings + 1][500]; というコードで確保しているので、複数行で指定するURLの正規表現は1行あたり499文字以下にする必要があります。ご注意ください。

多段キャッシュ

今回は試していませんが、Hierarchical Cachingに多段キャッシュについても説明がありました。

おわりに

初めてApache Traffic Serverを試してみたのですが、良い感じですね。今後活用していきたいです!

あと、6.x用のレポジトリhnakamur/apache-traffic-server-6 Coprも作りました。こちらも今後試してみようと思います。