タスクハンドラの作成

このページでは、タスクハンドラ(push タスクを処理するコード)を作成する方法を説明します。デベロッパーは、タスクを処理するためのリクエスト ハンドラを用意する必要があります。リクエスト URL から適切なハンドラへのマッピングの宣言は、開発するサービスの app.yaml で、他のリクエスト ハンドラと同様に行われます。タスク リクエストからハンドラへのマッピングはデベロッパーが制御するようになっているため、タスクハンドラの体系はデベロッパーが自由に決めることができます。アプリケーションで処理するタスクの種類が多数の場合に、すべてのハンドラをただ 1 つのサービスに追加することも、ハンドラを複数のサービスに振り分けることもできます。

push タスクのリクエスト ハンドラをプログラミングする

キュー内では、タスクキュー サービスによって HTTP ヘッダーが作成され、タスクのターゲットで指定されているワーカー サービスのインスタンスのいずれかに送信されます。タスクキュー リクエストは IP アドレス 0.1.0.2 から送信されます。

ハンドラの開発に使用する言語は、タスクを作成してキューに追加するときの言語と同一でなくてもかまいませんが、その場合、ハンドラは独立したサービスである必要があります。

ハンドラをプログラミングするときは、次のガイドラインに従ってください。

  • そのコードから、成功を示すために 200~299 の範囲内の HTTP ステータス コードを返してください。それ以外のコードは、タスクの失敗を示します。

  • push タスクには、所定の完了期限がありますが、これはそのタスクを実行するサービスのスケーリングのタイプによって決まります。自動スケーリング サービスは、10 分以内に完了する必要があります。手動および基本スケーリング サービスは最大で 24 時間実行できます。ハンドラが期限までに完了できない場合は、タスクキュー サービスはタスクが異常終了したと見なしてタスクを再試行します。

    タスクの実行時間が期限に近づいた場合、その期限の到達前に App Engine によって(google.appengine.runtime モジュールから)DeadlineExceededError が発行されます。このエラー発行を活用して、それまでの作業内容を保存したり進行状況を記録したりできます。

  • ハンドラはべき等である必要があります。App Engine の Task Queue API は「少なくとも 1 回」の処理を提供するように設計されています。つまり、タスクが正常に追加された場合、App Engine はそれを少なくとも 1 回処理します。まれに複数のタスクが実行されることもあるので、繰り返し実行されても有害な副作用が生じないように注意してください。

次の例では、リクエストから整数値を取得し、その値を Cloud Datastore で管理されているカウンタに追加しています。


from google.appengine.ext import ndb
import webapp2


COUNTER_KEY = 'default counter'


class Counter(ndb.Model):
    count = ndb.IntegerProperty(indexed=False)


class UpdateCounterHandler(webapp2.RequestHandler):
    def post(self):
        amount = int(self.request.get('amount'))

        # This task should run at most once per second because of the datastore
        # transaction write throughput.
        @ndb.transactional
        def update_counter():
            counter = Counter.get_or_insert(COUNTER_KEY, count=0)
            counter.count += amount
            counter.put()

        update_counter()


app = webapp2.WSGIApplication([
    ('/update_counter', UpdateCounterHandler)
], debug=True)

タスクの URL /update-counter からクラス UpdateCounterHandler へのマッピングは WSGIApplication の中で行っています。

worker.yaml ファイルで「worker」という名前のサービスを作成し、そのサービスにワーカーコードを追加します。login:admin を指定しているため、ハンドラの URL はセキュアです。

runtime: python27
api_version: 1
threadsafe: true
service: worker

handlers:
- url: /.*
  script: worker.app
  login: admin

タスクキューでは、ハンドラのレスポンスに HTTP コードを使用し、タスクが正常に実行されたかどうかを判断します。ハンドラからのレスポンスを認識するのはタスクキュー サービスのみであり、タスクが正常終了したかどうかを判定するためにのみ使用されます。レスポンスにあるその他のフィールドは、タスクキューではすべて無視されます。このサービスによってレスポンスは破棄されるので、元のアプリはどのデータも受け取りません。タスクが異常終了した場合は、タスクキュー サービスでタスクを再試行するために別のリクエストが送信されます。

ユーザーから渡されたデータをリクエストの中に入れてハンドラに配信することもできます。その場合はクエリ文字列として、またはリクエスト本体のペイロードとして配信します。ユーザーデータを挿入する方法の説明は、タスクを作成するをご覧ください。リクエストの中にデータがある場合は、そのデータがどのようにリクエストに挿入されたかをハンドラが知っている必要があります。データをリクエストから取り出すときに使用される実際のコードは、使用するウェブ フレームワークによって異なります。

タスクハンドラをテストするには、管理者としてログインし、ブラウザでハンドラの URL にアクセスします。

リクエスト ヘッダーの意味

push タスクの HTTP リクエストには App Engine によって特別なヘッダーが設定されます。ここには、ハンドラが使用できるタスク固有の情報が含まれています。

これらのヘッダーがアプリケーションへの外部ユーザー リクエストに含まれている場合、ヘッダーは削除されて置換されます。ただし、アプリケーションの管理者がログインしてリクエストした場合は例外で、テストするために、このヘッダーを設定できます。また、アプリが開発サーバーで実行されているときは、ヘッダーが削除されません。

タスクキューからのリクエストには、常に次のヘッダーが含まれます。

ヘッダー 説明
X-Appengine-QueueName キューの名前(デフォルトの push キューの場合はおそらく「default」)。
X-Appengine-TaskName タスクの名前。名前が指定されていない場合は、システムが生成した一意の ID。
X-Appengine-TaskRetryCount このタスクが再試行された回数。最初の試行の場合は、この値は 0 です。この試行回数には、インスタンス数不足が原因でタスクが異常終了したため実行フェーズに到達できなかった試行も含まれています。
X-Appengine-TaskExecutionCount このタスクがこれまでに実行フェーズ中に異常終了した回数。この回数には、インスタンス数不足が原因の失敗は含まれていません。
X-Appengine-TaskETA タスクの目標実行時間。1970 年 1 月 1 日からの秒数で表します。

リクエスト ハンドラが上記のヘッダーのいずれかを検出した場合、そのリクエストはタスクキュー リクエストであると判断できます。

さらに、タスクキューからのリクエストには、次のヘッダーを含めることができます。

ヘッダー 説明
X-Appengine-TaskPreviousResponse 前回の再試行の HTTP レスポンス コード。
X-Appengine-TaskRetryReason タスクを再試行する理由。
X-Appengine-FailFast 実行中のタスクを、既存のインスタンスが使用できない場合はすぐに失敗することを意味します。

タスクハンドラの URL をセキュリティで保護する

タスクで実行するオペレーション(データの変更など)の機密性が高い場合は、タスクが悪意のある外部ユーザーから直接呼び出されないよう、そのハンドラ URL をセキュリティで保護することをおすすめします。ユーザーがタスクの URL にアクセスできないようにするには、アクセスを App Engine 管理者のみに制限します。タスク リクエストそのものは App Engine によって発行されるため、制限付きの URL を常にターゲットにできます。

URL を制限するには、app.yaml ファイル内のハンドラ構成に login: admin 要素を追加します。

次に例を示します。

handlers:
- url: /your-task
  script: worker.app
  login: admin

次のステップ