」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 使用Amazon SQS構建Spring Boot消費者應用程序:使用Cloud Development套件(CDK)的設置基礎架構

使用Amazon SQS構建Spring Boot消費者應用程序:使用Cloud Development套件(CDK)的設置基礎架構

發佈於2025-03-24
瀏覽:367
Day 017 - 100DaysAWSIaCDevopsChallenge

Today in my series of 100 days of code challenge, I will show you how to decouple microservices developed with springboot using Amazon SQS.

What is Amazon SQS ?

Amazon SQS (Simple Queue Service) is a cloud service that helps applications communicate by sendyg, storing and receiving messages in a queue. It's like a waiting line where are stored until a consumer is ready to process them. This prevents systems from getting overhelmed and make sure no message is lost.

Consume a SQS Messages using Springboot application

To demonstrate how to consume SQS messages by creating a Spring Boot app that processes each message from an SQS queue. The infrastructure, built using CDK (Java), will include:

  • a VPC with a public Subnet to host an EC2 instance where the Spring Boot app will run.
  • An Internet Gateway for the EC2 instance to access the internet and download dependencies.
  • A SQS Queue and Dead Letter Queue for message storage.
  • An EC2 Instance for hosting SpringBoot App.
  • An IAM Role to allow the EC2 instance to retrieve messages from the SQS queue (very important).

Create the Infrastructure

Set up the necessary infrastructure using CDK (Java)

Building a Spring Boot Consumer Application with Amazon SQS: Setup Infrastructure Using Cloud Development Kit (CDK)

VPC & Subne Internet Gateway


public class NetworkContruct extends Construct {
  private final IVpc vpc;
  public NetworkContruct(Construct scope, String id, StackProps props) {
    super(scope, id);
    this.vpc =
        new Vpc(
            this,
            "VpcResource",
            VpcProps.builder()
                .vpcName("my-vpc")
                .enableDnsHostnames(true)
                .enableDnsSupport(true)
                .createInternetGateway(true)
                .ipProtocol(IpProtocol.IPV4_ONLY)
                .ipAddresses(IpAddresses.cidr("10.0.0.1/16"))
                .maxAzs(1)
                .subnetConfiguration(
                    List.of(
                        SubnetConfiguration.builder()
                            .name("Public-Subnet")
                            .mapPublicIpOnLaunch(true)
                            .subnetType(SubnetType.PUBLIC)
                            .build()))
                .build());
  }
  public IVpc getVpc() {
    return vpc;
  }
}


This cdk construct will create:
?? A VPC named my-vpc and enable DNS hostname enabled.
?? A public subnet named Public-Subnet which allows resources to attach a public IP (if configured with one).
?? An Internet Gateway to enable internet traffic.

SQS Queue & Dead Letter Queue


public class QueueConstruct extends Construct {
  private final IQueue queue;
  public QueueConstruct(Construct scope, String id, IVpc vpc, StackProps props) {
    super(scope, id);
    IQueue dlq =
        new Queue(
            this,
            "DeadLetterQueue",
            QueueProps.builder()
                .deliveryDelay(Duration.millis(0))
                .retentionPeriod(Duration.days(10))
                .queueName("my-queue-dlq")
                .build());
    DeadLetterQueue deadLetterQueue = DeadLetterQueue.builder()
        .queue(dlq)
        .maxReceiveCount(32)
        .build();

    this.queue =
        new Queue(
            this,
            "SQSQueueResource",
            QueueProps.builder()
                .queueName("my-queue")
                .retentionPeriod(Duration.minutes(15))
                .visibilityTimeout(Duration.seconds(90))
                .deadLetterQueue(deadLetterQueue)
                .build());
  }

  public IQueue getQueue() {
    return queue;
  }
}


The above CDK construct will create the following resources:

  • A Queue named my-queue, which will be used in the Spring Boot app.
  • A DeadLetter Queue named my-queue-dlq which captures failed messages so they can be analysed and fixed later, without blocking the main queue.

EC2 Instance for Hosting the Spring Boot Application


// ComputerProps.java
public record ComputerProps(IVpc vpc, String sqsQueueArn) {}

// ComputerConstruct.java
public class ComputerConstruct extends Construct {
  private final IInstance computer;
  public ComputerConstruct(
      Construct scope, String id, ComputerProps computerProps, StackProps props) {
    super(scope, id);
    SecurityGroup securityGroup =
        new SecurityGroup(
            this,
            "WebserverSGResource",
            SecurityGroupProps.builder()
                .allowAllOutbound(true)
                .securityGroupName("Webserver-security-group")
                .disableInlineRules(true)
                .vpc(computerProps.vpc())
                .description("Allow trafic from/to webserver instance")
                .build());

    securityGroup.addIngressRule(Peer.anyIpv4(), Port.SSH, "Allow ssh traffic");
    securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(8089), "Allow traffic from 8089 port");

