知らせを待つ関数
Lambda ハンドラーは、何かが起きたときに、クラウドのサービスが代わりに呼んでくれる関数です。たとえば、写真が S3 バケットに届いたときです。知らせは、イベントという辞書で届きます。
このモジュールでは、ハンドラーを書き、自分のパソコンで試します。何もどこにもデプロイせず、イベントは手で作ります。
このレッスンのゴール
メモ
イベントを読み、結果を返すハンドラーを、書くことができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| ハンドラー | サービスが呼ぶ関数 | handler(event, context) |
| イベント | 何が起きたかを書いた辞書 | |
| レコード | イベントの知らせの並びの、1件 | |
| フィクスチャ | テスト用に取っておく、作り物のイベント | |
| 冪等 | 2回実行しても、同じ結果になること |
学生が入力するセルは、このプレビューでは空白です。
ハンドラーは、イベントとコンテキストを受け取る
ハンドラーは、引数が2つある、ふつうの Python の関数です。event は知らせを持ちます。context は、実行中の関数についての事実を持ちます。たとえば、何秒まで動いてよいか、です。
関数は結果を返します。ただの関数なので、作り物のイベントで、自分で呼ぶことができます。
どこにもデプロイしない
メモ
このモジュールでは、関数を1つもアップロードしません。ここのイベントの形は、関数が受け取る、文書にある形で、あなたのスタックの出力ではありません。試すときは、手で作ったイベントを渡して、関数を呼びます。テスト用に取っておく、この作り物のイベントを、フィクスチャと呼びます。
実行: ハンドラーを手で呼ぶ
event = {
"Records": [
{
"eventSource": "aws:s3",
"eventName": "ObjectCreated:Put",
"s3": {"bucket": {"name": "shop-uploads"}, "object": {"key": "images/cat.png", "size": 1024}},
}
]
}
def handler(event, context):
first = event["Records"][0]
bucket = first["s3"]["bucket"]["name"]
key = first["s3"]["object"]["key"]
return {"stored": bucket + "/" + key}
print(handler(event, None))
# key を images/dog.png に変えて、もう一度実行しましょう。
イベントは、レコードの並びを持つ
1つのイベントが、何件ものレコードを運ぶことがあります。だから、ハンドラーは Records を順に回します。各レコードは、eventSource で、どこから来たかを言います。
S3 のレコードは、バケットとキーを言います。キューからの SQS のレコードは、body を持ちます。body は文字列なので、JSON の body は、json.loads で読んでから使います。
例: 2種類のレコード
| 項目 | S3 のレコード | SQS のレコード |
|---|---|---|
| eventSource | aws:s3 | aws:sqs |
| 何が起きたか | eventName。たとえば ObjectCreated:Put | body にあるメッセージ |
| どこを見るか | s3.bucket.name と s3.object.key | body。JSON の文字列 |
| 一意な id | 頼れるものはない | messageId |
学生が入力するセルは、このプレビューでは空白です。
実行: 2種類のレコードを読む
import json
event = {"Records": [
{"eventSource": "aws:sqs", "messageId": "m-1", "body": "{\"order\": 1, \"item\": \"tea\"}"},
{"eventSource": "aws:s3", "eventName": "ObjectCreated:Put",
"s3": {"bucket": {"name": "shop-uploads"}, "object": {"key": "logs/web.log"}}},
]}
def handler(event, context):
seen = []
for record in event["Records"]:
if record["eventSource"] == "aws:sqs":
order = json.loads(record["body"])
seen.append("注文 " + str(order["order"]))
elif record["eventSource"] == "aws:s3":
seen.append("ファイル " + record["s3"]["object"]["key"])
return seen
print(handler(event, None))
同じ知らせを2回受けることがある
キューは、少なくとも1回の配信を約束します。同じメッセージが、関数に2回届くことがあります。たとえば、実行が遅かったあとです。
2回目の実行で何も変わらないとき、ハンドラーは冪等です。終えた仕事を書き留め、すでに書いてある仕事は飛ばします。本物の関数は、その記録を表に置き、あとのパイプラインの回がそれを使います。ここでは、集合がその代わりをします。
よくある間違い
注意
各メッセージは、1回だけ届く、と考える人が多いです。届きません。配信は、少なくとも1回です。ハンドラーを、2回実行しても安全にしておかないと、1件の注文に、2回請求することになります。
実行: すでにした仕事を飛ばす
def handle(orders, done):
newly = []
for order in orders:
if order in done:
continue
done.add(order)
newly.append(order)
return newly
done = set()
print(handle([1, 2, 2], done))
print(handle([2, 3], done))
# 2つ目のリストを [4, 1] に変えましょう。Run の前に、2行目を予想しましょう。