AutoPatchWorkのサーバー周りのこと

地味に色々と調整しているのでまとめておきます。見よう見まねの継ぎ接ぎばかりですが…。

AutoPatchWorkはwedata(About - wedata)のAutoPagerize用SITEINFOを使わせて頂いていますが、wedataのサーバーは負荷に弱いので、SITEINFOは自前のサーバー(といっても安心のhetemlですが)に置いています。
hetemlサーバーでcronを設定して1時間に1回、SITEINFOを更新するようにしていて、AutoPatchWorkユーザーは一日に1回hetemlサーバーからSITEINFOを取得しています。
ただ、AutoPagerizeのSITEINFOは2MB近くあるので、1万人が毎日アクセスしたら転送量が20GBに達します(全員が毎日更新にくるわけではないので、実際はもっと少ないですが)。hetemlの転送量は「目安として 1日 20GB まで」(2011年6月27日時点。ちなみに以前は10GBだった)で、ChromeのAutoPatchWorkユーザーだけで6万人以上いるので余裕でアウトです(といっても、一度問い合わせてみたところ、目安を超えたら即制限がかかるようなことはないそうです)。
ただ、容量を節約するポイントは色々あって、まずオリジナルのSITEINFOは次のようなJSONデータです。

{
  "name": "Google Search",
  "resource_url": "http:\/\/wedata.net\/items\/472",
  "updated_at": "2011-04-24T23:09:52+09:00",
  "created_by": "swdyh",
  "database_resource_url": "http:\/\/wedata.net\/databases\/AutoPagerize",
  "data": {
    "pageElement": "id(\"search\")\/div[ol or div]|id(\"res\")[not(@role)]\/div[ol or div]|id(\"ofr\")",
    "url": "^https?:\/\/[^.\/]+\\.google(?:\\.[^.\/]{2,3}){1,2}\/(?:c(?:se|ustom)|search|webhp|#)",
    "nextLink": "id(\"pnnext\")|id(\"navbar navcnt nav\")\/\/td[span]\/following-sibling::td[1]\/a|id(\"nn\")\/parent::a",
    "exampleUrl": "http:\/\/www.google.com\/search?q=AutoPagerize\r\nhttp:\/\/www.google.com\/cse?cref=http%3A%2F%2Fwww.guha.com%2Fcref_cse.xml&q=firefox&sa=Search&siteurl=www.google.co.jp%2Fcse%2Fdocs%2Fcref.html\r\nhttp:\/\/www.google.co.jp\/custom?q=firefox\r\nhttp:\/\/www.google.com\/webhp?hl=en#hl=en&expIds=17259,18167,25901,25980,26440,26459,26512&sugexp=ldymls&tok=So3knfbAtc2kJy-W-lXiWw&xhr=t&q=autopagerize&cp=10&pf=p&sclient=psy&safe=off&site=webhp&aq=f&aqi=g5&aql=&oq=autopageri&gs_rfai=&pbx=1&fp=6052204b889acdd8"
  },
  "created_at": "2008-04-16T12:35:37+09:00"
}

実際に使うのはdataの中身で、さらにexampleUrlなどはデバッグ用なので、普段は使用しません。つまり、こうできます。

{
"pageElement": "id(\"search\")\/div[ol or div]|id(\"res\")[not(@role)]\/div[ol or div]|id(\"ofr\")",
"url": "^https?:\/\/[^.\/]+\\.google(?:\\.[^.\/]{2,3}){1,2}\/(?:c(?:se|ustom)|search|webhp|#)",
"nextLink": "id(\"pnnext\")|id(\"navbar navcnt nav\")\/\/td[span]\/following-sibling::td[1]\/a|id(\"nn\")\/parent::a"
}

これだけで1.9MBから715KBまで節約できます(ただ、元のデータを引けなくなるので、IDだけ追加しています)。結構節約できますが、まだ3万ユーザー程度しかカバーできません。
これにgzipを加えてみます。cronバッチの中でgzファイルを静的に作成して、.htaccessで次のように指定します。

RewriteEngine on
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.+)$ $1.gz
AddEncoding x-gzip .gz
  1. Accept-Encodingにgzipが含まれていて、
  2. リクエストされたファイル名+.gzなファイルが存在したら、
  3. URLを.gzつきに書き換え、
  4. ファイルタイプをjsonとして強制する

という指定です。
AddEncoding x-gzip .gz はレスポンスヘッダにContent-Encoging: gzipをつけて、ブラウザにgzipとして解釈させるようにしている(のだと思うのですが、あまり自信ない…)
gzip効果で、1.9MBのファイルは355KBに、715KBのファイルは152KBまで節約されます。
152KBならすくなくとも13万ユーザーまで大丈夫です。
実際、今のところ1日の転送量は8GB程度に抑えられています。

ついでにhetemlがmod_expiresにも対応していたので、ExpiresByTypeなんかも加えて、 http://ss-o.net/json/.htaccess はこんな感じになっています。

Options +Indexes
Header append Access-Control-Allow-Origin: *

ExpiresActive on
ExpiresByType application/json "access plus 12 hours"
ExpiresByType application/x-javascript "access plus 2 days"
<Files ~ "\.json">
  ForceType "application/json; charset=utf-8"
</Files>

RewriteEngine on
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule ^(.+)$ $1.gz
AddEncoding x-gzip .gz

IndexIgnore .htaccess *.bak *.gz
IndexOptions FancyIndexing HTMLTable NameWidth=* XHTML