home

JavaプロジェクトからSQSにキューを送信してlambdaをキックする

2022-10-15T17:08:00+09:00

前提

業務でこんな処理を実装する気がするので、簡単に予習してみます。
今回は動くか検証のみなので、ローカルで起動したJavaアプリケーション(Spring Boot)からSQSにメッセージを送信してみます。

本当はlambdaからrdsへ登録・更新する処理を実装したかったのですが、それはまた別の機会に。(なのでSQSやlambdaの名前がmergeXXXXみたいになってます。)

lambda-sqs.drawio.png

環境

  • macOS: 12.6
  • IntelliJ IDEA: 2022.2.3 (Community Edition)
  • Gradle: 7.5
  • Java: temurin 17.0.3

手順

プロジェクトの作成

spring initializrでプロジェクト作成

https://start.spring.io/でプロジェクトの雛形を作成します。
以下の画像のような構成にしています。

GENERATEボタンを押してダウンロードしてきます。それをIDEツールで開きます。

build.gradleの設定

com.amazonaws:aws-java-sdk-bomcom.amazonaws:aws-java-sdk-sqsを設定します。
この時点ではどちらも1.12.320が最新でした。

build.gradle
plugins {
	id 'org.springframework.boot' version '2.7.5-SNAPSHOT'
	id 'io.spring.dependency-management' version '1.0.14.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'com.amazonaws:aws-java-sdk-bom:1.12.320'
	implementation 'com.amazonaws:aws-java-sdk-sqs:1.12.320'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

AWS側の設定

IAMユーザーの作成

事前にJavaからアクセスするためのIAMユーザーを作成し、アクセスキーとシークレットキーをダウンロードしておきます。
そのIAMユーザーを使用してコンロールでSQS等を作成します。

SQSのキューを作成

コンソールでSQSのキューを作成します

lambdaを作成

IAMロールを作成する

lambdaからSQSを読み取りするのでIAMロールを作成します。
こちらを参考にさせていただきました。
https://www.stsd.co.jp/dev-blog/send_and_receive_amazon_sqs_messages_from_java.html#:~:text=SQS%E3%81%AB%E3%81%AFJava%E3%81%A7,%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%80%8D%E3%82%92%E9%81%B8%E6%8A%9E%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

  • ロール名:for-sqs
  • ポリシー名:sqs-policy
  • ポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "sqs:DeleteMessage",
                "sqs:ReceiveMessage",
                "sqs:GetQueueAttributes"
            ],
            "Resource": "*"
        }
    ]
}

またロールにAWSLambdaBasicExecutionRoleをアタッチしておくと、CloudWatchログにログを書き込めます。

lambdaにIAMロールの設定

コンロールのlambdaのページを開き、設定タブ > アクセス権限 > 実行ロール > 編集ボタンで設定します。

SQSでLambdaトリガーを設定

SQSのページで作成したキューを選択し、Lambdaトリガータブからlambdaを設定します。

これでaws側の設定は完了です。

実装

SqsClient

こちらを参考にしました。
https://docs.aws.amazon.com/ja_jp/sdk-for-java/v1/developer-guide/examples-sqs-messages.html

SqsClient.java
package com.example.lambdasqssample.aws;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
import com.amazonaws.services.sqs.model.AmazonSQSException;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import org.springframework.stereotype.Component;

@Component
public class SqsClient {

    private static final String QUEUE_NAME = "merge-data-sqs";

    public String getQueueUrl() {
        var sqs = getSqs();
        var create_request = new CreateQueueRequest(QUEUE_NAME)
                .addAttributesEntry("DelaySeconds", "60")
                .addAttributesEntry("MessageRetentionPeriod", "86400");

        try {
            sqs.createQueue(create_request);
        } catch (AmazonSQSException e) {
            if (!e.getErrorCode().equals("QueueAlreadyExists")) {
                throw e;
            }
        }
        return sqs.getQueueUrl(QUEUE_NAME).getQueueUrl();
    }

    public void sendMessage(String queueUrl) {
        var sqs = getSqs();
        var send_msg_request = new SendMessageRequest()
                .withQueueUrl(queueUrl)
                .withMessageBody("hello world")
                .withDelaySeconds(5);
        sqs.sendMessage(send_msg_request);
    }

    private AmazonSQS getSqs() {
        return AmazonSQSClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(Credentials.get()))
                .withRegion(Regions.AP_NORTHEAST_1.getName())
                .build();
    }
}
Credentials.java
package com.example.lambdasqssample.aws;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Credentials {

    private static String ACCESS_KEY;

    private static String SECRET_KEY;

    @Value("${aws.access-key}")
    public void setAccessKey(String accessKey) {
        this.ACCESS_KEY = accessKey;
    }

    @Value("${aws.secret-key}")
    public void setSecretKey(String secretKey) {
        this.SECRET_KEY = secretKey;
    }

    public static AWSCredentials get() {
        return new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY);
    }
}

ACCESS_KEYとSECRET_KEYはapplication.ymlで設定しておきます。

実行クラス

QueueのUrlを受け取り、メッセージを送信します。

SampleController.java
package com.example.lambdasqssample.Controller;

import com.example.lambdasqssample.aws.SqsClient;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/sample")
@RequiredArgsConstructor
public class SampleController {

    private final SqsClient sqsClient;

    @GetMapping("/result")
    public boolean getResult() {
        var queue = sqsClient.getQueueUrl();
        sqsClient.sendMessage(queue);
        return true;
    }

}

これで実装は完了です。

実行

アプリケーションをビルド・実行します。
今回はpostmanを使用してhttp://localhost:8080/api/v1/sample/resultにGETします。

CloudWatchログを見ると、アクセスするたびに実行されていることがわかります。

さいごに

まだlambdaで具体的な処理を実行していないですが、とりあえず動くことを確認できた第一歩でした。

自己紹介

サムネイル

y5347M

バックエンドエンジニアをしています。