【AWS】Lambda+SQSでサイロ + プールモデルを実現するためのディスパッチャ構成検討

## きっかけ
- 弊社が過去に開発し、現在も引き続き運用、追加開発をしているクライアントのSaaSがあり、:man_tone1: "特定のユーザが大量にリクエストしたときに他ユーザのリクエスト実行まで時間がかかるので、解消したい"とのリクエストをもらったから。

## 現状分析
- リクエスト受信部分はREST APIで構成、他ユーザのリクエストに処理時間は依存しない
- 実処理部分はリクエスト受信部分と非同期で処理を行っており、実処理が順次実行のため、この部分がボトルネック
- 以下サイトで分類されている分離パターンでは現状は、1. サイロモデル
 
https://aws.amazon.com/jp/builders-flash/202105/tenant-isolation/

- 特定処理、特定ユーザだけ分離できればいいので、3.サイロ + プールモデルを目指したい

## 検証で目指す構成
- リクエストを受けるSQS→各リクエストを割り当てるDispatcher用Lambda→特定ユーザ用SQS,汎用ユーザ用SQSとそれぞれ実処理Lambdaをつなげる(本記事では実処理Lambdaは省略)
![dispatcher_Structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/9f5d6797-96b9-1087-836d-c094f93fa57b.png)

## 構築Step
1. リクエスト受信SQSとDispatcher用Lambdaをつなぐ
1. Dispatcher用LambdaからCompanyA用SQSへメッセージを格納する
1. Dispatcher用Lambdaで受信メッセージに応じて格納する

### 1.リクエスト受信SQSとDispatcher用Lambdaをつなぐ
![dispatcher_Structure1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/b1fd21a4-b9de-801d-4809-ff2b17375fb0.png)

- リクエスト受信SQSを作成(fifoキュー、重複削除のみ設定)
![screencapture-ap-northeast-1-console-aws-amazon-sqs-v2-home-2021-08-03-09_08_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/bc33ed3e-bf49-1caa-7472-afd8360addf4.png)

- Dispatcher用Lambdaを作成(Node.js 14.xで構成)
![screencapture-ap-northeast-1-console-aws-amazon-lambda-home-2021-08-03-09_09_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/6f877c4c-ed82-34e0-9e35-3ce28ef03f22.png)

- Lambdaのソースコードを以下に修正、デプロイ

```javascript:index.js
exports.handler = async function(event, context) {
  event.Records.forEach(record => {
    const { body } = record;
    console.log(body);
  });
  return {};
};
```

- Lambda実行ロールに"AmazonSQSFullAccess"を付与
![1スクリーンショット 2021-08-03 092021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/c3dbe702-4a82-010b-6d64-db9ec2b35440.png)

- Lambda設定からリクエスト受信用SQSをトリガーとして設定
![スクリーンショット 2021-08-03 092131.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/8621160f-0030-381b-9612-4e496e8104be.png)

- 動作確認 SQSでメッセージを送信→Lambdaでログを確認
![スクリーンショット 2021-08-03 092628.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/64a63187-ba29-0b0a-32b7-e87170d9189e.png)
![スクリーンショット 2021-08-03 092708.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/a2897de0-a3e0-aea4-804d-bb21a08add77.png)

### 2. Dispatcher用LambdaからCompanyA用SQSへメッセージを格納する
![dispatcher_Structure2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/4bcc77b7-cde7-bceb-c6b6-a4e01cfd7efc.png)

- CompanyA用SQSを作成(fifoキュー、重複削除のみ設定)
![screencapture-ap-northeast-1-console-aws-amazon-sqs-v2-home-2021-08-03-09_28_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/3b6a3795-7e02-51ec-c9f6-9f73c6f9da22.png)

- Lambdaのソースコードを以下に修正、デプロイ

```javascript:index.js
const aws = require('aws-sdk');
aws.config.region = 'ap-northeast-1';
const sqs = new aws.SQS({apiVersion: '2012-11-05'});

const QUEUE_URL = 'https://sqs.ap-northeast-1.amazonaws.com/XXXXXXXXXX/companyA.fifo';

exports.handler = async function(event, context) {

        const recs = event.Records;
        
        for (let rec of recs){
            const body = rec.body;
            console.log(body);
            
            const params = {
                MessageBody: body,
                QueueUrl: QUEUE_URL,
                MessageGroupId:rec.attributes.MessageGroupId,
                DelaySeconds: 0
            };

            console.log(params);
            
            const result = await sqs.sendMessage(params).promise();
            console.log(result)
        }
};
```

- 動作確認 リクエスト受信用SQSへメッセージを送信→Lambdaのログを確認→CompanyA用SQSにメッセージが格納されていることを確認
![スクリーンショット 2021-08-03 093159.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/a7cb1bca-5492-6eb8-84dc-63300a547668.png)
![スクリーンショット 2021-08-03 093736.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/1841e8e5-c722-a9f3-84dc-9001a4840b09.png)
![スクリーンショット 2021-08-03 093903.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/a145387c-8a41-86d5-f7ae-e5ce98e25c9c.png)

### 3. Dispatcher用Lambdaで受信メッセージに応じて格納する
![dispatcher_Structure3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/318f32eb-aef0-8cd5-9dbb-63b7f8212a43.png)

- CompanyOTHER用SQSを作成
![screencapture-ap-northeast-1-console-aws-amazon-sqs-v2-home-2021-08-03-09_46_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/2fd1bd84-6407-b1f5-7f21-b33b4cd36938.png)

- Lambdaのソースコードを以下に修正、デプロイ

```javascript:index.js
const aws = require('aws-sdk');
aws.config.region = 'ap-northeast-1';
const sqs = new aws.SQS({apiVersion: '2012-11-05'});

const companyA_QUEUE_URL = 'https://sqs.ap-northeast-1.amazonaws.com/XXXXXXXXXX/companyA.fifo';
const companyOTHERS_QUEUE_URL = 'https://sqs.ap-northeast-1.amazonaws.com/XXXXXXXXXX/companyOTHER.fifo';
let queue_url = '';

exports.handler = async function(event, context) {

        const recs = event.Records;
        
        for (let rec of recs){
            const body = rec.body;
            console.log(body);
            
            const body_parse = JSON.parse(body);
            
            if(body_parse.company_code === '0001'){
                queue_url = companyA_QUEUE_URL;
            } else {
                queue_url = companyOTHERS_QUEUE_URL;
            }
            
            const params = {
                MessageBody: body,
                QueueUrl: queue_url,
                MessageGroupId:rec.attributes.MessageGroupId,
                DelaySeconds: 0
            };

            console.log(params);
            
            const result = await sqs.sendMessage(params).promise();
            console.log(result)
        }
};
```
- 動作確認 リクエスト受信用SQSへメッセージを送信(company_codeで送信先制御)→Lambdaのログを確認→CompanyA用SQSにメッセージが格納されていることを確認
![スクリーンショット 2021-08-03 100342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/aaba154d-0166-010e-f4db-e5a3c531a857.png)
![スクリーンショット 2021-08-03 100403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/7a46151e-4431-efbc-191b-05b96d4ab312.png)
![スクリーンショット 2021-08-03 100436.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/70764c8e-8d1d-0e11-0fc1-e102bff725c3.png)
![スクリーンショット 2021-08-03 100644.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/46c8de82-0fde-9c2a-7f8e-24f4defcb6af.png)
![スクリーンショット 2021-08-03 101122.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479886/e0236346-295c-3409-19e5-37a5dd9d69d9.png)

## 参考サイト

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-sqs-create-package.html

https://aws.amazon.com/jp/builders-flash/202105/tenant-isolation/