尝试使用 mysql2/promise,在VPC内的Lambda函数通过RDS Proxy连接到RDS [Node.js]

首先

我們在 VPC 內運行了通過 RDS Proxy 對 MySQL 進行訪問的 Lambda,以下是步驟摘要。

以前我們使用了mysql模塊來執行類似的處理,但由於mysql2模塊的代碼量較少,這次我們將嘗試使用mysql2模塊。

 

スクリーンショット 2022-06-23 22.00.58.png

创建RDS代理

请参考我之前写的关于RDS Proxy的文章。

 

在私有子网中创建Lambda函数。

设定

IAMロール

AWSLambdaENIManagementAccessを付与します.これによってVPC上でLambdaが実行できます。

タイムアウト時間

30秒にします。実行に数秒かかります。

環境変数

RDS_HOSTNAME:proxyのライターエンドポイント
RDS_DB_NAME:データベース名
RDS_USERNAME:ユーザー名
RDS_PASSWORD:パスワード

VPC

private subnetに設置
sgの設定は以下の通り

設定タイプポートソースLambdaAllAll0.0.0.0/0ProxyMySQL/Aurora3306LambdaのSGAuroraMySQL/Aurora3306ProxyのSG

mysql2模块

Lambda使用Node.js进行开发。

为了使用mysql2模块,在本地安装mysql2模块。

在LambdaLayer中上传

$ mkdir nodejs
$ cd nodejs
$ npm init -y
$ ls
package.json

$ npm install mysql2

$ tree
.nodejs
├── node_modules
├── package-lock.json
└── package.json]

$ cd ..

$ zip -r nodejs.zip nodejs

将整个nodejs文件夹打包为zip文件。
将打包好的nodejs.zip上传到LambdaLayer。

请参考之前创建的文章来了解具体的上传方法。

 

直接将文件上传到Lambda

如果不使用 Lambda Layer,则可以按照以下方式进行创建。

$ mkdir nodejs
$ cd nodejs
$ tree
.nodejs
├── package-lock.json
└── package.json

// package name以外はデフォルトにしました
$  npm init -y

$ npm install mysql

$ touch index.js

$ tree
.nodejs
├── index.js
├── node_modules
├── package-lock.json
└── package.json

$ zip -r nodejs.zip *
スクリーンショット 2022-11-04 12.56.40.png

Lambda代码

如果是插入的情况下

const hostname = process.env.RDS_HOSTNAME;
const user_name = process.env.RDS_USERNAME;
const password = process.env.RDS_PASSWORD;
const databaseName = process.env.RDS_DB_NAME;
const mysql = require('mysql2/promise');

exports.handler = async (event) => {
  // console.log('event:', JSON.stringify(event, null, 2));

  const connection = await mysql.createConnection({
    host: hostname,
    user: user_name,
    password: password,
    database: databaseName,
  });

  const now = new Date().toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
  const sql = 'INSERT INTO database_table (title, created_at) VALUES (?, ?);';
  const inserts = ['test', now];

  try {
    await connection.beginTransaction();

    const results = await connection.execute(sql, inserts);
    console.log(results);

    await connection.commit();
  } catch (error) {
    await connection.rollback();
    console.log(error.message);
    throw error;
  } finally {
    connection.end();
  }
};

这次,我尝试将标题和创建日期插入到名为database_table的数据库表中。
‘INSERT INTO database_table(title,created_at)VALUES(?,?);’

建议使用mysql2模块,因为与之前在mysql模块编写的代码相比,代码量更少。

 

mysql2的使用方法

createConnection

RDSのmysqlと接続します

beginTransaction

トランザクションを開始します

query

クエリを実行します

commit

トランザクション中のデータ処理を確定します

rollback

トランザクション中のデータ処理を取り消され、トランザクション実行前の状態に戻りトランザクションを終了します。

end

データベースと接続を終了します。

如果你在SQL中使用SELECT语句-选项1

如果使用SELECT语句而不是INSERT,可以得到以下结果。
这是一个简单的例子,从users表中获取所有数据。

const dbInfo = {
  host: process.env.RDS_HOSTNAME,
  user: process.env.RDS_USERNAME,
  password: process.env.RDS_PASSWORD,
  database: process.env.RDS_DB_NAME,
};
const mysql = require('mysql2/promise');

exports.handler = async (event) => {
  const connection = await mysql.createConnection(dbInfo);

  const usersSql = 'SELECT * FROM `users`';
  try {
    await connection.beginTransaction();

    const usersSqlResults = await connection.query(usersSql);
    console.log('Received usersSqlResults[0]:', JSON.stringify(usersSqlResults[0], null, 2));

    await connection.commit();
  } catch (error) {
    await connection.rollback();
    console.log(error.message);
    throw error;
  } finally {
    connection.end();
  }
};

对于SELECT语句,选择第二个选项(②)。

将多个店铺的ID各自添加到数组中,并返回该数组的内容。
条件是店铺的营业时间早于12点并且闭店时间晚于20点。

// db情報は、変数にしてもよい
const dbInfo = {
    host: process.env.RDS_HOSTNAME,
    user: process.env.RDS_USERNAME,
    password: process.env.RDS_PASSWORD,
    database: process.env.RDS_DB_NAME,
};
const mysql = require('mysql2/promise');

const getStoreIds = [23, 25, 29];
const startTime = 'start_time_Monday';
const endTime = 'end_time_Monday';

exports.handler = async (event) => {
  let userId = [];
  for (const storeId of getStoreIds) {
    const connection = await mysql.createConnection(dbInfo);

    const storesInserts = [storeId];
    const storesSql =
      'SELECT `user_id` FROM `stores` WHERE `store_id` = ? AND ' + startTime + ' < 12:00 AND ' + endTime + ' > 20:00;';
    try {
      await connection.beginTransaction();

      const storesSqlResults = await connection.query(storesSql, storesInserts);
      console.log('Received storesSqlResults[0]:', JSON.stringify(storesSqlResults[0], null, 2));

      // 値がなければスキップ
      if (!storesSqlResults[0][0]) continue;

      // 返ったuser_idを配列に加える
      storesSqlResults[0].map((item, index) => {
        userId.push(item.token);
      });

      await connection.commit();
    } catch (error) {
      await connection.rollback();
      console.log(error.message);
      throw error;
    } finally {
      connection.end();
    }
  }
  console.log('Received userId:', JSON.stringify(userId, null, 2));
  return userId;
};

storesSql中的?将被替换为storesInserts。

假如想要在SQL的表名和列名中使用变量,可以将其作为字符串连接在SQL内部使用。
比方说,本次是startTime和endTime。

参考文献

 

广告
将在 10 秒后关闭
bannerAds