GAEのurl.fetchでDownloadError ApplicationError: 5

By |2月 3, 2010|Google App Engine, |


はじめまして。Joeと申します。

クラウドに着目した事業展開に惹かれ、昨年ウフルに入社させていただいた新入社員です。技術的に経験が浅く未熟な面も多々ありますが、一生懸命アウトプットしていきますんでツッコミよろしくお願いします。ブログ記事を通して少しでも成長できれば良いな〜と思っております。

さて、ついこの間まで開発していたGoogle App Engineの受託案件についてお話しさせていただきます。仕様や概略は[Google App Engine]事例紹介 – 某ECサイトでご確認いただくとして、今回は開発中で最も苦しんだ部分について書かせていただきます。

その最も苦しんだ部分というのはurl.fetchのレスポンスタイムアウトです。

Google App Engineで外部サーバから情報を取得するには、Googleが用意してくれているurl.fetchサービスというAPIを利用します。使用方法はURL Fetch Python API(Python版)URL Fetch Java API(Java版)をご覧ください。

今回は外部サーバから情報を取得する場面が多々ありましたのでurl.fetchは至る所で使用しています。テストなどで試す分にはそんなに問題が顕在化しないのですが、いざ運用が始まると「DownloadError ApplicationError: 5」という例外に苦しめられます。

詳しくは知らないのですが、かなり噛み砕いて言いますと「url.fetchでアクセスしたサーバからだいたい5秒以内にレスポンスが返ってこないとタイムアウトする」ようです。アプリの仕様を見ていただくとわかる通り5分おきに数百件の処理をTaskQueueに積んで順次実行する仕様になってますので失敗が重なると一瞬にしてタスクが渋滞します。

今のところTaskQueueを自動で削除する方法がないため、帰省ラッシュのごとく大渋滞したタスクを放っておくとリソースを使い果たし、渋滞の運転で疲れ果てた父ちゃんのようにアプリがダウンします。しかし5秒というのはシビア過ぎると思いませんか?レスポンスが5秒返らないことなんて普通にブラウジングしてても結構ありますよね?

でも、いろいろと探して解決方法を見つけました。Pythonの場合(あ、すいません、今回の開発はPythonで行いました)以下のようにdeadlineという引数を10にすればタイムアウトを10秒まで延ばせるようです。この引数は英語版のドキュメントにしか書いていません^^;(ドキュメントの言語を日本語にすると消えます…)※2010年2月3日現在

[python]
res = urlfetch.fetch(url=URL, method=urlfetch.GET, deadline=10)
[/python]

Javaの場合はJava.net.URLConnectionのsetConnectTimeoutとかsetReadTimeoutなどがありますが、残念ながらGAE/jのドキュメントに「アプリケーションは、リクエストのタイムアウトを明示的に設定できません。」とバッチリ書かれてますね。Java.net.URLConnectionを利用するとできないんでしょうか?ちょっと試せてないのでわかりません。

ただJava版でも低レベルAPIというのがあって、こちらのオプションにsetDeadlineというオプションがありますね。(こちらも英語版のみ書いてます^^;)最悪これでできるでしょう。

世間では「8秒ルール」なんて言われてますので、10秒まで延ばせばだいぶタイムアウトになる率も下がります。それでもタイムアウトしてしまう場合は大人しく例外処理をしましょう。特に今回のようなタスクが積み重なるようなアプリは例外を拾って何らかの処理をしてあげないと、失敗したタスクが残り続けリソースを食いつぶし「Over Quota」という寂しい画面になってしまいます。

※このエントリを書きながら思いついたんですが例外時に新たにタスクを積む処理を書き、何らかの方法で積んだ回数を保持すれば「リトライは5回まで」みたいなこともできるのかな?ちょっと試してみます。