App Engine でバックグラウンド タスクを実行するにはどうすればよいですか?
8 に答える
Task Queue Python APIを使用できます。
Python App Engine の cron ジョブの詳細については、こちらを参照してください。
GAE は、スケーラブルな Web アプリケーションを構築するための非常に便利なツールです。多くの人が指摘する制限のいくつかは、バックグラウンド タスクのサポートの欠如、定期的なタスクの欠如、および各 HTTP リクエストにかかる時間の厳密な制限です。リクエストがその制限時間を超えると操作が終了し、時間のかかるタスクの実行が不可能になります.
バックグラウンド タスクの実行方法
GAE では、HTTP 要求がある場合にのみコードが実行されます。コードにかかる時間には厳密な時間制限 (10 秒だと思います) があります。したがって、リクエストがない場合、コードは実行されません。推奨される回避策の 1 つは、外部ボックスを使用してリクエストを継続的に送信することでした。つまり、バックグラウンド タスクを作成するようなものです。しかし、これには外部ボックスが必要で、もう 1 つの要素に依存しています。もう1つの選択肢は、クライアントがリクエストを再送信するように302リダイレクト応答を送信することでした。これにより、クライアントである外部要素に依存することになります。その外部ボックスが GAE そのものである場合はどうなるでしょうか。言語でループ構造をサポートしていない関数型言語を使用したことがある人なら誰でも、代替手段、つまり再帰がループの代わりになることを認識しています。では、計算の一部を完了し、同じ URL に対して HTTP GET を非常に短いタイムアウト (たとえば 1 秒) で実行するとどうなるでしょうか? これにより、apache で実行されている php コードにループ (再帰) が作成されます。
<?php $i = 0; if(isset($_REQUEST["i"])){ $i= $_REQUEST["i"]; スリープ (1); } $ch = curl_init("http://localhost".$_SERVER["PHP_SELF"]."?i=".($i+1)); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_exec($ch); print "Hello World\n"; ?>
これがGAEでどのように機能しないか。では、最初の URL で HTTP GET を実行する url2 などの他の URL で HTTP GET を実行するとどうなるでしょうか。これはGAEで機能するようです。このためのコードは次のようになります。
クラス FirstUrl (webapp.RequestHandler): デフゲット(自己): self.response.out.write("OK") 時間.スリープ(2) urlfetch.fetch("http://"+self.request.headers["ホスト"]+'/url2') クラス SecondUrl (webapp.RequestHandler): デフゲット(自己): self.response.out.write("OK") 時間.スリープ(2) urlfetch.fetch("http://"+self.request.headers["ホスト"]+'/url1') application = webapp.WSGIApplication([('/url1', FirstUrl), ('/url2', SecondUrl)]) デフメイン(): run_wsgi_app(アプリケーション) if __name__ == "__main__": 主要()
バックグラウンド タスクを実行する方法を見つけたので、定期的なタスク (タイマー) の抽象化と、多くの HTTP 要求にまたがるループ構造 (foreach) を構築しましょう。
タイマー タイマー
の構築は簡単です。基本的な考え方は、タイマーのリストとそれぞれが呼び出される間隔を持つことです。その間隔に達したら、コールバック関数を呼び出します。memcache を使用してタイマー リストを維持します。いつコールバックを呼び出すかを調べるために、有効期限として間隔を指定して memcache にキーを保存します。そのキーが存在するかどうかを定期的に (たとえば 5 秒) チェックし、存在しない場合はコールバックを呼び出し、そのキーを間隔を置いて再度設定します。
デフタイマー(関数、間隔): timerlist = memcache.get('タイマー') if (なし == タイマーリスト): タイマーリスト = [] timerlist.append({'func':func, 'interval':interval}) memcache.set('timer-'+func, '1', interval) memcache.set('タイマー', タイマーリスト) デフチェックタイマー(): timerlist = memcache.get('タイマー') if (なし == タイマーリスト): 偽を返す 現在のタイマーリストの場合: if(None == memcache.get('timer-'+current['func'])): #リセット間隔 memcache.set('timer-'+current['func'], '1', current['interval']) #コールバック関数を呼び出す 試す: eval(current['func']+'()') を除外する: 合格 真を返す 偽を返す
Foreach
これは、1000 のデータベース行に対して何らかの操作を実行したり、1000 の URL を取得したりするなど、時間のかかる計算を実行する場合に必要です。基本的な考え方は、memcache でコールバックと引数のリストを維持し、毎回引数を使用してコールバックを呼び出すことです。
def foreach(関数、引数): looplist = memcache.get('foreach') if(なし == ループリスト): ループリスト = [] looplist.append({'func':func, 'args':args}) memcache.set('foreach', ループリスト) デフチェックループ(): looplist = memcache.get('foreach') if(なし == ループリスト): 偽を返す if((len(looplist) > 0) および (len(looplist[0]['args']) > 0)): arg = looplist[0]['args'].pop(0) func = looplist[0]['func'] if(len(looplist[0]['args']) == 0): ループリスト.ポップ(0) if((len(looplist) > 0) および (len(looplist[0]['args']) > 0)): memcache.set('foreach', ループリスト) そうしないと: memcache.delete('foreach') 試す: eval(func+'('+repr(arg)+')') を除外する: 合格 真を返す そうしないと: 偽を返す # それ以外の # 範囲内の foreach インデックス (0, 1000): # いくつかの操作 (インデックス) # 私たちは言います # foreach('someoperaton', range(0, 1000))
これで、1 時間ごとに URL のリストを取得するプログラムを作成するのは簡単になりました。これがコードです。
デフ getone (URL): 試す: 結果 = urlfetch.fetch(url) 場合 (結果。ステータス コード == 200): memcache.set(url, '1', 60*60) #処理結果.内容 を除外する : 合格 デフ getallurl(): #取得する URL のリスト urllist = ['http://www.google.com/', 'http://www.cnn.com/', 'http://www.yahoo.com', 'http://news.google.コム] フェッチリスト = [] urllist の url の場合: (memcache.get(url) が None の場合): fetchlist.append(url) #これは同等です #fetchlist の URL: getone(url) if(len(fetchlist) > 0): foreach('getone', フェッチリスト) #タイマー コールバックを登録する タイマー ('getallurl'、3*60)
完全なコードはこちらhttp://groups.google.com/group/httpmr-discuss/t/1648611a54c01aa このコードを appengine で数日間実行しましたが、特に問題はありませんでした。
警告: urlfetch を多用しています。urlfetch の 1 日あたりの制限は 160000 です。この制限に達しないように注意してください。
ランタイムの今後のバージョンには、ある種の定期的な実行エンジンが cron として含まれます。AppEngine グループのこのメッセージを参照してください。
したがって、すべての SDK の部分が機能しているように見えますが、私のテストでは、これはまだ実稼働サーバーで実行されていないことが示されています。実行時にログを記録する「1 分ごと」の cron を設定しましたが、まだ呼び出されていません。
ただし、これがいつ利用可能になるかはわかりません...
Deferred Python Libraryを使用することは、TaskQueue API の上に構築された Python を使用して Appengine でバックグラウンド タスクを実行する最も簡単な方法です。
from google.appengine.ext import deferred
def do_something_expensive(a, b, c=None):
logging.info("Doing something expensive!")
# Do your work here
# Somewhere else
deferred.defer(do_something_expensive, "Hello, world!", 42, c=True)
アプリ エンジンには cron 機能が組み込まれています。
参照してください: https://developers.google.com/appengine/docs/python/config/cron?hl=en
バックグラウンドで定期的なタスクを実行する場合は、この質問(AppEngine cron)を参照してください。
タスクが定期的でない場合は、Task Queue Python APIまたはTask Queue Java APIを参照してください。