Qtの応用 - AWS Lambda
ナビゲーションに移動
検索に移動
概要
AWS SDK for C++のインストール
Qtの応用_-_AWS#AWS_SDK_for_C++のインストールのページを参照すること。
Lambdaクライアントの初期化
指定されたリージョンでLambdaクライアントを初期化する。
#include <aws/lambda/LambdaClient.h>
QString region = "<リージョン名>"
// Aws::Client::ClientConfiguration
// AWS SDKのクライアント設定を保持するオブジェクト
// リージョン、エンドポイント、タイムアウト、リトライ設定などを指定できる
Aws::Client::ClientConfiguration clientConfig;
// clientConfig.region
// Lambda関数が配置されているAWSリージョンを指定する
// 例 : "us-east-1", "ap-northeast-1", "eu-west-1"
clientConfig.region = region.toStdString();
// Aws::Lambda::LambdaClient
// AWS Lambda APIとの通信を行うクライアントクラス
// 全てのLambda操作 (Invoke, ListFunctions等) はこのクライアントを通じて実行される
auto lambdaClient = std::make_unique<Aws::Lambda::LambdaClient>(clientConfig);
Lambda関数の呼び出し
Lambda関数を呼び出し、レスポンスを取得する。
以下の例では、同期呼び出し、非同期呼び出し、ドライランに対応している。
#include <aws/lambda/LambdaClient.h>
#include <aws/lambda/model/InvokeRequest.h>
// Aws::Lambda::Model::InvokeRequest
// Lambda関数呼び出しリクエストを表すクラス
// 関数名、ペイロード、呼び出しタイプ、ログタイプ等を設定する
Aws::Lambda::Model::InvokeRequest request;
// SetFunctionName()
// 呼び出すLambda関数の名前またはARN(Amazon Resource Name)を指定する
// 例 : "my-function" または "arn:aws:lambda:us-east-1:123456789012:function:my-function"
request.SetFunctionName(functionName.toStdString());
// InvocationType (呼び出しタイプ) の設定
// Lambda関数の呼び出し方法を指定する列挙型
if (invocationType == "RequestResponse") {
// RequestResponse - 同期呼び出し
// 関数の実行完了を待ち、結果(レスポンスペイロード)を返す
// リアルタイムで結果が必要な場合に使用
request.SetInvocationType(Aws::Lambda::Model::InvocationType::RequestResponse);
}
else if (invocationType == "Event") {
// Event - 非同期呼び出し
// 関数をイベントキューに追加し、すぐに制御を返す(結果は返らない)
// 処理の完了を待つ必要がない場合に使用
request.SetInvocationType(Aws::Lambda::Model::InvocationType::Event);
}
else if (invocationType == "DryRun") {
// DryRun - テスト呼び出し
// 実際には関数を実行せず、アクセス許可のみを検証する
// 本番実行前の権限チェックに使用
request.SetInvocationType(Aws::Lambda::Model::InvocationType::DryRun);
}
// LogType(ログタイプ)の設定
// 実行ログの取得方法を指定する列挙型
if (logType == "Tail") {
// Tail - 最後の4KBの実行ログをBase64エンコードして返す
// デバッグ時に関数の実行状況を確認する場合に使用
request.SetLogType(Aws::Lambda::Model::LogType::Tail);
}
else {
// None - ログを返さない
// ログが不要な場合に使用 (デフォルト)
request.SetLogType(Aws::Lambda::Model::LogType::None);
}
// SetBody() - ペイロード (入力データ) の設定
// Lambda関数に渡すデータを設定する (JSON形式が一般的)
// Aws::MakeShared
// AWS SDKのメモリアロケータを使用してshared_ptrを作成する関数
// 第1引数は識別用のタグ (メモリトラッキング用)
std::shared_ptr<Aws::IOStream> payload_stream = Aws::MakeShared<Aws::StringStream>("PayloadStream");
*payload_stream << payload.toStdString();
request.SetBody(payload_stream);
// Invoke() - Lambda関数を実際に呼び出すメソッド
// 戻り値はOutcomeオブジェクト (成功 / 失敗の結果を保持)
//
// Outcome
// AWS SDK for C++の結果を表すテンプレートクラス
// 成功時はGetResult(), 失敗時はGetError()でアクセスする
auto outcome = m_lambdaClient->Invoke(request);
// IsSuccess() - 呼び出しが成功したかをチェック
if (outcome.IsSuccess()) {
// GetResult() - 成功時の結果オブジェクトを取得
const auto& result = outcome.GetResult();
// GetStatusCode()
// HTTPステータスコードを取得
// 通常、成功時は200が返る
int statusCode = result.GetStatusCode();
// GetFunctionError()
// Lambda関数内でエラーが発生した場合、エラータイプを返す
// "Handled" - 関数コード内でキャッチされたエラー
// "Unhandled" - 関数コードでキャッチされなかったエラー
// 空文字列 - エラーなし
std::string functionError = result.GetFunctionError();
// GetPayload()
// Lambda関数から返されたレスポンスデータのストリームを取得
// 通常、JSON形式のデータが返される
auto& payload_stream = result.GetPayload();
std::stringstream ss;
ss << payload_stream.rdbuf();
std::string response_payload = ss.str();
// GetLogResult()
// 実行ログ (Base64エンコード済み) を取得
// LogType::Tailを指定した場合のみ利用可能
// デコードすることで関数の実行ログ (print文やエラーメッセージ等) を確認できる
std::string logResult = result.GetLogResult();
return true;
}
else {
// GetError() - 失敗時のエラー情報を取得
// GetMessage() - エラーメッセージを取得
std::cerr << "✗ 呼び出し失敗: " << outcome.GetError().GetMessage() << std::endl;
return false;
}
Lambda関数の一覧の取得
アカウント内に存在するLambda関数の一覧を取得する。
関数の基本情報 (ランタイム、メモリ、タイムアウト等) も同時に取得することができる。
#include <aws/lambda/LambdaClient.h>
#include <aws/lambda/model/ListFunctionsRequest.h>
// Aws::Lambda::Model::ListFunctionsRequest
// Lambda関数一覧取得リクエストを表すクラス
// オプションで最大取得数 (MaxItems) や ページネーショントークン (Marker) を設定できる
Aws::Lambda::Model::ListFunctionsRequest request;
// ListFunctions()
// アカウント内のLambda関数一覧を取得するメソッド
// 指定したリージョン内の全ての関数情報を返す
auto outcome = m_lambdaClient->ListFunctions(request);
// IsSuccess() : 呼び出しが成功したかをチェック
if (outcome.IsSuccess()) {
// GetResult() - 成功時の結果オブジェクトを取得
const auto& result = outcome.GetResult();
// GetFunctions()
// Lambda関数設定のベクトル (std::vector) を返す
// 各要素はFunctionConfigurationオブジェクト (個々のLambda関数の設定情報)
const auto& functions = result.GetFunctions();
// 各関数の情報を取得
for (const auto& func : functions) {
// GetFunctionName()
// Lambda関数の名前を取得
// 例 : "my-function", "data-processor", "api-handler"
std::string functionName = func.GetFunctionName();
// GetHandler()
// Lambda関数のハンドラを取得
// ハンドラは関数のエントリーポイントを指定する
// 例 : "index.handler" (Node.js), "lambda_function.lambda_handler" (Python)
std::string handler = func.GetHandler();
// GetRuntime()
// Lambda関数のランタイム環境を列挙型で取得
// 例 : Python3_9, nodejs18_x, java17, dotnet6, go1_x, ruby3_2
Aws::Lambda::Model::Runtime runtime = func.GetRuntime();
// RuntimeMapper::GetNameForRuntime()
// Runtime列挙型を文字列表現に変換するユーティリティメソッド
// 例 : Runtime::Python3_9 → "python3.9"
std::string runtimeName = Aws::Lambda::Model::RuntimeMapper::GetNameForRuntime(runtime);
// GetMemorySize()
// Lambda関数に割り当てられたメモリサイズ (MB単位) を取得
// 設定可能範囲 : 128[MB]~10,240[MB] (10[GB])
// メモリサイズに比例してCPUパワーも増加する
int memorySize = func.GetMemorySize();
// GetTimeout()
// Lambda関数のタイムアウト時間 (秒単位) を取得
// 設定可能範囲: 1秒~900秒 (15分)
// この時間を超えると関数の実行が強制終了される
int timeout = func.GetTimeout();
// GetLastModified()
// 関数が最後に更新された日時をISO 8601形式で取得
// 例 : "2024-01-15T10:30:45.123+0000"
std::string lastModified = func.GetLastModified();
// GetFunctionArn()
// Lambda関数のAmazon Resource Name (ARN) を取得
// ARNは関数を一意に識別するグローバル識別子
// 例 : "arn:aws:lambda:us-east-1:123456789012:function:my-function"
std::string functionArn = func.GetFunctionArn();
// GetDescription()
// Lambda関数の説明文を取得
// コンソールやCLIで設定した関数の説明を返す
std::string description = func.GetDescription();
// その他にも取得可能な情報:
// - GetCodeSize() : 関数コードのサイズ (バイト単位)
// - GetRole() : 関数に関連付けられたIAMロールのARN
// - GetVersion() : 関数のバージョン番号
// - GetLayers() : 関数にアタッチされたレイヤーの情報
// - GetEnvironment() : 環境変数の設定
}
return true;
}
else {
// GetError() - 失敗時のエラー情報を取得
std::cerr << "✗ 取得失敗: " << outcome.GetError().GetMessage() << std::endl;
return false;
}
エラーハンドリング
全ての操作はOutcomeオブジェクトを返すため、必ず IsSuccess メソッドで確認する。
auto outcome = m_lambdaClient->Invoke(request);
// 必ず成功 / 失敗を確認
if (!outcome.IsSuccess()) {
// GetError() : エラー情報オブジェクトを取得
auto error = outcome.GetError();
// GetErrorType() : エラーの種類を列挙型で取得
// 例 : INVALID_PARAMETER_VALUE, RESOURCE_NOT_FOUND, ACCESS_DENIED
std::cerr << "エラーコード: " << error.GetErrorType() << std::endl;
// GetMessage() : エラーの詳細メッセージを取得
// ユーザーに表示可能な分かりやすいエラー説明
std::cerr << "メッセージ: " << error.GetMessage() << std::endl;
// GetExceptionName() : AWS APIが返した例外の名前を取得
// 例 : "ResourceNotFoundException", "InvalidParameterValueException"
std::cerr << "例外名: " << error.GetExceptionName() << std::endl;
}
サンプルコード
以下の例では、AWS Lambdaの関数呼び出しとレスポンスの取得を行っている。
全ての設定 (リージョン、関数名、ペイロード) を任意の設定ファイルから読み込んでいる。
- 呼び出しタイプ
- RequestResponse
- 同期呼び出し
- 関数の実行結果を待ってレスポンスを返す。
- Event
- 非同期呼び出し
- 関数をキューに入れてすぐに制御を返す。
- DryRun
- テスト呼び出し
- 関数を実行せずにアクセス許可を検証する。
- RequestResponse
- ログタイプ
- Tail
- 最後の4KBの実行ログをBase64エンコードして返す。
- None
- ログを返さない。
- Tail
- ペイロード形式
- ペイロードは有効なJSON形式である必要がある。
# 例 : { "body": { "message": "Hello Lambda!" }, "queryStringParameters": { "param1": "value1" } }
# config.iniファイル
[AWS]
# AWSリージョン (必須)
region=us-east-1
[Lambda]
# Lambda関数名 (必須)
function_name=my-lambda-function
# 呼び出しタイプ (RequestResponse, Event, DryRun)
invocation_type=RequestResponse
# ペイロード (JSON形式)
payload={"key1": "value1", "key2": "value2"}
# ログタイプ (None または Tail)
log_type=Tail
# CMakeLists.txtファイル
cmake_minimum_required(VERSION 3.16)
project(<プロジェクト名> VERSION 1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Qt設定
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# Qtの検索
find_package(Qt6 COMPONENTS Core QUIET)
if (NOT Qt6_FOUND)
find_package(Qt5 5.15 REQUIRED COMPONENTS Core)
endif()
# AWS SDK for C++の検索
find_package(AWSSDK REQUIRED COMPONENTS lambda)
# 実行ファイル
add_executable(<プロジェクト名>
main.cpp
)
# Qt6とQt5の両方に対応
if (Qt6_FOUND)
target_link_libraries(<プロジェクト名>
Qt6::Core
${AWSSDK_LINK_LIBRARIES}
)
else()
target_link_libraries(<プロジェクト名>
Qt5::Core
${AWSSDK_LINK_LIBRARIES}
)
endif()
# インストール
install(TARGETS <プロジェクト名>
RUNTIME DESTINATION bin
)
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QSettings>
#include <QFileInfo>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <iostream>
#include <aws/core/Aws.h>
#include <aws/lambda/LambdaClient.h>
#include <aws/lambda/model/InvokeRequest.h>
#include <aws/lambda/model/ListFunctionsRequest.h>
// 設定情報を保持する構造体
struct LambdaConfig {
QString region;
QString functionName;
QString invocationType;
QString payload;
QString logType;
};
// 設定ファイルを読み込む関数
bool loadConfig(const QString& configPath, LambdaConfig& config)
{
QFileInfo fileInfo(configPath);
if (!fileInfo.exists()) {
std::cerr << "エラー: 設定ファイルが見つかりません: " << configPath.toStdString() << std::endl;
return false;
}
QSettings settings(configPath, QSettings::IniFormat);
settings.setIniCodec("UTF-8");
// AWS設定
settings.beginGroup("AWS");
config.region = settings.value("region").toString();
settings.endGroup();
// Lambda設定
settings.beginGroup("Lambda");
config.functionName = settings.value("function_name").toString();
config.invocationType = settings.value("invocation_type", "RequestResponse").toString();
config.payload = settings.value("payload", "{}").toString();
config.logType = settings.value("log_type", "None").toString();
settings.endGroup();
// 必須項目のチェック
if (config.region.isEmpty()) {
std::cerr << "エラー: [AWS] region が設定されていません" << std::endl;
return false;
}
if (config.functionName.isEmpty()) {
std::cerr << "エラー: [Lambda] function_name が設定されていません" << std::endl;
return false;
}
std::cout << "設定ファイル読み込み完了: " << configPath.toStdString() << std::endl;
std::cout << " リージョン: " << config.region.toStdString() << std::endl;
std::cout << " 関数名: " << config.functionName.toStdString() << std::endl;
std::cout << " 呼び出しタイプ: " << config.invocationType.toStdString() << std::endl;
return true;
}
class LambdaManager {
private:
std::unique_ptr<Aws::Lambda::LambdaClient> m_lambdaClient;
public:
LambdaManager(const QString& region)
{
Aws::Client::ClientConfiguration clientConfig;
clientConfig.region = region.toStdString();
m_lambdaClient = std::make_unique<Aws::Lambda::LambdaClient>(clientConfig);
std::cout << "Lambdaクライアント初期化完了 (リージョン: " << region.toStdString() << ")" << std::endl;
}
bool invokeFunction(const QString& functionName, const QString& payload,
const QString& invocationType, const QString& logType)
{
std::cout << "\n=== Lambda関数呼び出し開始 ===" << std::endl;
std::cout << " 関数名: " << functionName.toStdString() << std::endl;
std::cout << " ペイロード: " << payload.toStdString() << std::endl;
// ペイロードのJSON検証
QJsonParseError parseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload.toUtf8(), &parseError);
if (parseError.error != QJsonParseError::NoError) {
std::cerr << "エラー: ペイロードが有効なJSONではありません: "
<< parseError.errorString().toStdString() << std::endl;
return false;
}
Aws::Lambda::Model::InvokeRequest request;
request.SetFunctionName(functionName.toStdString());
// 呼び出しタイプの設定
if (invocationType == "RequestResponse") {
request.SetInvocationType(Aws::Lambda::Model::InvocationType::RequestResponse);
}
else if (invocationType == "Event") {
request.SetInvocationType(Aws::Lambda::Model::InvocationType::Event);
}
else if (invocationType == "DryRun") {
request.SetInvocationType(Aws::Lambda::Model::InvocationType::DryRun);
}
// ログタイプの設定
if (logType == "Tail") {
request.SetLogType(Aws::Lambda::Model::LogType::Tail);
}
else {
request.SetLogType(Aws::Lambda::Model::LogType::None);
}
// ペイロードの設定
std::shared_ptr<Aws::IOStream> payload_stream =
Aws::MakeShared<Aws::StringStream>("PayloadStream");
*payload_stream << payload.toStdString();
request.SetBody(payload_stream);
auto outcome = m_lambdaClient->Invoke(request);
if (outcome.IsSuccess()) {
const auto& result = outcome.GetResult();
std::cout << "✓ 呼び出し成功!" << std::endl;
std::cout << "\n--- レスポンス ---" << std::endl;
std::cout << " ステータスコード: " << result.GetStatusCode() << std::endl;
// 関数エラーの確認
if (!result.GetFunctionError().empty()) {
std::cout << " 関数エラー: " << result.GetFunctionError() << std::endl;
}
// レスポンスペイロードの取得
auto& payload_stream = result.GetPayload();
std::stringstream ss;
ss << payload_stream.rdbuf();
std::string response_payload = ss.str();
if (!response_payload.empty()) {
std::cout << " ペイロード: " << response_payload << std::endl;
// JSONの整形表示
QJsonDocument responseDoc = QJsonDocument::fromJson(QString::fromStdString(response_payload).toUtf8());
if (!responseDoc.isNull()) {
std::cout << " 整形済みペイロード:\n"
<< responseDoc.toJson(QJsonDocument::Indented).toStdString() << std::endl;
}
}
// ログの表示 (LogType=Tailの場合)
if (!result.GetLogResult().empty()) {
std::cout << "\n--- 実行ログ (Base64デコード済み) ---" << std::endl;
// Base64デコード
QByteArray logData = QByteArray::fromBase64(QByteArray::fromStdString(result.GetLogResult()));
std::cout << logData.toStdString() << std::endl;
}
return true;
}
else {
std::cerr << "✗ 呼び出し失敗: " << outcome.GetError().GetMessage() << std::endl;
return false;
}
}
bool listFunctions()
{
std::cout << "\n=== Lambda関数一覧取得 ===" << std::endl;
Aws::Lambda::Model::ListFunctionsRequest request;
auto outcome = m_lambdaClient->ListFunctions(request);
if (outcome.IsSuccess()) {
const auto& result = outcome.GetResult();
const auto& functions = result.GetFunctions();
std::cout << "✓ 取得成功! (" << functions.size() << "個の関数)" << std::endl;
if (functions.empty()) {
std::cout << " 登録されている関数がありません" << std::endl;
}
else {
std::cout << "\n--- 関数一覧 ---" << std::endl;
for (const auto& func : functions) {
std::cout << " • " << func.GetFunctionName() << std::endl;
std::cout << " ランタイム: " << Aws::Lambda::Model::RuntimeMapper::GetNameForRuntime(func.GetRuntime()) << std::endl;
std::cout << " ハンドラ: " << func.GetHandler() << std::endl;
std::cout << " メモリ: " << func.GetMemorySize() << " MB" << std::endl;
std::cout << " タイムアウト: " << func.GetTimeout() << " 秒" << std::endl;
std::cout << " 最終更新: " << func.GetLastModified() << std::endl;
std::cout << std::endl;
}
}
return true;
}
else {
std::cerr << "✗ 取得失敗: " << outcome.GetError().GetMessage() << std::endl;
return false;
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationName("AWS Lambda Manager");
QCoreApplication::setApplicationVersion("1.0");
// コマンドライン引数のパーサーを設定
QCommandLineParser parser;
parser.setApplicationDescription("AWS Lambda関数呼び出しツール (設定ファイルベース)");
parser.addHelpOption();
parser.addVersionOption();
// オプション定義
QCommandLineOption invokeOption(QStringList() << "i" << "invoke", "Lambda関数を呼び出し");
parser.addOption(invokeOption);
QCommandLineOption listOption(QStringList() << "l" << "list", "Lambda関数の一覧を取得");
parser.addOption(listOption);
QCommandLineOption configOption(QStringList() << "c" << "config", "設定ファイルのパス (デフォルト: config.ini)", "config", "config.ini");
parser.addOption(configOption);
parser.process(app);
// AWS SDKの初期化
Aws::SDKOptions options;
Aws::InitAPI(options);
int result = 0;
{
// 設定ファイルのパスを取得
QString configPath = parser.value(configOption);
// 設定を読み込む
LambdaConfig config;
if (!loadConfig(configPath, config)) {
std::cerr << "\n設定ファイルの例:" << std::endl;
std::cerr << " [AWS]" << std::endl;
std::cerr << " region=ap-northeast-1" << std::endl;
std::cerr << " " << std::endl;
std::cerr << " [Lambda]" << std::endl;
std::cerr << " function_name=my-function" << std::endl;
std::cerr << " invocation_type=RequestResponse" << std::endl;
std::cerr << " payload={\"key\": \"value\"}" << std::endl;
std::cerr << " log_type=Tail" << std::endl;
Aws::ShutdownAPI(options);
return 1;
}
// Lambdaマネージャーを初期化
LambdaManager lambdaManager(config.region);
if (parser.isSet(invokeOption)) {
// Lambda関数呼び出し
if (!lambdaManager.invokeFunction(config.functionName, config.payload,
config.invocationType, config.logType)) {
result = 1;
}
}
else if (parser.isSet(listOption)) {
// Lambda関数一覧取得
if (!lambdaManager.listFunctions()) {
result = 1;
}
}
else {
std::cerr << "エラー: --invoke または --list を指定してください" << std::endl;
parser.showHelp(1);
}
}
// AWS SDKのクリーンアップ
Aws::ShutdownAPI(options);
return result;
}
# 実行例
# Lambda関数を呼び出し
./lambda-manager --invoke --config config.ini
# Lambda関数の一覧を取得
./lambda-manager --list --config config.ini
# 別の設定ファイルを使用
./lambda-manager --invoke --config /path/to/custom-config.ini