EC2からAmazon SQSにメッセージを送り、RDSのDBに保存する方法

## 内容
EC2からAmazon SQSを経由してRDSにデータを保存する方法をまとめます。
RDSのダウンタイム中にRDSに保存処理をしようとするとエラーになり、そのデータが失われます。
そのため、ダウンタイム中はSQSにメッセージをためておいて、RDSが起動したタイミングでデータを保存することにします。

こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/349d0f2e-814b-9c1e-e9a6-b039adb77e8a.png)



前回作成したものを使用します。

https://qiita.com/shogo30/items/d2e0fa2e7ecafc690952

https://qiita.com/shogo30/items/d43c281512bf321f4f66


## Amazon SQSでキューを作成する
Amazon SQSのダッシュボードから「キューの作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/99f72371-3592-9979-dd65-4119330e8af8.png)

今回は「標準」を選択します。
FIFOとの違いは、FIFOがメッセージの順番を約束してくれるのに対し、標準は保証されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/ff88b199-cc6c-6bee-9af5-5ed60e823401.png)

以上です。

## SQSにメッセージを送信する
### AWS SDK for PHPのインストール
前回作成したindex.phpからAmazon SQSにメッセージを送信する処理を追加します。
今回はAWS SDK for PHPを使用します。
EC2にsshし、``/var/www/html/``で↓を叩きます。
```
[ec2-user@ip-10-0-0-102 html]$ sudo composer require aws/aws-sdk-php
[ec2-user@ip-10-0-0-102 html]$ composer show -i
　　　　→aws/aws-sdk-phpがあればOK
```
これでAWS SDK for PHPが使用可能になりました。

そして作成したSQSのキューのURLをコピーを確認します。
![スクリーンショット 2023-10-29 8.13.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/3b19d2ee-ff9b-8f06-a3a6-45cb67d2f75e.png)

``/var/www/html/index.php``に↓を追記します。

```
<?php
require_once 'vendor/autoload.php';

use Aws\Sqs\SqsClient;
use Aws\Exception\AwsException;

// AWSの認証情報
$credentials = new Aws\Credentials\Credentials([アクセスキーID], [アクセスキー]);

$sqsClient = new SqsClient([
    'version' => 'latest',
    'region' => '[リージョンのコード]',
　　　　　　　　'credentials' => $credentials,
]);

$queueUrl = [キューのURL];

...

if ($post_message) {
    // メッセージを送信する処理
    $result = $sqsClient->sendMessage([
        'QueueUrl' => $queueUrl,
        'MessageBody' => $post_message,
    ]);

    $query = "INSERT INTO post (post) VALUES ('" . $post_message  . "')";
    $stmt = $dbh->query($query);
}

...
```
アクセスキーIDとアクセスキーをベタ打ちしていますが、本当にセキュリティ上やばいのでやめましょう。
今回だけです。

アクセスキーIDとアクセスキーを取得するには、
``AWSのダッシュボード→セキュリティ認証情報→アクセスキーを作成``
です。

ではブラウザからアクセスしてメッセージを送信してみましょう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/eadcab45-4e24-1017-a28d-5c353aee62b9.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/3ba20537-b61d-e3e4-44f9-fd9437773d8f.png)
前回と同様に、送信したメッセージがDBに保存されました。

ではAmazon SQSも見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/322f23c9-b3d0-c011-13ec-365fe204b87e.png)

「利用可能なメッセージ」が1件あるのがわかります。
無事送信したメッセージがSQSに保存されていることがわかります。

## SQSからメッセージを受信する
/var/www/html/に``recieve.php``を作成します。
```
<?php
require 'vendor/autoload.php';

use Aws\Sqs\SqsClient;

$dsn = "mysql:host=[エンドポイント];dbname=[データベース名]";
$username = "ユーザー名";
$password = "DBのパスワード";
$dbh = new PDO($dsn, $username, $password);

// AWS認証情報の設定
$credentials = new Aws\Credentials\Credentials('アクセスキーID', 'アクセスキー');

// SQSクライアントの作成
$sqsClient = new SqsClient([
    'version' => 'latest',
    'region' => '[リージョンコード]', 
    'credentials' => $credentials,
]);

// メッセージを受信
$queueUrl = 'amazon SQSのURL';

if ($_POST['fetch']) {
    $result = $sqsClient->receiveMessage([
        'QueueUrl' => $queueUrl,
    ]);
}

// 受信したメッセージを処理
if (!empty($result['Messages'])) {
    foreach ($result['Messages'] as $message) {
        $messageBody = $message['Body'];
        
	echo $messageBody . "を受信しました"  . PHP_EOL;

        $query = "INSERT INTO post (post) VALUES ('" . $messageBody  . "')";
        $stmt = $dbh->query($query);

        // メッセージを削除
        $sqsClient->deleteMessage([
            'QueueUrl' => $queueUrl,
            'ReceiptHandle' => $message['ReceiptHandle'],
        ]);
    }
} else {
    echo '受信したメッセージはありません。';
}
?>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sqs</title>
</head>
<body>
    <form action="" method="post">
        <button name="fetch" value="1">メッセージを受信</button>
    </form>
</body>
</html>

```
何度も言いますが、アクセスキーなどをベタ打ちするのはやめましょう。

ではブラウザで実行してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/7579c027-bb0b-82d0-bd4a-da7bfd99f577.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/503c7ebb-9f6e-1134-93a6-033be4f95473.png)
先ほど送信した「HELLO」というメッセージが取得できました。

ここでもDBに保存処理をしているので、index.phpを表示させると「HELLO」が２件保存されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/8965e75e-ac2c-5322-ddaa-b9bd033467e4.png)

## RDSのダウンタイム中に実行する
RDSのダウンタイムを想定してこの処理を実行してみます。
RDSのダウンタイム中はDBの接続情報をコメントアウトしておきましょう。

ブラウザで表示させると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/2e732832-9c26-ecf2-62d7-8cba9aa12b61.png)
（ダウンタイム中なのでDBのデータが取得できていません）

ではこの状態でメッセージを送信してみましょう。
「Good Morning」と入力して送信ボタンを押すと...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/4507e69f-5b8e-11b8-d883-3af3db3a996a.png)
ダウンタイム中ですし、DBへの接続を切っているのでうんともすんともなりません...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/78ceca69-61ca-edb3-fcd3-9e614ba378ed.png)

「私のあいさつがこの世から消えてしまったのか...?」

そんな心配はご無用です。
SQSをのぞいてみると、きちんとメッセージが保存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/4895b6d5-b022-89cd-144d-e9ab0db3c1b8.png)

ここでRDSのダウンタイムが終わり、再起動しました。
DBへの接続情報のコメントアウトを外します。

recieve.phpからメッセージを受信してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/e035b939-a2ef-0f2e-990d-76ba5c955698.png)
失ったと思われていた「Good Morning」が取得できました。
index.phpを確認すると、きちんとDBにも保存されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935700/4af6b089-e76f-6e52-7bb1-a2cfd656700f.png)

という感じで、RDSのダウンタイム中の処理を止めない方法を作ってみました。
とはいえSELECTができないので、完璧ではないですね。
また何か方法があるか探ってみます。