S3をトリガーにしてLambdaでSQSにメッセージを送信する(Python)

## はじめに
ひとまず、S3に新しいファイルを保存した際にLambdaを起動したり、SQSにメッセージをキューイングしたりしたときの手順。初歩的な部分から書いています。ところどころぼかしあります。
SQSにキューイングして処理する部分については[こちら](https://qiita.com/ybsh2891/items/c137660f72007b73dbe1)。

## 目標
図の通り、S3にcsvファイルが入ったらLambda経由からSQSにメッセージを投げる仕組みを作成する。
![s3tosqs.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/904ab2ec-148b-906a-f3df-d3d47bf61789.jpeg)

## 1. SQSでキューを作成する
### Amazon SQSでキューを作成
「キューを作成」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/ce3444df-8d6e-45dc-0e3b-61930b96e1df.png)

### 作成するキューのタイプを指定
今回は順番に処理してほしかったので、タイプで「FIFO」を選択した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/fde15bcb-1c0f-72aa-4cef-7508bd2a9556.png)
標準タイプだと、トランザクション(TPS)に制限はないが、順番が保証されなかったり、複数メッセージのコピーが送信されることがある。
FIFOタイプだと、トランザクションに制限はあるが、順番が保証され、重複がなくなる。
FIFOの場合は名称の末尾を`.fifo`にする。

詳細は以下を参照。
[【新機能】Amazon SQSにFIFOが追加されました！](https://dev.classmethod.jp/articles/sqs-new-fifo/)
[【AWS】楽々SQS解説〜5分で理解〜](https://qiita.com/UpAllNight/items/7c5022661a1eadc8a88a)

:::note info
標準タイプのキューを作成する際の注意点などはこちら参照。
[Amazon SQSを使う前に知っておきたい基本的なこと](https://qiita.com/tomoya_ozawa/items/dc0286cdb30763eab174)
:::

### キューの設定
タイムアウト時間などを設定する。
メッセージ受信待機時間を20秒にしてロングポーリングにする。
[Amazon SQS ロングポーリングを理解する](https://qiita.com/taka_22/items/718ec340a710bbf5e3d0)
[AWS SQSの性能を調査しました。 (ロングポーリングが良いよ & FIFOスループット注意 & 並行で受けよう)](https://qiita.com/momotaro98/items/30533f4133d08459d8f6)


FIFOの場合、「コンテンツに基づく重複排除」をONにしておく。
[AmazonCloudWatchイベントがSQS FIFOに対応したようなのでただただツッコんでみただけの話](https://qiita.com/LightSilver7/items/fdf661e157539061fda9)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/2dd860e1-f369-a598-f53b-96cff18619eb.png)

その他の設定項目はAWSのサイト参照。
[キューパラメータの設定 (コンソール)](https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-queue-parameters.html)


## 2. Lambda関数を作成し、トリガーにS3を設定する
### Lambda関数を作成
関数名と使用する言語を指定(今回はPython)して、関数を作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/1d586664-3f3f-6c69-a92d-2e34e46d5675.png)

### S3をLambda起動のトリガーにする
トリガーを追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/68bc254b-98c2-ce38-7f97-8c8e80403ee4.png)
トリガーのサービスとしてS3を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/c9ff60b5-48ce-681e-9aba-9c2e6a046f2f.png)
バケット、監視するイベントタイプを指定する。
プレフィックスで監視したいファイルパス(キー)の先頭文字(フォルダパスなど)、サフィックスで監視したいファイルパスの末尾(拡張子など)を指定する。
![s3setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/80bf6a3c-7c8a-fd41-223b-a8fe5f4aedfd.png)
このようにつながればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/a441cb8d-7f34-3e8d-bf17-ad25c96a923a.png)

## 3. LambdaにSQSにへのアクセス許可を付与する。
### SQSにアクセスを許可するポリシーを作成する
IAMから、ポリシーの作成を選択(IAM権限が必要)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/98f8fba2-f102-93f8-2211-0fcd43d0b55e.png)

サービス(SQS)と、許可するアクションを指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/205aea1c-05bd-75d1-d19c-71901a253bea.png)