    KeyPair keyPair =
        new KeyPair(
            this,
            "KeyPairResource",
            KeyPairProps.builder()
                .keyPairName("ws-keypair")
                .account(Objects.requireNonNull(props.getEnv()).getAccount())
                .type(KeyPairType.RSA)
                .format(KeyPairFormat.PEM)
                .build());

    new CfnOutput(
        this, "KeyPairId", CfnOutputProps.builder().value(keyPair.getKeyPairId()).build());

    Instance ec2Instance =
        new Instance(
            this,
            "WebServerInstanceResource",
            InstanceProps.builder()
                .securityGroup(securityGroup)
                .keyPair(keyPair)
                .instanceName("Webserver-Instance")
                .machineImage(
                    MachineImage.lookup(
                        LookupMachineImageProps.builder()
                            .name("*ubuntu*")
                            .filters(
                                Map.ofEntries(
                                    Map.entry("image-id", List.of("ami-0e86e20dae9224db8")),
                                    Map.entry("architecture", List.of("x86_64"))))
                            .windows(false)
                            .build()))
                .vpc(computerProps.vpc())
                .role(buildInstanceRole(computerProps))
                .instanceType(InstanceType.of(InstanceClass.T2, InstanceSize.MICRO))
                .associatePublicIpAddress(true)
                .blockDevices(
                    List.of(
                        BlockDevice.builder()
                            .mappingEnabled(true)
                            .deviceName("/dev/sda1")
                            .volume(
                                BlockDeviceVolume.ebs(
                                    10,
                                    EbsDeviceOptions.builder()
                                        .deleteOnTermination(true)
                                        .volumeType(EbsDeviceVolumeType.GP3)
                                        .build()))
                            .build()))
                .userDataCausesReplacement(true)
                .vpcSubnets(SubnetSelection.builder().subnetType(SubnetType.PUBLIC).build())
                .build());

    UserData userData = UserData.forLinux();
    userData.addCommands(readFile("./webserver-startup.sh"));

    ec2Instance.addUserData(userData.render());

    this.computer = ec2Instance;
  }

  public IInstance getComputer() {
    return computer;
  }

  private String readFile(String filename) {

    InputStream scriptFileStream = getClass().getClassLoader().getResourceAsStream(filename);

    try {
      assert scriptFileStream != null;
      try (InputStreamReader isr = new InputStreamReader(scriptFileStream, StandardCharsets.UTF_8);
          BufferedReader br = new BufferedReader(isr)) {
        StringBuilder content = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
          content.append(line).append("\n");
        }
        return content.toString();
      }
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage());
    }
  }

  private IRole buildInstanceRole(ComputerProps props) {
    return new Role(
        this,
        "WebserverInstanceRoleResource",
        RoleProps.builder()
            .roleName("webserver-role")
            .assumedBy(new ServicePrincipal("ec2.amazonaws.com"))
            .path("/")
            .inlinePolicies(
                Map.ofEntries(
                    Map.entry(
                        "sqs",
                        new PolicyDocument(
                            PolicyDocumentProps.builder()
                                .assignSids(true)
                                .statements(
                                    List.of(
                                        new PolicyStatement(
                                            PolicyStatementProps.builder()
                                                .effect(Effect.ALLOW)
                                                .actions(
                                                    List.of(
                                                        "sqs:DeleteMessage",
                                                        "sqs:ReceiveMessage",
                                                        "sqs:SendMessage",
                                                        "sqs:GetQueueAttributes",
                                                        "sqs:GetQueueUrl"))
                                                .resources(List.of(props.sqsQueueArn()))
                                                .build())))
                                .build()))))
            .build());
  }
}


The above CDK construct will create the following resources:

  • A Security Group named Webserver-security-group that allow inbound traffic on Port 22 for SSH connections and allow inbound traffic on Port 8089, which is the application connection port.
  • A Key Pair named ws-keypair that will be used to connect to the app host via SSH. Since we are using CDK to build the infrastructure, if you need to download the private key (PEM file) after deployment, refer to my previous article on How the retrieve the private key file PEM after Cloudformation or CDK stack creation[↗].
  • An Ec2 Instance named Webserver-Instance.
  • An IAM Role for the Ec2 Instance named webserver-role, which allows the spring Boot application hosted on the Ec2 Instance to establish connections with the Amazon SQS Queue (already created) and perform actions: Delete Message, Receive Message, Send Message, Get Queue Attributes and Get Queue Url.

Create the stack


// MyStack.java
public class MyStack extends Stack {
  public MyStack(final Construct scope, final String id, final StackProps props) {
    super(scope, id, props);
    IVpc vpc = new NetworkContruct(this, "NetworkResource", props).getVpc();
    IQueue queue = new QueueConstruct(this, "QueueResource", vpc, props).getQueue();
    IInstance webserver =
        new ComputerConstruct(
                this, "ComputerResource", new ComputerProps(vpc, queue.getQueueArn()), props)
            .getComputer();
  }
}

