Lambda × SQSでのサーバーレスアーキテクチャの構築

## はじめに
先日、AWSのLambdaとSQSを用いた、サーバーレスな設計で、あるアプリケーションを作成したので、その時のアーキテクチャや、実装面で工夫した点をまとめました！
基本的なアーキテクチャですが、前提知識が少し必要なので、やや中級者向けの記事になります！

こういう処理を走らせたいけど、お金がかかるしサーバーを動かし続けたくないなあ、、
という方は、ぜひlambdaを活用してみてはいかがでしょうか！

## アプリの概要
作成したアプリは、大枠では下記の処理を順番に実行するものです。
1. 処理する対象のURL一覧を取得する処理
2. URLに登録されている文章を取得する処理
3. 文章を要約する処理
4. 要約された文章を、S3に登録する処理

周期を設定したEventBridgeをトリガーとして、この処理を開始します。
2番目以降の処理から、lambdaの同時実行の力とSQSの特性を活かして、並行処理を実施します。

## アーキテクチャ
![スクリーンショット 2023-10-01 15.53.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351273/e3eda768-7d16-907f-7390-49b7bc4c5c5d.png)

### 処理の詳細
1の処理の最後に、URLを1つずつQueueに格納する処理にすることで、URL単位でメッセージを送信することができ、その後の処理をメッセージ単位（URL単位）で実施することができます！

あまり触れたことない方はイメージしづらいかもしれませんが、例えば、100件のURLを処理する必要がある場合、URLを100件取得し、メッセージに登録する最初の処理は100回ループする必要があります。
しかし、その後の処理は、100件のURLを、100個のlambdaで並行処理することができるようにしています。
よくあるSQSの使用方法ですね！

### コードの例
SQSにアクセスし、情報をQueueに格納する処理の参考コードは以下になります！
YOUR_ACCESS_KEY_IDなどの情報は、別途lambdaの環境変数の設定画面から設定する必要があります！
```python:main.py
from sqs_connect import post_queue

def handler(event, context):

  # 今回はURLリストを取得するメソッドは割愛
  url_list = get_url_list()

  #SQS送信
  for url in url_list:
    post_queue(url)
```

```python:sqs_connect.py
import boto3
import json
import os
import logging

def post_queue(url:str):
    #json形式なので、dicに入れる
    data_for_sqs = {"url": url}

    #jsonに変換
    message = json.dumps(data_for_sqs, ensure_ascii=False)

    #sqsと接続
    try:
        sqs = boto3.client('sqs',
                aws_access_key_id=os.environ["YOUR_ACCESS_KEY_ID"],
                aws_secret_access_key=os.environ["YOUR_SECRET_ACCESS_KEY"],
                region_name='ap-northeast-1'
                )

        # メッセージをSQSに送信
        response = sqs.send_message(
            QueueUrl=os.environ['YOUE_QUEUE_NAME'],
            MessageBody=message
            )

        logging.info(f'Successfully sent message: {response}')

    except Exception as e:
        logging.error(f'Failed to send message: {e}')
```

後続のlambdaで、SQSからメッセージを受け取るコードは以下になります！
```python:main.py
def handler(event, context):
    
    #SQSから要素を取得
    queue_info =  json.loads(event['Records'][0]['body'])

    url = queue_info.get('url')

    #ここから、取得したURLに対する後続の処理を記載
```

## ソリューションの特徴

### 完全サーバーレス
EC2インスタンスなどを使用していないため、サーバーを管理する必要がありません。
処理が走っていない時に料金は発生せず、監視も不要なので、運用面で非常に楽に管理することができます。

### コスト
lambdaは1ヶ月あたり、100万件分のリクエストが無料であり、その後も100万リクエストあたり0.2USDで使用可能です。
また、SQSも同様に毎月100万件まで無料で実施可能です。
その後の料金も100万-1億リクエストまでは、100万リクエストあたり0.4USDで使用できます。
このように、lambdaとSQSを用いたサーバーレスアーキテクチャは、非常にコストエフェクティブなソリューションの1つです！

### lambdaの同時実行の特性
lambdaはデフォルトで1アカウントに対し、1リージョンで1000件まで同時実行することが可能です。
処理単位を細かくlambdaで分けることにより、素早く処理を完了することができます！

### インターフェースの統一と転用
このようなアーキテクチャで処理を実施するにあたり、インターフェースを工夫しました。
SQSとlambda間でやり取りをするインターフェース項目を、json形式で設定するのですが、これらのキー値を全体的に統一しています。
SQSのjsonに載せるキー名を、SQSや内部のコードで統一し、SQSから受け取る際のコードを転用できるようにすることで、開発効率を向上させることができます！

また、URL以外から文章を取得する必要があるケースも、今後の拡張としてあったため、3の文章を要約する処理へのインターフェースを拡張しやすいように設計して開発したことにより、今後文章の取得元が増えても、コード修正不要で3と4のメソッドを使用することができます。
lambdaで処理を細かく分けることで、影響範囲の制限を簡略化することができます！

## 参考
lambda料金について: https://aws.amazon.com/jp/lambda/pricing/
lambdaの無料枠について: https://aws.amazon.com/jp/lambda/pricing/?loc=ft#Free_Tier
SQSの料金について: https://aws.amazon.com/jp/sqs/pricing/
lambdaの同時実行について: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-concurrency.html