リソースは、「ARNの追加」から作成したキューを指定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/7c37af79-cac4-a23e-64a4-d213a5ab8e00.png)

「ARNの指定」の欄に、キューの詳細画面からARNの値をコピーする。あとは名称、タグなどを設定してポリシーを作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/905067c4-5555-5d39-869c-505cab55e40e.png)

### Lambdaの実行ロールに作成したポリシーを付与する
作成したLambdaの「アクセス権限」から、実行ロールを選択する。
![lambda_role.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/ff5fc6cf-1ac0-724b-9473-1a8f159e3a66.png)

「アクセス許可を追加」→「ポリシーをアタッチ」で、作成したポリシーを選択し、アタッチする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/b0e3c4f8-e44e-197f-5e6a-c274eaf36186.png)


## 4. Lambda関数からSQSにメッセージを送信
### 環境変数を設定
「設定」→「環境変数」で設定可能。
キー：「SQS」、値：送信先のキューの名前 で設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/a421a723-6709-ebdd-17eb-265e4e496946.png)

### SQSを呼び出すコードを作成
Lambdaが起動すると、`lambda_function.py`の関数`lambda_handler`が呼び出される。
引数として、`event`、`context`のオブジェクトがトリガーのイベントから投げられる。
`event`はJSON形式で、トリガーのS3の情報などが入っている。

今回は、S3のbucket名と、新規ファイルのパス(キー)をSQSに送信する。

```python:lambda_function.py

import json
import urllib3
import os
import boto3

def lambda_handler(event, context):

    # eventオブジェクトからトリガーのbucket_nameを取得
    bucket_name = event['Records'][0]['s3']['bucket']['name']
    # eventオブジェクトからトリガーになった新規ファイルのkey(ファイルパス)を取得
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    
    # boto3でSQSへアクセスするオブジェクトを取得
    sqs = boto3.resource('sqs')

    # メッセージを投げるキューの名前を環境変数から取得
    name = os.environ['SQS']
    try:
       # キューの名前を指定してインスタンスを取得
       queue = sqs.get_queue_by_name(QueueName=name)
    # キューがない場合はexceptionを返却
    except:
       return {
          'statusCode': 500,
          'body': json.dumps('SQS_No_Exist')
       }

    # キューに送信するメッセージをdict形式で作成
    queue_msg = {
        "bucket_name": bucket_name,
        "key": key,
    }
    
    id = "0001" # MessageGroupIdを指定(指定しないとエラーになる)
    
    # メッセージをキューに送信
    response = queue.send_message(MessageBody = json.dumps(queue_msg), MessageGroupId = id)
    print(response)
   
    # 送信完了のメッセージをログに返す
    return {
        'statusCode': 200,
        'body': json.dumps('Request is sent.')
    }

```

### eventオブジェクトの例
`event`オブジェクトの構造は以下のようになっている。
(Lambdaのテスト画面から確認可能)

```json
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "example-bucket",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::example-bucket"
        },
        "object": {
          "key": "test%2Fkey",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
```

### テスト
テストイベントを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/296c412b-4301-44ce-7f83-975937e4b4a9.png)

テスト内容の詳細を設定する。テンプレートを「s3-put」にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/e80e1608-9ee0-a1f3-e995-e1aa9437a3a6.png)

テストを選択し、「Test」ボタンでテスト実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/1e4c92bf-f73b-a540-49e5-579d328b2f65.png)

「Execution result」からログを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/7e24fcff-fdc5-cf10-4bba-83fcd1ff54ed.png)

作成したキューの「メッセージを送受信」から、メッセージの増加を確認可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/5a772ac5-f76b-08c9-ef16-5f4407d65373.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/1dd64c25-1c55-e371-a25e-e6a6a22ed1e8.png)

## その他参考
JavaScriptの場合はこちらの記事が参考になります。

[【 AWS 】S3 → Lambda → SQS にメッセージを投げるまでの構築手順！](https://qiita.com/Futo_Horio/items/92a92f3ebb3fbbf95572)