// Day17App.java
public class Day017App {
  public static void main(final String[] args) {
    App app = new App();
    new MyStack(app,"Day017Stack",
        StackProps.builder()
                .env(
                    Environment.builder()
                        .account(System.getenv("CDK_DEFAULT_ACCOUNT"))
                        .region(System.getenv("CDK_DEFAULT_REGION"))
                        .build())
                .build());
    app.synth();
  }
}


Create SpringBoot Consumer Application

To keep things simple and avoid complicating my life, I will use Spring Cloud AWS Docs[↗]

Spring Cloud AWS simplifies using AWS managed services in a Spring Framework and Spring Boot applications. It offers a convenient way to interact with AWS provided services using well-known Spring idioms and APIs.

To configure the SQS service in my project, I will add the following beans in the configuration class:


@Configuration
public class ApplicationConfiguration {
    @Bean
    public AwsRegionProvider customRegionProvider() {
        return new InstanceProfileRegionProvider();
    }
    @Bean
    public AwsCredentialsProvider customInstanceCredProvider() {
        return  InstanceProfileCredentialsProvider.builder()
                .build();
    }
}


And the listener that captures all new message and prints their content.


@Slf4j
@Component
public class ExampleSQSConsumer {
    @SqsListener(queueNames = { "my-queue" }) // ??
    public void listen(String payload) {
        log.info("*******************  SQS Payload ***************");
        log.info("Message Content: {}", payload);
        log.info("Received At: {}", Date.from(Instant.now()));
        log.info("************************************************");
    }
}


You can find the full project in my GitHub repo[↗]

Deployment

⚠️⚠️ Before run the deployment commands ensure that you have java installed on your host machine. I used Java 21 under MacOs to build this insfrastructure.

Open the terminal anywhere and run the following commands:


git clone https://github.com/nivekalara237/100DaysTerraformAWSDevops.git
cd 100DaysTerraformAWSDevops/day_017
cdk bootstrap --profile cdk-user
cdk deploy --profile cdk-user Day017Stack




Resut

Building a Spring Boot Consumer Application with Amazon SQS: Setup Infrastructure Using Cloud Development Kit (CDK)


Your can find the full source code on GitHub Repo↗

版本聲明 本文轉載於:https://dev.to/nivekalara237/building-a-spring-boot-consumer-application-with-amazon-sqs-setup-infrastructure-using-cloud-development-kit-cdk-41a2?1如有侵犯,請聯繫[email protected]刪除
最新教學 更多>
  • 在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在JTable中維護jtable單元格渲染後,在JTable中,在JTable中實現自定義單元格渲染和編輯功能可以增強用戶體驗。但是,至關重要的是要確保即使在編輯操作後也保留所需的格式。 在設置用於格式化“價格”列的“價格”列,用戶遇到的數字格式丟失的“價格”列的“價格”之後,問題在設置自定義單元...
    程式設計 發佈於2025-04-19
  • 如何使用組在MySQL中旋轉數據?
    如何使用組在MySQL中旋轉數據?
    在關係數據庫中使用mySQL組使用mySQL組進行查詢結果,在關係數據庫中使用MySQL組,轉移數據的數據是指重新排列的行和列的重排以增強數據可視化。在這裡,我們面對一個共同的挑戰:使用組的組將數據從基於行的基於列的轉換為基於列。 Let's consider the following ...
    程式設計 發佈於2025-04-19
  • SQL Server 2008如何使用自定義函數和檢查約束限制事件容量?
    SQL Server 2008如何使用自定義函數和檢查約束限制事件容量?
    在SQL Server 2008中使用帶有檢查約束的自定義函數強制實現事件容量限制,以確保事件的預期出現不超過現場能力對計劃和資源分配至關重要。為了強制執行此約束,可以與檢查約束一起使用自定義函數。 命名checkvenuecapacity的自定義函數採用兩個參數:@venue_id and @c...
    程式設計 發佈於2025-04-19
  • Python元類工作原理及類創建與定制
    Python元類工作原理及類創建與定制
    python中的metaclasses是什麼? Metaclasses負責在Python中創建類對象。就像類創建實例一樣,元類也創建類。他們提供了對類創建過程的控制層,允許自定義類行為和屬性。 在Python中理解類作為對象的概念,類是描述用於創建新實例或對象的藍圖的對象。這意味著類本身是使用...
    程式設計 發佈於2025-04-19
  • FastAPI自定義404頁面創建指南
    FastAPI自定義404頁面創建指南
    response = await call_next(request) if response.status_code == 404: return RedirectResponse("https://fastapi.tiangolo.com") else: ...
    程式設計 發佈於2025-04-19
  • 為什麼不使用CSS`content'屬性顯示圖像?
    為什麼不使用CSS`content'屬性顯示圖像?
    在Firefox extemers屬性為某些圖像很大,&& && && &&華倍華倍[華氏華倍華氏度]很少見,卻是某些瀏覽屬性很少,尤其是特定於Firefox的某些瀏覽器未能在使用內容屬性引用時未能顯示圖像的情況。這可以在提供的CSS類中看到:。 googlepic { 內容:url(&...
    程式設計 發佈於2025-04-19
  • Netbeans 7.4 為何警告直接訪問 PHP 中的 $\_POST 數組
    Netbeans 7.4 為何警告直接訪問 PHP 中的 $\_POST 數組
    檢查“請勿直接訪問$ _POST數組” NetBeans 7.4在NetBeans 7.4警告php 理解含義 1。使用filter_input()對單個變量:用filter_input(input_post,'var_name')替換$ _post ['var_n...
    程式設計 發佈於2025-04-19
  • JavaScript計算兩個日期之間天數的方法
    JavaScript計算兩個日期之間天數的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    程式設計 發佈於2025-04-19
  • 將圖片浮動到底部右側並環繞文字的技巧
    將圖片浮動到底部右側並環繞文字的技巧
    在Web設計中圍繞在Web設計中,有時可以將圖像浮動到頁面右下角,從而使文本圍繞它纏繞。這可以在有效地展示圖像的同時創建一個吸引人的視覺效果。 css位置在右下角,使用css float and clear properties: img { 浮點:對; ...
    程式設計 發佈於2025-04-19
  • 選對全棧開發公司指南
    選對全棧開發公司指南
    选择一个完美的全栈开发公司是希望在现代数字空间中开发或升级其可扩展性且功能丰富的应用程序的企业的主要优先事项。全堆栈开发需要创建前端和后端,这确保了交互式的用户体验和体系结构。为此端到端服务选择的合适合作伙伴可以为您的业务成功设置正确的平台。下面,指南列举了人们在选择全堆栈开发公司时应提出的一些关...
    程式設計 發佈於2025-04-19
  • 如何限制動態大小的父元素中元素的滾動範圍?
    如何限制動態大小的父元素中元素的滾動範圍?
    在交互式接口中實現垂直滾動元素的CSS高度限制問題:考慮一個佈局,其中我們具有與用戶垂直滾動一起移動的可滾動地圖div,同時與固定的固定sidebar保持一致。但是,地圖的滾動無限期擴展,超過了視口的高度,阻止用戶訪問頁面頁腳。 $("#map").css({ margin...
    程式設計 發佈於2025-04-19
  • 切換到MySQLi後CodeIgniter連接MySQL數據庫失敗原因
    切換到MySQLi後CodeIgniter連接MySQL數據庫失敗原因
    Unable to Connect to MySQL Database: Troubleshooting Error MessageWhen attempting to switch from the MySQL driver to the MySQLi driver in CodeIgniter,...
    程式設計 發佈於2025-04-19
  • PHP陣列鍵值異常:了解07和08的好奇情況
    PHP陣列鍵值異常:了解07和08的好奇情況
    PHP數組鍵值問題,使用07&08 在給定數月的數組中,鍵值07和08呈現令人困惑的行為時,就會出現一個不尋常的問題。運行print_r($月份)返回意外結果:鍵“ 07”丟失,而鍵“ 08”分配給了9月的值。 此問題源於PHP對領先零的解釋。當一個數字帶有0(例如07或08)的前綴時,PHP...
    程式設計 發佈於2025-04-19
  • 在程序退出之前,我需要在C ++中明確刪除堆的堆分配嗎?
    在程序退出之前,我需要在C ++中明確刪除堆的堆分配嗎?
    在C中的顯式刪除 在C中的動態內存分配時,開發人員通常會想知道是否需要手動調用“ delete”操作員在heap-exprogal exit exit上。本文深入研究了這個主題。 在C主函數中,使用了動態分配變量(HEAP內存)的指針。當應用程序退出時,此內存是否會自動發布?通常,是。但是,即使在...
    程式設計 發佈於2025-04-19
  • 如何從PHP中的數組中提取隨機元素?
    如何從PHP中的數組中提取隨機元素?
    從陣列中的隨機選擇,可以輕鬆從數組中獲取隨機項目。考慮以下數組:; 從此數組中檢索一個隨機項目,利用array_rand( array_rand()函數從數組返回一個隨機鍵。通過將$項目數組索引使用此鍵,我們可以從數組中訪問一個隨機元素。這種方法為選擇隨機項目提供了一種直接且可靠的方法。
    程式設計 發佈於2025-04-19

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3