Heim  >  Artikel  >  Java  >  Erstellen einer Spring Boot Consumer-Anwendung mit Amazon SQS: Einrichten der Infrastruktur mit dem Cloud Development Kit (CDK)

Erstellen einer Spring Boot Consumer-Anwendung mit Amazon SQS: Einrichten der Infrastruktur mit dem Cloud Development Kit (CDK)

Susan Sarandon
Susan SarandonOriginal
2024-10-07 22:08:30564Durchsuche
Tag 017 – 100DaysAWSIaCDevopsChallenge

Heute zeige ich Ihnen in meiner Serie von 100 Tagen Code-Challenge, wie Sie mit Springboot entwickelte Microservices mithilfe von Amazon SQS entkoppeln können.

Was ist Amazon SQS?

Amazon SQS (Simple Queue Service) ist ein Cloud-Dienst, der Anwendungen durch Senden, Speichern und Empfangen von Nachrichten in einer Warteschlange bei der Kommunikation unterstützt. Es ist wie eine Warteschlange, in der sie gespeichert werden, bis ein Verbraucher bereit ist, sie zu verarbeiten. Dies verhindert eine Überlastung der Systeme und stellt sicher, dass keine Nachricht verloren geht.

Verarbeiten Sie SQS-Nachrichten mit der Springboot-Anwendung

Um zu demonstrieren, wie man SQS-Nachrichten konsumiert, indem man eine Spring Boot-App erstellt, die jede Nachricht aus einer SQS-Warteschlange verarbeitet. Die mit CDK (Java) erstellte Infrastruktur umfasst:

  • eine VPC mit einem öffentlichen Subnetz zum Hosten einer EC2-Instanz, auf der die Spring Boot-App ausgeführt wird.
  • Ein Internet-Gateway für die EC2-Instanz, um auf das Internet zuzugreifen und Abhängigkeiten herunterzuladen.
  • Eine SQS-Warteschlange und eine Dead Letter Queue zur Nachrichtenspeicherung.
  • Eine EC2-Instanz zum Hosten der SpringBoot-App.
  • Eine IAM-Rolle, die es der EC2-Instanz ermöglicht, Nachrichten aus der SQS-Warteschlange abzurufen (sehr wichtig).

Erstellen Sie die Infrastruktur

Richten Sie die erforderliche Infrastruktur mit CDK (Java) ein

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

VPC- und 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;
  }
}


Dieses CDK-Konstrukt erstellt:
?? Eine VPC mit dem Namen my-vpc und aktivierter DNS-Hostname.
?? Ein öffentliches Subnetz mit dem Namen „Public-Subnet“, das es Ressourcen ermöglicht, eine öffentliche IP anzuhängen (falls mit einer solchen konfiguriert).
?? Ein Internet-Gateway zur Aktivierung des Internetverkehrs.

SQS-Warteschlange und Warteschlange für unzustellbare Nachrichten


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;
  }
}


Das obige CDK-Konstrukt erstellt die folgenden Ressourcen:

  • Eine Warteschlange mit dem Namen my-queue, die in der Spring Boot-App verwendet wird.
  • Eine DeadLetter-Warteschlange mit dem Namen my-queue-dlq, die fehlgeschlagene Nachrichten erfasst, damit sie später analysiert und behoben werden können, ohne die Hauptwarteschlange zu blockieren.

EC2-Instanz zum Hosten der Spring Boot-Anwendung


// 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());
  }
}


Das obige CDK-Konstrukt erstellt die folgenden Ressourcen:

  • Eine Sicherheitsgruppe namens Webserver-security-group, die eingehenden Datenverkehr auf Port 22 für SSH-Verbindungen und eingehenden Datenverkehr auf Port 8089 zulässt, dem Anwendungsverbindungsport.
  • Ein Schlüsselpaar mit dem Namen „ws-keypair“, das für die Verbindung mit dem App-Host über SSH verwendet wird. Da wir CDK zum Aufbau der Infrastruktur verwenden, lesen Sie, wenn Sie den privaten Schlüssel (PEM-Datei) nach der Bereitstellung herunterladen müssen, meinen vorherigen Artikel zum Abrufen der privaten Schlüsseldatei PEM nach Cloudformation oder CDK-Stack-Erstellung[↗].
  • Eine Ec2-Instanz namens Webserver-Instance.
  • Eine IAM-Rolle für die Ec2-Instanz mit dem Namen „webserver-role“, die es der auf der Ec2-Instanz gehosteten Spring Boot-Anwendung ermöglicht, Verbindungen mit der Amazon SQS-Warteschlange (bereits erstellt) herzustellen und Aktionen auszuführen: Nachricht löschen, Nachricht empfangen, Nachricht senden , Warteschlangenattribute abrufen und Warteschlangen-URL abrufen.

Erstellen Sie den Stapel


// 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();
  }
}


Erstellen Sie eine SpringBoot-Consumer-Anwendung

Um die Dinge einfach zu halten und mein Leben nicht zu verkomplizieren, verwende ich Spring Cloud AWS Docs[↗]

Spring Cloud AWS vereinfacht die Verwendung von AWS-verwalteten Diensten in einem Spring Framework und Spring Boot-Anwendungen. Es bietet eine bequeme Möglichkeit, mit von AWS bereitgestellten Diensten unter Verwendung bekannter Spring-Redewendungen und APIs zu interagieren.

Um den SQS-Dienst in meinem Projekt zu konfigurieren, füge ich die folgenden Beans in der Konfigurationsklasse hinzu:


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


Und der Zuhörer, der alle neuen Nachrichten erfasst und deren Inhalt ausdruckt.


@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("************************************************");
    }
}


Das vollständige Projekt finden Sie in meinem GitHub-Repo[↗]

Einsatz

⚠️⚠️ Bevor Sie die Bereitstellungsbefehle ausführen, stellen Sie sicher, dass Java auf Ihrem Hostcomputer installiert ist. Zum Aufbau dieser Infrastruktur habe ich Java 21 unter MacOs verwendet.

Öffnen Sie das Terminal an einer beliebigen Stelle und führen Sie die folgenden Befehle aus:


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




Ergebnis

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


Den vollständigen Quellcode finden Sie auf GitHub Repo↗

Das obige ist der detaillierte Inhalt vonErstellen einer Spring Boot Consumer-Anwendung mit Amazon SQS: Einrichten der Infrastruktur mit dem Cloud Development Kit (CDK). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn