Day 018 - 100DaysAWSIaCDevopsChallenge
Amazon SNS (Simple Notification Service) is a fully managed Amazon messaging service that allows large number of subscribers using different delivery protocoles, such as HTTP/HTTPS, email, SQS, SMS and AWS Lambda.
It designed for scalable, high-throughput, push-bash messaging. It enables applications, microservices, and systems to communicate with each other asynchroniously in real time.
The core concept of SNS revolvers around topics
and subscriptions
.
Subscribe and Consume a SNS Messages using Springboot application
This guide demonstrates how to create a Spring Boot application that subscribes to and processes messages from an SNS topic. The infrastructure, built using AWS CDK (in Java), includes the following components:
- A
VPC
with a publicSubnet
to host an EC2 instance where the Spring Boot application will run. - An
Internet Gateway
to provide the EC2 instance with internet access for downloading dependencies. - An
SNS Topic
for publishing messages. - An
EC2 Instance
for hosting the Spring Boot application. - An
IAM Role
to grant the EC2 instance permissions to receive messages from the SNS topic (critical for secure communication).
Create the Infrastructure
Set up the necessary infrastructure using CDK (Java)
VPC & Subnet + Internet Gateway
// constructs/NetworkConstruct.java
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;
}
}
// MyStatck.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();
}
}
The above code 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.
SNS Topic
// MyStatck.java
public class MyStack extends Stack {
public MyStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
//...
String topicName = "example-topic-main";
ITopic topic = new Topic(
this, "TopicResource", TopicProps.builder()
.topicName(topicName)
.fifo(false)
.build());
}
}
The above code creates an SNS Topic named example-topic-main
. This topic will be used for publishing messages, which the Spring Boot application will subscribe to and process.
EC2 Instance for Hosting the Spring Boot Application
// MyStatck.java
public class MyStack extends Stack {
public MyStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
//...
IVpc vpc; // previously instanciated
String topicName;
int port = 8089;
ITopic topic; // previously instanciated
ComputerConstruct webserver =
new ComputerConstruct(
this, "ComputerResource", ComputerProps.builder().vpc(vpc).port(port).build(), props);
IInstance instance = webserver.getComputer();
webserver.addPolicyToComputer(
PolicyStatement.Builder.create()
.effect(Effect.ALLOW)
.resources(List.of(topic.getTopicArn()))
.actions(
List.of(
"sns:ConfirmSubscription",
"sns:Subscribe",
"sns:GetTopicAttributes",
"sns:ListTopics"))
.build());
ITopicSubscription endpointSubscription =
new UrlSubscription(
"http://%s:%d/topics/%s"
.formatted(instance.getInstancePublicDnsName(), port, topicName),
UrlSubscriptionProps.builder()
.rawMessageDelivery(false)
.protocol(SubscriptionProtocol.HTTP)
.build());
topic.addSubscription(endpointSubscription);
}
}
ComputerConstruct.java [↗]
The above CDK construct will create the following resources:
- A Security Group named
Webserver-security-group
that allows inbound traffic onPort 22
for SSH connections and allows 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:sns:ConfirmSubscription
,sns:Subscribe
,sns:GetTopicAttributes
andsns:ListTopics
. - An SNS Subscription for the HTTP endpoint, allowing the Spring Boot application hosted on the EC2 instance to receive and process messages from the SNS topic.
Create the stack
// 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 Subscriber 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 SNS service, 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();
}
}
Finally, add a controller designed to handle incoming notifications from an AWS SNS (Simple Notification Service) topic. This controller allows the application to subscribe to the SNS topic and receive messages sent to it. It utilizes annotations from the Spring Cloud AWS library to manage SNS message mappings effectively.
@RestController
@RequestMapping("/topics/${sns.topic.name}") // ${sns.topic.name} will return example-topic-main
@Slf4j
public class ConsumeNotificationResource {
@NotificationSubscriptionMapping
public void confirmSubscription(NotificationStatus status) {
status.confirmSubscription();
log.info("Subscription confirmed");
}
@NotificationMessageMapping
public void receiveMessage(@NotificationSubject String subject, @NotificationMessage String message) {
log.info(
"""
************************* SNS Notification ***************
* Subject : {}
* Content: {}
* Date : {}
**********************************************************
""",
subject,
message,
LocalDateTime.now());
}
@NotificationUnsubscribeConfirmationMapping
public void unsubscribe(NotificationStatus status) {
status.confirmSubscription();
log.info("Unsubscription confirmed");
}
}
You can find the full project in my GitHub repo[↗]
Deployment
⚠️⚠️ Before run the deployment command 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 commande:
git clone https://github.com/nivekalara237/100DaysTerraformAWSDevops.git
cd 100DaysTerraformAWSDevops/day_018
cdk bootstrap --profile cdk-user
cdk deploy --profile cdk-user Day018Stack
Resut
Your can find the full source code here #day_18
nivekalara237 / 100DaysTerraformAWSDevops
Thriving with AWS and Terraform
articles about my work 👉🏽 https://dev.to/nivekalara237/series/28339
Top comments (0)