SPRING BOOT
ON AMAZON WEB SERVICES
WITH SPRING CLOUD AWS
MACIEJ
WALKOWIAK
MATEJ
NEDIC
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
MACIEJ WALKOWIAK
- independent consultant
- Spring Cloud AWS Lead
- working with
@maciejwalkowiak @matejnedic1
https://youtube.com/springacademy
@maciejwalkowiak @matejnedic1
MATEJ NEDIC
- Software Engineer
- Spring Cloud AWS Core team member
- working at
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
•Created by Agim Emruli and Alain Shall
•First commit in February 2011
•Community project
•April 2020 - not a part of Spring
Cloud release train
•May 2020 - new maintainers
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
MACIEJ
WALKOWIAK
MATEJ
NEDIC
EDDU
MELENDEZ
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
https://github.com/awspring/spring-cloud-aws
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
https://github.com/awspring/spring-cloud-aws
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
SPRING BOOT
ON AMAZON WEB
SERVICES
WITH SPRING CLOUD AWS
@maciejwalkowiak @matejnedic1
Application RDBMS
@maciejwalkowiak @matejnedic1
EC2
•Updates
•Backups
•Security
•Scaling
•High Availability
RDS
•Updates
•Backups
•Security
•Scaling
•High Availability
@maciejwalkowiak @matejnedic1
EC2
•! Updates
•! Backups
•! Security
•! Scaling
•! High Availability
RDS
•✅ Updates
•✅ Backups
•✅ Security
•✅ Scaling
•✅ High Availability
@maciejwalkowiak @matejnedic1
EC2
•! Updates
•! Backups
•! Security
•! Scaling
•! High Availability
RDS
•✅ Updates
•✅ Backups
•✅ Security
•✅ Scaling
•✅ High Availability
#
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
Application RDS
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica
Asynchronous
Replication
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica
Asynchronous
Replication
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica
Asynchronous
Replication
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica
Asynchronous
Replication
Write
Read
@maciejwalkowiak @matejnedic1
Application
RDS - Primary
RDS - Read Replica #1
Asynchronous
Replication
Write
Read
RDS - Read Replica #2
RDS - Read Replica #3
Read
Read
@maciejwalkowiak @matejnedic1
spring:
datasource:
url: jdbc:postgresql://springone.c0x5be2ybrmz.eu-west-2.rds.amazonaws.com:5432/postgres
username: postgres
password: postgres
@maciejwalkowiak @matejnedic1
cloud:
aws:
rds:
instances:
-
db-instance-identifier: springone
database-name: postgres
username: postgres
password: postgres
@maciejwalkowiak @matejnedic1
cloud:
aws:
rds:
instances:
-
db-instance-identifier: springone
database-name: postgres
username: postgres
password: postgres
read-replica-support: true
@maciejwalkowiak @matejnedic1
@Service
class UserService {
@Transactional
void registerUser(User user) {
...
}
@Transactional(readOnly=true)
User findUser(Long id) {
...
}
}
@maciejwalkowiak @matejnedic1
https://vladmihalcea.com/read-write-read-only-transaction-routing-spring/
@maciejwalkowiak @matejnedic1
•Only Tomcat connection pool supported (will change
in 3.0)
•No Aurora support (will change in 3.0)
WHAT IS MISSING?
@maciejwalkowiak @matejnedic1
•Aurora support
•IAM Authentication
•RDS Proxy Support
•Secrets Manager JDBC authentication (?)
WHAT IS COMING?
@maciejwalkowiak @matejnedic1
MESSAGING ON AWS
@maciejwalkowiak @matejnedic1
Service A Service B
HTTP
@maciejwalkowiak @matejnedic1
Service A Service B
@maciejwalkowiak @matejnedic1
Service A Service B
@maciejwalkowiak @matejnedic1
Service A Service B
@maciejwalkowiak @matejnedic1
Service A Service B
@maciejwalkowiak @matejnedic1
Service A Service B
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html
@maciejwalkowiak @matejnedic1
@Service
public class RegistrationService {
void register(Attendee attendee) {
// persist attendee, send confirmation email
}
}
@maciejwalkowiak @matejnedic1
@Service
public class RegistrationService {
private final QueueMessagingTemplate queueMessagingTemplate;
public RegistrationService(QueueMessagingTemplate queueMessagingTemplate)
this.queueMessagingTemplate = queueMessagingTemplate;
}
void register(Attendee attendee) {
// persist attendee, send confirmation email
queueMessagingTemplate.convertAndSend("springone-queue", attendee);
}
}
@maciejwalkowiak @matejnedic1
@Component
public class AttendeeListener {
private AttendeeRepository attendeeRepository;
public AttendeeListener(AttendeeRepository attendeeRepository) {
this.attendeeRepository = attendeeRepository;
}
@SqsListener("springone-queue")
void handle(Attendee attendee) {
attendeeRepository.save(attendee);
}
}
@maciejwalkowiak @matejnedic1
@Component
public class AttendeeListener {
private AttendeeRepository attendeeRepository;
public AttendeeListener(AttendeeRepository attendeeRepository) {
this.attendeeRepository = attendeeRepository;
}
@SqsListener("springone-queue")
void handle(Attendee attendee, @Header("header-name") String header) {
attendeeRepository.save(attendee);
}
}
@maciejwalkowiak @matejnedic1
@Component
public class AttendeeListener {
private AttendeeRepository attendeeRepository;
public AttendeeListener(AttendeeRepository attendeeRepository) {
this.attendeeRepository = attendeeRepository;
}
@SqsListener("springone-queue")
void handle(Attendee attendee, Acknowledgment acknowledgment) {
if (...) {
acknowledgment.acknowledge();
}
}
}
@maciejwalkowiak @matejnedic1
@Component
public class AttendeeListener {
private AttendeeRepository attendeeRepository;
public AttendeeListener(AttendeeRepository attendeeRepository) {
this.attendeeRepository = attendeeRepository;
}
@SqsListener("springone-queue")
void handle(Attendee attendee, Visibility visibility) {
if (...) {
visibility.extend(2);
}
}
}
@maciejwalkowiak @matejnedic1
@Component
public class AttendeeListener {
private AttendeeRepository attendeeRepository;
public AttendeeListener(AttendeeRepository attendeeRepository) {
this.attendeeRepository = attendeeRepository;
}
@SqsListener("springone-queue")
@SendTo("tickets-queue")
Ticket handle(Attendee attendee) {
attendeeRepository.save(attendee);
// return ticket
}
}
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
@maciejwalkowiak @matejnedic1
Service A Service C
Service B
SNS
HTTP
HTTP
HTTP
@maciejwalkowiak @matejnedic1
@Component
public class SnsNotificationSender {
private final NotificationMessagingTemplate
notificationMessagingTemplate;
public SnsNotificationSender(
NotificationMessagingTemplate notificationMessagingTemplate)
{
this.notificationMessagingTemplate =
notificationMessagingTemplate;
}
public void send(String subject, String message) {
this.notificationMessagingTemplate.sendNotification(
"physicalTopicName", message, subject);
}
}
@maciejwalkowiak @matejnedic1
@Controller
@RequestMapping("/springone-topic")
public class NotificationController {
@NotificationSubscriptionMapping
public void handleSubscriptionMessage(NotificationStatus status) {
//We subscribe to start receive the message
status.confirmSubscription();
}
@NotificationMessageMapping
public void handleNotificationMessage(@NotificationSubject String subject,
@NotificationMessage String message) {
// ...
}
@NotificationUnsubscribeConfirmationMapping
public void handleUnsubscribeMessage(NotificationStatus status) {
//e.g. the client has been unsubscribed and we want to "re-subscribe"
status.confirmSubscription();
}
}
@maciejwalkowiak @matejnedic1
•Performance …
•Reactive support
•Spring Cloud Stream SQS (?)
WHAT IS COMING?
@maciejwalkowiak @matejnedic1
•Performance …
•Reactive support
•Spring Cloud Stream SQS (?)
WHAT IS COMING?
@maciejwalkowiak @matejnedic1
STORING CONFIGURATION WITH
SECRETS MANAGER
AND
PARAMETER STORE
@maciejwalkowiak @matejnedic1
https://cloud.spring.io/spring-cloud-config/reference/html/
Service A
Service B
Service C
@maciejwalkowiak @matejnedic1
https://cloud.spring.io/spring-cloud-config/reference/html/
Service A
Service B
Service C
Spring Cloud Config
@maciejwalkowiak @matejnedic1
https://cloud.spring.io/spring-cloud-config/reference/html/
Service A
Service B
Service C
Spring Cloud Config
DB
File
System
Vault
Git
@maciejwalkowiak @matejnedic1
But what if your
storage for properties
goes down?
@maciejwalkowiak @matejnedic1
AWS PARAMETER STORE
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
Service A Service B
Parameter store
@maciejwalkowiak @matejnedic1
spring.config.import=aws-parameterstore:/config/spring/
spring.config.import=optional:aws-parameterstore:/config/common/;/config/urls/
spring:
config:
import:
- aws-parameterstore:/config/spring/
- optional:aws-parameterstore:/config/common/;/config/urls/
@maciejwalkowiak @matejnedic1
@Value("${message}") String message;
spring.cloud.config.server.git.refreshRate=${refresh}
@maciejwalkowiak @matejnedic1
AWS SECRETS MANAGER
Parameter store
Secret Manager
Service A
Parameter store
Secret Manager
Service A
RDS
Parameter store
Secret Manager
@maciejwalkowiak @matejnedic1
spring.config.import=aws-secretmanager:/secret/db/prod/url
spring.config.import=optional:aws-secretmanager:/secret/common
spring.datasoruce.url=${url}
@maciejwalkowiak @matejnedic1
Parameter Store
• Type: Supports
StringList,String,
SecureString.
• 4KB char max
• Can’t be referenced cross
account
• Can’t rotate secrets
• Cheaper
Secret manager
• Can be referenced cross
account
• Automatic Secret rotation
• More expensive
• Can store more characters 10kb
• Built in password generator
• Supports secret cross region
replication
@maciejwalkowiak @matejnedic1
•Secret rotation support
•Refreshing context on parameter and secret change
WHAT IS COMING?
@maciejwalkowiak @matejnedic1
OTHER SUPPORTED SERVICES
•S3
•EC2 metadata
•Cloud Watch
•ElastiCache (Redis & Memcached)
•SES (Simple Email Service)
•Cloud Formation
@maciejwalkowiak @matejnedic1
@maciejwalkowiak @matejnedic1
WHEN SPRING CLOUD AWS IS NOT ENOUGH
•Spring Cloud Stream Kinesis Binder
https://github.com/spring-cloud/spring-cloud-stream-binder-aws-kinesis
•Spring Integration AWS
https://github.com/spring-projects/spring-integration-aws
•Spring Cloud Config Server
https://github.com/spring-cloud/spring-cloud-config
•Spring Data DynamoDB
https://github.com/boostchicken/spring-data-dynamodb
@maciejwalkowiak @matejnedic1
•AWS SDK:
•AWS SDK v1
•AWS SDK v2
WHEN SPRING CLOUD AWS IS NOT ENOUGH
@maciejwalkowiak @matejnedic1
•AWS SDK v2
•$ GraalVM Native Image compatible
•$ Non Blocking - Spring WebFlux friendly!
•$ Pluggable HTTP client
•$ Does not conflict with SDK v1
•% Still no feature parity with 1.x
WHEN SPRING CLOUD AWS IS NOT ENOUGH
@maciejwalkowiak @matejnedic1
TESTING
@maciejwalkowiak @matejnedic1
TESTING WITH LOCALSTACK
@maciejwalkowiak @matejnedic1
version: '3.1'
services:
localstack:
image: localstack/localstack:latest
environment:
- SERVICES=sqs,s3
ports:
- ‘4566:4566'
- ‘4571:4571’
volumes:
- "${TEMPDIR:-/tmp/localstack}:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
LOCALSTACK
@maciejwalkowiak @matejnedic1
$ docker-compose up
localstack_1 |
localstack_1 | __ _______ __ __
localstack_1 | / / ____ _________ _/ / ___// /_____ ______/ /__
localstack_1 | / / / __ / ___/ __ `/ /__ / __/ __ `/ ___/ //_/
localstack_1 | / /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
localstack_1 | /_____/____/___/__,_/_//____/__/__,_/___/_/|_|
localstack_1 |
localstack_1 | & LocalStack CLI 0.12.17.3
localstack_1 |
localstack_1 | [16:23:49] starting LocalStack in host mode & localstack.py:101
localstack_1 | ──────────────── LocalStack Runtime Log (press CTRL-C to quit) ─────────────────
localstack_1 | 2021-09-01 16:23:50,722 INFO success: infra entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
localstack_1 |
localstack_1 | LocalStack version: 0.12.17.3
localstack_1 | LocalStack Docker container id: 67fb82278492
localstack_1 | LocalStack build date: 2021-09-01
localstack_1 | LocalStack build git hash: 637d9bfa
localstack_1 |
localstack_1 | Starting edge router (https port 4566)...
localstack_1 | Starting mock S3 service on http port 4566 ...
localstack_1 | 2021-09-01T16:23:54:INFO:localstack.multiserver: Starting multi API server process on port 44089
localstack_1 | [2021-09-01 16:23:54 +0000] [21] [INFO] Running on https://0.0.0.0:4566 (CTRL + C to quit)
localstack_1 | 2021-09-01T16:23:54:INFO:hypercorn.error: Running on https://0.0.0.0:4566 (CTRL + C to quit)
localstack_1 | [2021-09-01 16:23:54 +0000] [21] [INFO] Running on http://0.0.0.0:44089 (CTRL + C to quit)
localstack_1 | 2021-09-01T16:23:54:INFO:hypercorn.error: Running on http://0.0.0.0:44089 (CTRL + C to quit)
localstack_1 | Waiting for all LocalStack services to be ready
localstack_1 | Starting mock SQS service on http port 4566 ...
localstack_1 | Ready.
LOCALSTACK
@maciejwalkowiak @matejnedic1
$ aws sqs create-queue --queue-name “hello-springone" --endpoint-url http://localhost:4566
LOCALSTACK
@maciejwalkowiak @matejnedic1
$ aws sqs create-queue --queue-name “hello-springone" --endpoint-url http://localhost:4566
LOCALSTACK
{
"QueueUrl": "http://localhost:4566/000000000000/hello-springone"
}
@maciejwalkowiak @matejnedic1
$ aws sqs create-queue --queue-name “hello-springone" --endpoint-url http://localhost:4566
LOCALSTACK
{
"QueueUrl": "http://localhost:4566/000000000000/hello-springone"
}
$ aws sqs list-queues --endpoint-url http://localhost:4566
@maciejwalkowiak @matejnedic1
$ aws sqs create-queue --queue-name “hello-springone" --endpoint-url http://localhost:4566
LOCALSTACK
{
"QueueUrl": "http://localhost:4566/000000000000/hello-springone"
}
$ aws sqs list-queues --endpoint-url http://localhost:4566
{
"QueueUrls": [
"http://localhost:4566/000000000000/testqueue",
"http://localhost:4566/000000000000/hello-springone"
]
}
@maciejwalkowiak @matejnedic1
cloud.aws.sqs.endpoint: http://localhost:4566
LOCALSTACK
cloud.aws.sns.endpoint: http://localhost:4566
@maciejwalkowiak @matejnedic1
@SpringBootTest
@Testcontainers
class DemoApplicationTests {
@Container
static LocalStackContainer localstack =
new LocalStackContainer(DockerImageName.parse("localstack/localstack:0.12.17"))
.withServices(LocalStackContainer.Service.SQS);
@DynamicPropertySource
static void localstackProperties(DynamicPropertyRegistry registry) {
registry.add("clous.aws.sqs.endpoint",
() -> localstack.getEndpointOverride(LocalStackContainer.Service.SQS));
}
// ..
}
LOCALSTACK
@maciejwalkowiak @matejnedic1
LOCALSTACK
• ACM
• API Gateway
• CloudFormation
• CloudWatch
• CloudWatch Logs
• DynamoDB
• DynamoDB Streams
• EC2
• Elasticsearch Service
• EventBridge
(CloudWatch Events)
• Firehose
• IAM
• Kinesis
• KMS
• Lambda
• Redshift
• STS
• Route53
• S3
• SecretsManager
• SES
• SNS
• SQS
• SSM
• StepFunctions
@maciejwalkowiak @matejnedic1
LOCALSTACK
• Amplify
• API Gateway V2
(WebSockets support)
• AppConfig
• Application
AutoScaling
• AppSync
• Athena
• Backup
• Batch
• CloudFront
• CloudTrail
• CodeCommit
• Cognito
• CostExplorer
• DocumentDB
• ECR/ECS/EKS
• ElastiCache
• ElasticBeanstalk
• ELB/ELBv2
• EMR
• Glacier / S3 Select
• Glue
• IAM Security Policy
Enforcement
• IoT
• Kinesis Data Analytics
• Lambda Layers &
Container Images
@maciejwalkowiak @matejnedic1
LOCALSTACK
https://localstack.dev
@maciejwalkowiak @matejnedic1
FUTURE
@maciejwalkowiak @matejnedic1
•Migrate from AWS SDK v1 to AWS SDK v2
•CloudMap integration (PR ready)
•Spring Data Dynamo DB (?)
•Drop support for: ElastiCache, CloudFormation
•Improve startup times
•GraalVM Native Image compatibility
FUTURE  SPRING CLOUD AWS 3.X
@maciejwalkowiak @matejnedic1
SPRING CLOUD AWS
SAMPLES
@maciejwalkowiak @matejnedic1
https://github.com/awspring/spring-cloud-aws/tree/2.3.x/spring-cloud-aws-samples
https://awspring.io/
https://stratospheric.dev/
BJÖRN WILMSMANN
PHILIP RIECKS
TOM HOMBERGS
@maciejwalkowiak @matejnedic1
THANK YOU!

Spring Boot on Amazon Web Services with Spring Cloud AWS