Repository: amigoscode/microservices
Branch: main
Commit: 12dcd5d4758b
Files: 76
Total size: 655.9 KB
Directory structure:
gitextract_8gj87pry/
├── .gitignore
├── README.md
├── amqp/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── amigoscode/
│ └── amqp/
│ ├── RabbitMQConfig.java
│ └── RabbitMQMessageProducer.java
├── apigw/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── apigw/
│ │ └── ApiGWApplication.java
│ └── resources/
│ ├── application-docker.yml
│ ├── application.yml
│ └── banner.txt
├── clients/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── clients/
│ │ ├── fraud/
│ │ │ ├── FraudCheckResponse.java
│ │ │ └── FraudClient.java
│ │ └── notification/
│ │ ├── NotificationClient.java
│ │ └── NotificationRequest.java
│ └── resources/
│ ├── clients-default.properties
│ ├── clients-docker.properties
│ └── clients-kube.properties
├── customer/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── customer/
│ │ ├── Customer.java
│ │ ├── CustomerApplication.java
│ │ ├── CustomerController.java
│ │ ├── CustomerRegistrationRequest.java
│ │ ├── CustomerRepository.java
│ │ └── CustomerService.java
│ └── resources/
│ ├── application-docker.yml
│ ├── application-kube.yml
│ ├── application.yml
│ └── banner.txt
├── diagrams.drawio
├── docker-compose.yml
├── eureka-server/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── eurekaserver/
│ │ └── EurekaServerApplication.java
│ └── resources/
│ ├── application-docker.yml
│ ├── application.yml
│ └── banner.txt
├── fraud/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── fraud/
│ │ ├── FraudApplication.java
│ │ ├── FraudCheckHistory.java
│ │ ├── FraudCheckHistoryRepository.java
│ │ ├── FraudCheckService.java
│ │ └── FraudController.java
│ └── resources/
│ ├── application-docker.yml
│ ├── application-kube.yml
│ ├── application.yml
│ └── banner.txt
├── k8s/
│ └── minikube/
│ ├── bootstrap/
│ │ ├── postgres/
│ │ │ ├── configmap.yml
│ │ │ ├── service.yml
│ │ │ ├── statefulset.yml
│ │ │ └── volume.yml
│ │ ├── rabbitmq/
│ │ │ ├── README.md
│ │ │ ├── configmap.yaml
│ │ │ ├── rbac.yaml
│ │ │ ├── services.yaml
│ │ │ └── statefulset.yaml
│ │ └── zipkin/
│ │ ├── service.yml
│ │ └── statefulset.yml
│ └── services/
│ ├── customer/
│ │ ├── deployment.yml
│ │ └── service.yml
│ ├── fraud/
│ │ ├── deployment.yml
│ │ └── service.yml
│ └── notification/
│ ├── deployment.yml
│ └── service.yml
├── notification/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── amigoscode/
│ │ └── notification/
│ │ ├── Notification.java
│ │ ├── NotificationApplication.java
│ │ ├── NotificationConfig.java
│ │ ├── NotificationController.java
│ │ ├── NotificationRepository.java
│ │ ├── NotificationService.java
│ │ └── rabbitmq/
│ │ └── NotificationConsumer.java
│ └── resources/
│ ├── application-docker.yml
│ ├── application-kube.yml
│ ├── application.yml
│ └── banner.txt
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
HELP.md
target/
**/target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.idea/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
================================================
FILE: README.md
================================================
# Microservices

================================================
FILE: amqp/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
amqp
org.springframework.boot
spring-boot-starter-amqp
================================================
FILE: amqp/src/main/java/com/amigoscode/amqp/RabbitMQConfig.java
================================================
package com.amigoscode.amqp;
import lombok.AllArgsConstructor;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@AllArgsConstructor
public class RabbitMQConfig {
private final ConnectionFactory connectionFactory;
@Bean
public AmqpTemplate amqpTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jacksonConverter());
return rabbitTemplate;
}
@Bean
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory =
new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jacksonConverter());
return factory;
}
@Bean
public MessageConverter jacksonConverter() {
MessageConverter jackson2JsonMessageConverter =
new Jackson2JsonMessageConverter();
return jackson2JsonMessageConverter;
}
}
================================================
FILE: amqp/src/main/java/com/amigoscode/amqp/RabbitMQMessageProducer.java
================================================
package com.amigoscode.amqp;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Component;
@Component
@Slf4j
@AllArgsConstructor
public class RabbitMQMessageProducer {
private final AmqpTemplate amqpTemplate;
public void publish(Object payload, String exchange, String routingKey) {
log.info("Publishing to {} using routingKey {}. Payload: {}", exchange, routingKey, payload);
amqpTemplate.convertAndSend(exchange, routingKey, payload);
log.info("Published to {} using routingKey {}. Payload: {}", exchange, routingKey, payload);
}
}
================================================
FILE: apigw/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
apigw
org.springframework.boot
spring-boot-maven-plugin
build-docker-image
com.google.cloud.tools
jib-maven-plugin
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-sleuth-zipkin
================================================
FILE: apigw/src/main/java/com/amigoscode/apigw/ApiGWApplication.java
================================================
package com.amigoscode.apigw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class ApiGWApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGWApplication.class, args);
}
}
================================================
FILE: apigw/src/main/resources/application-docker.yml
================================================
server:
port: 8083
spring:
application:
name: api-gateway
zipkin:
base-url: http://zipkin:9411
cloud:
gateway:
routes:
- id: customer
uri: lb://CUSTOMER
predicates:
- Path=/api/v1/customers/**
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
================================================
FILE: apigw/src/main/resources/application.yml
================================================
server:
port: 8083
spring:
application:
name: api-gateway
zipkin:
base-url: http://localhost:9411
cloud:
gateway:
routes:
- id: customer
uri: lb://CUSTOMER
predicates:
- Path=/api/v1/customers/**
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
fetch-registry: true
register-with-eureka: true
================================================
FILE: apigw/src/main/resources/banner.txt
================================================
,---. ,------. ,--. ,----. ,--. ,--.
/ O \ | .--. ' | | ' .-./ | | | |
| .-. | | '--' | | | | | .---. | |.'.| |
| | | | | | --' | | ' '--' | | ,'. |
`--' `--' `--' `--' `------' '--' '--'
${application.title} ${application.version}
Powered by Spring Boot ${spring-boot.version}
================================================
FILE: clients/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
clients
================================================
FILE: clients/src/main/java/com/amigoscode/clients/fraud/FraudCheckResponse.java
================================================
package com.amigoscode.clients.fraud;
public record FraudCheckResponse(Boolean isFraudster) {
}
================================================
FILE: clients/src/main/java/com/amigoscode/clients/fraud/FraudClient.java
================================================
package com.amigoscode.clients.fraud;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(
name = "fraud",
url = "${clients.fraud.url}"
)
public interface FraudClient {
@GetMapping(path = "api/v1/fraud-check/{customerId}")
FraudCheckResponse isFraudster(
@PathVariable("customerId") Integer customerId);
}
================================================
FILE: clients/src/main/java/com/amigoscode/clients/notification/NotificationClient.java
================================================
package com.amigoscode.clients.notification;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
@FeignClient(
name = "notification",
url = "${clients.notification.url}"
)
public interface NotificationClient {
@PostMapping("api/v1/notification")
void sendNotification(NotificationRequest notificationRequest);
}
================================================
FILE: clients/src/main/java/com/amigoscode/clients/notification/NotificationRequest.java
================================================
package com.amigoscode.clients.notification;
public record NotificationRequest(
Integer toCustomerId,
String toCustomerName,
String message
) {
}
================================================
FILE: clients/src/main/resources/clients-default.properties
================================================
clients.customer.url=http://localhost:8080
clients.fraud.url=http://localhost:8081
clients.notification.url=http://localhost:8082
================================================
FILE: clients/src/main/resources/clients-docker.properties
================================================
clients.customer.url=http://customer:8080
clients.fraud.url=http://fraud:8081
clients.notification.url=http://notification:8082
================================================
FILE: clients/src/main/resources/clients-kube.properties
================================================
clients.customer.url=http://customer:8080
clients.fraud.url=http://fraud:8081
clients.notification.url=http://notification:8082
================================================
FILE: customer/pom.xml
================================================
com.amigoscode
amigosservices
1.0-SNAPSHOT
4.0.0
jar
customer
org.springframework.boot
spring-boot-maven-plugin
build-docker-image
com.google.cloud.tools
jib-maven-plugin
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.postgresql
postgresql
runtime
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-sleuth-zipkin
org.springframework.boot
spring-boot-starter-amqp
com.amigoscode
amqp
1.0-SNAPSHOT
compile
com.amigoscode
clients
1.0-SNAPSHOT
compile
================================================
FILE: customer/src/main/java/com/amigoscode/customer/Customer.java
================================================
package com.amigoscode.customer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.GenerationType;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Customer {
@Id
@SequenceGenerator(
name = "customer_id_sequence",
sequenceName = "customer_id_sequence"
)
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "customer_id_sequence"
)
private Integer id;
private String firstName;
private String lastName;
private String email;
}
================================================
FILE: customer/src/main/java/com/amigoscode/customer/CustomerApplication.java
================================================
package com.amigoscode.customer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
@SpringBootApplication(
scanBasePackages = {
"com.amigoscode.customer",
"com.amigoscode.amqp",
}
)
@EnableEurekaClient
@EnableFeignClients(
basePackages = "com.amigoscode.clients"
)
@PropertySources({
@PropertySource("classpath:clients-${spring.profiles.active}.properties")
})
public class CustomerApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerApplication.class, args);
}
}
================================================
FILE: customer/src/main/java/com/amigoscode/customer/CustomerController.java
================================================
package com.amigoscode.customer;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("api/v1/customers")
@AllArgsConstructor
public class CustomerController {
private final CustomerService customerService;
@PostMapping
public void registerCustomer(@RequestBody CustomerRegistrationRequest customerRegistrationRequest) {
log.info("new customer registration {}", customerRegistrationRequest);
customerService.registerCustomer(customerRegistrationRequest);
}
}
================================================
FILE: customer/src/main/java/com/amigoscode/customer/CustomerRegistrationRequest.java
================================================
package com.amigoscode.customer;
public record CustomerRegistrationRequest(
String firstName,
String lastName,
String email) {
}
================================================
FILE: customer/src/main/java/com/amigoscode/customer/CustomerRepository.java
================================================
package com.amigoscode.customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository {
}
================================================
FILE: customer/src/main/java/com/amigoscode/customer/CustomerService.java
================================================
package com.amigoscode.customer;
import com.amigoscode.amqp.RabbitMQMessageProducer;
import com.amigoscode.clients.fraud.FraudCheckResponse;
import com.amigoscode.clients.fraud.FraudClient;
import com.amigoscode.clients.notification.NotificationClient;
import com.amigoscode.clients.notification.NotificationRequest;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@AllArgsConstructor
public class CustomerService {
private final CustomerRepository customerRepository;
private final FraudClient fraudClient;
private final RabbitMQMessageProducer rabbitMQMessageProducer;
public void registerCustomer(CustomerRegistrationRequest request) {
Customer customer = Customer.builder()
.firstName(request.firstName())
.lastName(request.lastName())
.email(request.email())
.build();
// todo: check if email valid
// todo: check if email not taken
customerRepository.saveAndFlush(customer);
FraudCheckResponse fraudCheckResponse =
fraudClient.isFraudster(customer.getId());
if (fraudCheckResponse.isFraudster()) {
throw new IllegalStateException("fraudster");
}
NotificationRequest notificationRequest = new NotificationRequest(
customer.getId(),
customer.getEmail(),
String.format("Hi %s, welcome to Amigoscode...",
customer.getFirstName())
);
rabbitMQMessageProducer.publish(
notificationRequest,
"internal.exchange",
"internal.notification.routing-key"
);
}
}
================================================
FILE: customer/src/main/resources/application-docker.yml
================================================
server:
port: 8080
spring:
application:
name: customer
datasource:
password: password
url: jdbc:postgresql://postgres:5432/customer
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
rabbitmq:
addresses: rabbitmq:5672
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
================================================
FILE: customer/src/main/resources/application-kube.yml
================================================
server:
port: 8080
spring:
application:
name: customer
datasource:
password: password
url: jdbc:postgresql://postgres:5432/customer
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
rabbitmq:
addresses: rabbitmq:5672
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
================================================
FILE: customer/src/main/resources/application.yml
================================================
server:
port: 8080
spring:
application:
name: customer
datasource:
password: password
url: jdbc:postgresql://localhost:5432/customer
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://localhost:9411
rabbitmq:
addresses: localhost:5672
profiles:
active: default
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: true
================================================
FILE: customer/src/main/resources/banner.txt
================================================
,-----. ,--.
' .--./ ,--.,--. ,---. ,-' '-. ,---. ,--,--,--. ,---. ,--.--.
| | | || | ( .-' '-. .-' | .-. | | | | .-. : | .--'
' '--'\ ' '' ' .-' `) | | ' '-' ' | | | | \ --. | |
`-----' `----' `----' `--' `---' `--`--`--' `----' `--'
================================================
FILE: diagrams.drawio
================================================
================================================
FILE: docker-compose.yml
================================================
services:
postgres:
container_name: postgres
image: postgres
environment:
POSTGRES_USER: amigoscode
POSTGRES_PASSWORD: password
PGDATA: /data/postgres
volumes:
- postgres:/data/postgres
ports:
- "5432:5432"
networks:
- postgres
restart: unless-stopped
pgadmin:
container_name: pgadmin
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
PGADMIN_CONFIG_SERVER_MODE: 'False'
volumes:
- pgadmin:/var/lib/pgadmin
ports:
- "5050:80"
networks:
- postgres
restart: unless-stopped
zipkin:
image: openzipkin/zipkin
container_name: zipkin
ports:
- "9411:9411"
networks:
- spring
rabbitmq:
image: rabbitmq:3.9.11-management-alpine
container_name: rabbitmq
ports:
- "5672:5672"
- "15672:15672"
networks:
- spring
# eureka-server:
# image: amigoscode/eureka-server:latest
# container_name: eureka-server
# ports:
# - "8761:8761"
# environment:
# - SPRING_PROFILES_ACTIVE=docker
# networks:
# - spring
# depends_on:
# - zipkin
# apigw:
# image: amigoscode/apigw:latest
# container_name: apigw
# ports:
# - "8083:8083"
# environment:
# - SPRING_PROFILES_ACTIVE=docker
# networks:
# - spring
# depends_on:
# - zipkin
# - eureka-server
# customer:
# image: amigoscode/customer:latest
# container_name: customer
# ports:
# - "8080:8080"
# environment:
# - SPRING_PROFILES_ACTIVE=docker
# networks:
# - spring
# - postgres
# depends_on:
# - zipkin
# - postgres
# - rabbitmq
# fraud:
# image: amigoscode/fraud:latest
# container_name: fraud
# ports:
# - "8081:8081"
# environment:
# - SPRING_PROFILES_ACTIVE=docker
# networks:
# - spring
# - postgres
# depends_on:
# - zipkin
# - postgres
# - rabbitmq
# notification:
# image: amigoscode/notification:latest
# container_name: notification
# ports:
# - "8082:8082"
# environment:
# - SPRING_PROFILES_ACTIVE=docker
# networks:
# - spring
# - postgres
# depends_on:
# - zipkin
# - postgres
# - rabbitmq
networks:
postgres:
driver: bridge
spring:
driver: bridge
volumes:
postgres:
pgadmin:
================================================
FILE: eureka-server/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
eureka-server
org.springframework.boot
spring-boot-maven-plugin
build-docker-image
com.google.cloud.tools
jib-maven-plugin
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-sleuth-zipkin
================================================
FILE: eureka-server/src/main/java/com/amigoscode/eurekaserver/EurekaServerApplication.java
================================================
package com.amigoscode.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
================================================
FILE: eureka-server/src/main/resources/application-docker.yml
================================================
spring:
application:
name: eureka-server
zipkin:
base-url: http://zipkin:9411
server:
port: 8761
eureka:
client:
fetch-registry: false
register-with-eureka: false
================================================
FILE: eureka-server/src/main/resources/application.yml
================================================
spring:
application:
name: eureka-server
zipkin:
base-url: http://localhost:9411
server:
port: 8761
eureka:
client:
fetch-registry: false
register-with-eureka: false
================================================
FILE: eureka-server/src/main/resources/banner.txt
================================================
,------. ,--. ,---.
| .---' ,--.,--. ,--.--. ,---. | |,-. ,--,--. ' .-' ,---. ,--.--. ,--. ,--. ,---. ,--.--.
| `--, | || | | .--' | .-. : | / ' ,-. | `. `-. | .-. : | .--' \ `' / | .-. : | .--'
| `---. ' '' ' | | \ --. | \ \ \ '-' | .-' | \ --. | | \ / \ --. | |
`------' `----' `--' `----' `--'`--' `--`--' `-----' `----' `--' `--' `----' `--'
================================================
FILE: fraud/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
fraud
org.springframework.boot
spring-boot-maven-plugin
build-docker-image
com.google.cloud.tools
jib-maven-plugin
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.postgresql
postgresql
runtime
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-sleuth-zipkin
com.amigoscode
clients
1.0-SNAPSHOT
compile
================================================
FILE: fraud/src/main/java/com/amigoscode/fraud/FraudApplication.java
================================================
package com.amigoscode.fraud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
@SpringBootApplication
@EnableEurekaClient
@PropertySources({
@PropertySource("classpath:clients-${spring.profiles.active}.properties")
})
public class FraudApplication {
public static void main(String[] args) {
SpringApplication.run(FraudApplication.class, args);
}
}
================================================
FILE: fraud/src/main/java/com/amigoscode/fraud/FraudCheckHistory.java
================================================
package com.amigoscode.fraud;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.GenerationType;
import java.time.LocalDateTime;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class FraudCheckHistory {
@Id
@SequenceGenerator(
name = "fraud_id_sequence",
sequenceName = "fraud_id_sequence"
)
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "fraud_id_sequence"
)
private Integer id;
private Integer customerId;
private Boolean isFraudster;
private LocalDateTime createdAt;
}
================================================
FILE: fraud/src/main/java/com/amigoscode/fraud/FraudCheckHistoryRepository.java
================================================
package com.amigoscode.fraud;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FraudCheckHistoryRepository
extends JpaRepository {
}
================================================
FILE: fraud/src/main/java/com/amigoscode/fraud/FraudCheckService.java
================================================
package com.amigoscode.fraud;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
@AllArgsConstructor
public class FraudCheckService {
private final FraudCheckHistoryRepository fraudCheckHistoryRepository;
public boolean isFraudulentCustomer(Integer customerId) {
fraudCheckHistoryRepository.save(
FraudCheckHistory.builder()
.customerId(customerId)
.isFraudster(false)
.createdAt(LocalDateTime.now())
.build()
);
return false;
}
}
================================================
FILE: fraud/src/main/java/com/amigoscode/fraud/FraudController.java
================================================
package com.amigoscode.fraud;
import com.amigoscode.clients.fraud.FraudCheckResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("api/v1/fraud-check")
@AllArgsConstructor
@Slf4j
public class FraudController {
private final FraudCheckService fraudCheckService;
@GetMapping(path = "{customerId}")
public FraudCheckResponse isFraudster(
@PathVariable("customerId") Integer customerId) {
boolean isFraudulentCustomer = fraudCheckService.
isFraudulentCustomer(customerId);
log.info("fraud check request for customer {}", customerId);
return new FraudCheckResponse(isFraudulentCustomer);
}
}
================================================
FILE: fraud/src/main/resources/application-docker.yml
================================================
server:
port: 8081
spring:
application:
name: fraud
datasource:
password: password
url: jdbc:postgresql://postgres:5432/fraud
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
================================================
FILE: fraud/src/main/resources/application-kube.yml
================================================
server:
port: 8081
spring:
application:
name: fraud
datasource:
password: password
url: jdbc:postgresql://postgres:5432/fraud
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
================================================
FILE: fraud/src/main/resources/application.yml
================================================
server:
port: 8081
spring:
application:
name: fraud
datasource:
password: password
url: jdbc:postgresql://localhost:5432/fraud
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://localhost:9411
profiles:
active: default
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: true
================================================
FILE: fraud/src/main/resources/banner.txt
================================================
,------. ,--.
| .---' ,--.--. ,--,--. ,--.,--. ,-| |
| `--, | .--' ' ,-. | | || | ' .-. |
| |` | | \ '-' | ' '' ' \ `-' |
`--' `--' `--`--' `----' `---'
================================================
FILE: k8s/minikube/bootstrap/postgres/configmap.yml
================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
data:
POSTGRES_DB: amigoscode
POSTGRES_USER: amigoscode
POSTGRES_PASSWORD: password
================================================
FILE: k8s/minikube/bootstrap/postgres/service.yml
================================================
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
type: ClusterIP
================================================
FILE: k8s/minikube/bootstrap/postgres/statefulset.yml
================================================
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
labels:
app: postgres
spec:
serviceName: postgres
replicas: 1
template:
metadata:
name: postgres
labels:
app: postgres
spec:
volumes:
- name: postgres
persistentVolumeClaim:
claimName: postgres-pc-volume-claim
containers:
- name: postgres
image: postgres
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: "/var/lib/postgresql/data"
name: postgres
envFrom:
- configMapRef:
name: postgres-config
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
restartPolicy: Always
selector:
matchLabels:
app: postgres
================================================
FILE: k8s/minikube/bootstrap/postgres/volume.yml
================================================
apiVersion: v1
kind: PersistentVolume
metadata:
name: postgres-pc-volume
labels:
type: local
app: postgres
spec:
storageClassName: manual
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
hostPath:
path: /mnt/data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pc-volume-claim
labels:
app: postgres
spec:
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
================================================
FILE: k8s/minikube/bootstrap/rabbitmq/README.md
================================================
# Deploy RabbitMQ on Kubernetes with the Kubernetes Peer Discovery Plugin to Minikube
This is an **example** that demonstrates a RabbitMQ deployment on Kubernetes with peer discovery
via `rabbitmq-peer-discovery-k8s` plugin.
## Production (Non-)Suitability
Some values in this example **may or may not be optimal for your deployment**. We encourage users
to get familiar with the [RabbitMQ Peer Discovery guide](https://www.rabbitmq.com/cluster-formation.html), [RabbitMQ Production Checklist](https://www.rabbitmq.com/production-checklist.html)
and the rest of [RabbitMQ documentation](https://www.rabbitmq.com/documentation.html) before going into production.
Having [metrics](https://www.rabbitmq.com/monitoring.html), both of RabbitMQ and applications that use it,
is critically important when making informed decisions about production systems.
## Pre-requisites
The example uses, targets or assumes:
* [Minikube](https://kubernetes.io/docs/setup/learning-environment/minikube/) with the [VirtualBox](https://www.virtualbox.org/) driver (other drivers can be used, too)
* Kubernetes 1.6
* RabbitMQ [Docker image](https://hub.docker.com/_/rabbitmq/) (maintained [by Docker, Inc](https://hub.docker.com/_/rabbitmq/))
* A [StatefulSets controller](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
## Quick Start with Make
This example comes with a Make target that sets up VirtualBox, Minikube and an example cluster
in a single command. It can be found under this directory. [Homebrew](https://brew.sh/) will be used to install
packages and on macOS, VirtualBox [will need OS permissions to install its kernel module](https://developer.apple.com/library/archive/technotes/tn2459/_index.html).
The Homebrew cask installer will ask for your password at some point with a prompt that looks like this:
```
Changing ownership of paths required by virtualbox; your password may be necessary
```
Please inspect the Make file to be extra sure that you understand and agree to what it does.
After enabling 3rd party kernel extensions in OS setings, run the default Make target in this directory:
```
make
```
which is equivalent to first running
```
make start-minikube
```
to install VirtualBox and Minikube using Homebrew, then
```
make run-in-minikube
```
to start Minikube and `kubectl apply` the example, and finally
```
make wait-for-rabbitmq
```
to wait for cluster formation.
Once the changes are applied, follow the steps in the Check Cluster Status section below.
In case you would prefer to install and run Minikube manually, see the following few sections.
## Running the Example Manually with Minikube
### Preresuites
* Make sure that VirtualBox is installed
* Install [`minikube`](https://kubernetes.io/docs/tasks/tools/install-minikube/) and start it with `--vm-driver=virtualbox`
* Install [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
### Start Minikube
Start a `minikube` virtual machine:
``` sh
minikube start --cpus=2 --memory=2040 --disk-size "10 GB" --vm-driver=virtualbox
```
### Create a Namespace
Create a Kubernetes namespace for RabbitMQ tests:
``` sh
kubectl create namespace test-rabbitmq
```
### Set Up Kubernetes Permissions
In Kubernetes 1.6 or above, RBAC authorization is enabled by default.
This example configures RBAC related bits so that the peer discovery plugin is allowed to access
the nodes information it needs. The `ServiceAccount` and `Role` resources will be created
in the following step.
### kubectl Apply Things
Deploy the config map, services, a stateful set and so on:
``` sh
# will apply all files under this directory
kubectl create -f minikube
```
### Check Cluster Status
Wait for a a few minutes for pods to start. Since this example uses a stateful set with ordered
startup, the pods will be started one by one. To monitor pod startup process, use
``` sh
kubectl --namespace="test-rabbitmq" get pods
```
To run `rabbitmq-diagnostics cluster_status`:
``` sh
FIRST_POD=$(kubectl get pods --namespace test-rabbitmq -l 'app=rabbitmq' -o jsonpath='{.items[0].metadata.name }')
kubectl exec --namespace=test-rabbitmq $FIRST_POD -- rabbitmq-diagnostics cluster_status
```
to check cluster status. Note that nodes can take some time to start and discover each other.
The output should look something like this:
```
Cluster status of node rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local ...
Basics
Cluster name: rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local
Disk Nodes
rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local
rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local
rabbit@rabbitmq-2.rabbitmq.test-rabbitmq.svc.cluster.local
Running Nodes
rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local
rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local
rabbit@rabbitmq-2.rabbitmq.test-rabbitmq.svc.cluster.local
Versions
rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local: RabbitMQ 3.8.1 on Erlang 22.1.8
rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local: RabbitMQ 3.8.1 on Erlang 22.1.8
rabbit@rabbitmq-2.rabbitmq.test-rabbitmq.svc.cluster.local: RabbitMQ 3.8.1 on Erlang 22.1.8
Alarms
(none)
Network Partitions
(none)
Listeners
Node: rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@rabbitmq-0.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 15672, protocol: http, purpose: HTTP API
Node: rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Node: rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0
Node: rabbit@rabbitmq-1.rabbitmq.test-rabbitmq.svc.cluster.local, interface: [::], port: 15672, protocol: http, purpose: HTTP API
Feature flags
Flag: drop_unroutable_metric, state: enabled
Flag: empty_basic_get_metric, state: enabled
Flag: implicit_default_bindings, state: enabled
Flag: quorum_queue, state: enabled
Flag: virtual_host_metadata, state: enabled
```
### Use Public Minikube IP Address to Connect
Get the public `minikube` VM IP address:
``` sh
minikube ip
# => 192.168.99.104
```
The [ports used](https://www.rabbitmq.com/networking.html#ports) by this example are:
* `amqp://guest:guest@{minikube_ip}:30672`: [AMQP 0-9-1 and AMQP 1.0](https://www.rabbitmq.com/networking.html#ports) client connections
* `http://{minikube_ip}:31672`: [HTTP API and management UI](https://www.rabbitmq.com/management.html)
### Scaling the Number of RabbitMQ Cluster Nodes (Kubernetes Pod Replicas)
``` sh
# Odd numbers of nodes are necessary for a clear quorum: 3, 5, 7 and so on
kubectl scale statefulset/rabbitmq --namespace=test-rabbitmq --replicas=5
```
================================================
FILE: k8s/minikube/bootstrap/rabbitmq/configmap.yaml
================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: rabbitmq-config
data:
enabled_plugins: |
[rabbitmq_management,rabbitmq_peer_discovery_k8s].
rabbitmq.conf: |
## Cluster formation. See https://www.rabbitmq.com/cluster-formation.html to learn more.
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s
cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
## Should RabbitMQ node name be computed from the pod's hostname or IP address?
## IP addresses are not stable, so using [stable] hostnames is recommended when possible.
## Set to "hostname" to use pod hostnames.
## When this value is changed, so should the variable used to set the RABBITMQ_NODENAME
## environment variable.
cluster_formation.k8s.address_type = hostname
## How often should node cleanup checks run?
cluster_formation.node_cleanup.interval = 30
## Set to false if automatic removal of unknown/absent nodes
## is desired. This can be dangerous, see
## * https://www.rabbitmq.com/cluster-formation.html#node-health-checks-and-cleanup
## * https://groups.google.com/forum/#!msg/rabbitmq-users/wuOfzEywHXo/k8z_HWIkBgAJ
cluster_formation.node_cleanup.only_log_warning = true
cluster_partition_handling = autoheal
## See https://www.rabbitmq.com/ha.html#master-migration-data-locality
queue_master_locator=min-masters
## This is just an example.
## This enables remote access for the default user with well known credentials.
## Consider deleting the default user and creating a separate user with a set of generated
## credentials instead.
## Learn more at https://www.rabbitmq.com/access-control.html#loopback-users
loopback_users.guest = false
================================================
FILE: k8s/minikube/bootstrap/rabbitmq/rbac.yaml
================================================
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: rabbitmq
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rabbitmq-peer-discovery-rbac
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rabbitmq-peer-discovery-rbac
subjects:
- kind: ServiceAccount
name: rabbitmq
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: rabbitmq-peer-discovery-rbac
================================================
FILE: k8s/minikube/bootstrap/rabbitmq/services.yaml
================================================
kind: Service
apiVersion: v1
metadata:
name: rabbitmq
labels:
app: rabbitmq
type: LoadBalancer
spec:
type: NodePort
ports:
- name: http
protocol: TCP
port: 15672
targetPort: 15672
nodePort: 31672
- name: amqp
protocol: TCP
port: 5672
targetPort: 5672
nodePort: 30672
selector:
app: rabbitmq
================================================
FILE: k8s/minikube/bootstrap/rabbitmq/statefulset.yaml
================================================
apiVersion: apps/v1
# See the Prerequisites section of https://www.rabbitmq.com/cluster-formation.html#peer-discovery-k8s.
kind: StatefulSet
metadata:
name: rabbitmq
spec:
serviceName: rabbitmq
# Three nodes is the recommended minimum. Some features may require a majority of nodes
# to be available.
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
serviceAccountName: rabbitmq
terminationGracePeriodSeconds: 10
nodeSelector:
# Use Linux nodes in a mixed OS kubernetes cluster.
# Learn more at https://kubernetes.io/docs/reference/kubernetes-api/labels-annotations-taints/#kubernetes-io-os
kubernetes.io/os: linux
containers:
- name: rabbitmq-k8s
image: rabbitmq:3.8
volumeMounts:
- name: config-volume
mountPath: /etc/rabbitmq
# Learn more about what ports various protocols use
# at https://www.rabbitmq.com/networking.html#ports
ports:
- name: http
protocol: TCP
containerPort: 15672
- name: amqp
protocol: TCP
containerPort: 5672
livenessProbe:
exec:
# This is just an example. There is no "one true health check" but rather
# several rabbitmq-diagnostics commands that can be combined to form increasingly comprehensive
# and intrusive health checks.
# Learn more at https://www.rabbitmq.com/monitoring.html#health-checks.
#
# Stage 2 check:
command: ["rabbitmq-diagnostics", "status"]
initialDelaySeconds: 60
# See https://www.rabbitmq.com/monitoring.html for monitoring frequency recommendations.
periodSeconds: 60
timeoutSeconds: 15
readinessProbe:
exec:
# This is just an example. There is no "one true health check" but rather
# several rabbitmq-diagnostics commands that can be combined to form increasingly comprehensive
# and intrusive health checks.
# Learn more at https://www.rabbitmq.com/monitoring.html#health-checks.
#
# Stage 1 check:
command: ["rabbitmq-diagnostics", "ping"]
initialDelaySeconds: 20
periodSeconds: 60
timeoutSeconds: 10
imagePullPolicy: Always
env:
- name: MY_POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: MY_POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: RABBITMQ_USE_LONGNAME
value: "true"
# See a note on cluster_formation.k8s.address_type in the config file section
- name: K8S_SERVICE_NAME
value: rabbitmq
- name: RABBITMQ_NODENAME
value: rabbit@$(MY_POD_NAME).$(K8S_SERVICE_NAME).$(MY_POD_NAMESPACE).svc.cluster.local
- name: K8S_HOSTNAME_SUFFIX
value: .$(K8S_SERVICE_NAME).$(MY_POD_NAMESPACE).svc.cluster.local
- name: RABBITMQ_ERLANG_COOKIE
value: "mycookie"
volumes:
- name: config-volume
configMap:
name: rabbitmq-config
items:
- key: rabbitmq.conf
path: rabbitmq.conf
- key: enabled_plugins
path: enabled_plugins
================================================
FILE: k8s/minikube/bootstrap/zipkin/service.yml
================================================
apiVersion: v1
kind: Service
metadata:
name: zipkin
spec:
selector:
app: zipkin
ports:
- port: 9411
targetPort: 9411
protocol: TCP
type: LoadBalancer
================================================
FILE: k8s/minikube/bootstrap/zipkin/statefulset.yml
================================================
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zipkin
labels:
app: zipkin
spec:
serviceName: zipkin
replicas: 1
template:
metadata:
name: zipkin
labels:
app: zipkin
spec:
containers:
- name: zipkin
image: openzipkin/zipkin
imagePullPolicy: Always
ports:
- containerPort: 9411
protocol: TCP
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 256Mi
restartPolicy: Always
selector:
matchLabels:
app: zipkin
================================================
FILE: k8s/minikube/services/customer/deployment.yml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: customer
labels:
app: customer
spec:
replicas: 1
template:
metadata:
name: customer
labels:
app: customer
spec:
containers:
- name: customer
image: amigoscode/customer:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: kube
restartPolicy: Always
selector:
matchLabels:
app: customer
================================================
FILE: k8s/minikube/services/customer/service.yml
================================================
apiVersion: v1
kind: Service
metadata:
name: customer
spec:
selector:
app: customer
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
================================================
FILE: k8s/minikube/services/fraud/deployment.yml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: fraud
labels:
app: fraud
spec:
replicas: 1
template:
metadata:
name: fraud
labels:
app: fraud
spec:
containers:
- name: fraud
image: amigoscode/fraud:latest
imagePullPolicy: Always
ports:
- containerPort: 8081
env:
- name: SPRING_PROFILES_ACTIVE
value: kube
restartPolicy: Always
selector:
matchLabels:
app: fraud
================================================
FILE: k8s/minikube/services/fraud/service.yml
================================================
apiVersion: v1
kind: Service
metadata:
name: fraud
spec:
selector:
app: fraud
ports:
- port: 80
targetPort: 8081
type: NodePort
================================================
FILE: k8s/minikube/services/notification/deployment.yml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: notification
labels:
app: notification
spec:
replicas: 1
template:
metadata:
name: notification
labels:
app: notification
spec:
containers:
- name: notification
image: amigoscode/notification:latest
imagePullPolicy: Always
ports:
- containerPort: 8083
env:
- name: SPRING_PROFILES_ACTIVE
value: kube
restartPolicy: Always
selector:
matchLabels:
app: notification
================================================
FILE: k8s/minikube/services/notification/service.yml
================================================
apiVersion: v1
kind: Service
metadata:
name: notification
spec:
selector:
app: notification
ports:
- port: 80
targetPort: 8082
type: NodePort
================================================
FILE: notification/pom.xml
================================================
amigosservices
com.amigoscode
1.0-SNAPSHOT
4.0.0
jar
notification
org.springframework.boot
spring-boot-maven-plugin
build-docker-image
com.google.cloud.tools
jib-maven-plugin
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
org.postgresql
postgresql
runtime
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-sleuth-zipkin
org.springframework.boot
spring-boot-starter-amqp
com.amigoscode
amqp
1.0-SNAPSHOT
compile
com.amigoscode
clients
1.0-SNAPSHOT
compile
================================================
FILE: notification/src/main/java/com/amigoscode/notification/Notification.java
================================================
package com.amigoscode.notification;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Notification {
@Id
@SequenceGenerator(
name = "notification_id_sequence",
sequenceName = "notification_id_sequence"
)
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "notification_id_sequence"
)
private Integer notificationId;
private Integer toCustomerId;
private String toCustomerEmail;
private String sender;
private String message;
private LocalDateTime sentAt;
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/NotificationApplication.java
================================================
package com.amigoscode.notification;
import com.amigoscode.amqp.RabbitMQMessageProducer;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
@SpringBootApplication(
scanBasePackages = {
"com.amigoscode.notification",
"com.amigoscode.amqp",
}
)
@PropertySources({
@PropertySource("classpath:clients-${spring.profiles.active}.properties")
})
public class NotificationApplication {
public static void main(String[] args) {
SpringApplication.run(NotificationApplication.class, args);
}
// @Bean
// CommandLineRunner commandLineRunner(
// RabbitMQMessageProducer producer,
// NotificationConfig notificationConfig
// ) {
// return args -> {
// producer.publish(
// new Person("Ali", 18),
// notificationConfig.getInternalExchange(),
// notificationConfig.getInternalNotificationRoutingKey());
// };
// }
//
// record Person(String name, int age){}
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/NotificationConfig.java
================================================
package com.amigoscode.notification;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NotificationConfig {
@Value("${rabbitmq.exchanges.internal}")
private String internalExchange;
@Value("${rabbitmq.queues.notification}")
private String notificationQueue;
@Value("${rabbitmq.routing-keys.internal-notification}")
private String internalNotificationRoutingKey;
@Bean
public TopicExchange internalTopicExchange() {
return new TopicExchange(this.internalExchange);
}
@Bean
public Queue notificationQueue() {
return new Queue(this.notificationQueue);
}
@Bean
public Binding internalToNotificationBinding() {
return BindingBuilder
.bind(notificationQueue())
.to(internalTopicExchange())
.with(this.internalNotificationRoutingKey);
}
public String getInternalExchange() {
return internalExchange;
}
public String getNotificationQueue() {
return notificationQueue;
}
public String getInternalNotificationRoutingKey() {
return internalNotificationRoutingKey;
}
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/NotificationController.java
================================================
package com.amigoscode.notification;
import com.amigoscode.clients.notification.NotificationRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("api/v1/notification")
@AllArgsConstructor
@Slf4j
public class NotificationController {
private final NotificationService notificationService;
@PostMapping
public void sendNotification(@RequestBody NotificationRequest notificationRequest) {
log.info("New notification... {}", notificationRequest);
notificationService.send(notificationRequest);
}
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/NotificationRepository.java
================================================
package com.amigoscode.notification;
import org.springframework.data.jpa.repository.JpaRepository;
public interface NotificationRepository extends JpaRepository {
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/NotificationService.java
================================================
package com.amigoscode.notification;
import com.amigoscode.clients.notification.NotificationRequest;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
@AllArgsConstructor
public class NotificationService {
private final NotificationRepository notificationRepository;
public void send(NotificationRequest notificationRequest) {
notificationRepository.save(
Notification.builder()
.toCustomerId(notificationRequest.toCustomerId())
.toCustomerEmail(notificationRequest.toCustomerName())
.sender("Amigoscode")
.message(notificationRequest.message())
.sentAt(LocalDateTime.now())
.build()
);
}
}
================================================
FILE: notification/src/main/java/com/amigoscode/notification/rabbitmq/NotificationConsumer.java
================================================
package com.amigoscode.notification.rabbitmq;
import com.amigoscode.clients.notification.NotificationRequest;
import com.amigoscode.notification.NotificationService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@AllArgsConstructor
@Slf4j
public class NotificationConsumer {
private final NotificationService notificationService;
@RabbitListener(queues = "${rabbitmq.queues.notification}")
public void consumer(NotificationRequest notificationRequest) {
log.info("Consumed {} from queue", notificationRequest);
notificationService.send(notificationRequest);
}
}
================================================
FILE: notification/src/main/resources/application-docker.yml
================================================
server:
port: 8082
spring:
application:
name: notification
datasource:
password: password
url: jdbc:postgresql://postgres:5432/notification
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
rabbitmq:
addresses: rabbitmq:5672
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
rabbitmq:
exchanges:
internal: internal.exchange
queues:
notification: notification.queue
routing-keys:
internal-notification: internal.notification.routing-key
================================================
FILE: notification/src/main/resources/application-kube.yml
================================================
server:
port: 8082
spring:
application:
name: notification
datasource:
password: password
url: jdbc:postgresql://postgres:5432/notification
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://zipkin:9411
rabbitmq:
addresses: rabbitmq:5672
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: false
rabbitmq:
exchanges:
internal: internal.exchange
queues:
notification: notification.queue
routing-keys:
internal-notification: internal.notification.routing-key
================================================
FILE: notification/src/main/resources/application.yml
================================================
server:
port: 8082
spring:
application:
name: notification
datasource:
password: password
url: jdbc:postgresql://localhost:5432/notification
username: amigoscode
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
zipkin:
base-url: http://localhost:9411
rabbitmq:
addresses: localhost:5672
profiles:
active: default
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
fetch-registry: true
register-with-eureka: true
enabled: true
rabbitmq:
exchanges:
internal: internal.exchange
queues:
notification: notification.queue
routing-keys:
internal-notification: internal.notification.routing-key
================================================
FILE: notification/src/main/resources/banner.txt
================================================
,--. ,--. ,--. ,--. ,---. ,--. ,--. ,--.
| ,'.| | ,---. ,-' '-. `--' / .-' `--' ,---. ,--,--. ,-' '-. `--' ,---. ,--,--,
| |' ' | | .-. | '-. .-' ,--. | `-, ,--. | .--' ' ,-. | '-. .-' ,--. | .-. | | \
| | ` | ' '-' ' | | | | | .-' | | \ `--. \ '-' | | | | | ' '-' ' | || |
`--' `--' `---' `--' `--' `--' `--' `---' `--`--' `--' `--' `---' `--''--'
${application.title} ${application.version}
Powered by Spring Boot ${spring-boot.version}
================================================
FILE: pom.xml
================================================
4.0.0
com.amigoscode
amigosservices
pom
1.0-SNAPSHOT
customer
fraud
eureka-server
clients
notification
apigw
amqp
amigosservices
https://www.amigoscode.com
UTF-8
17
17
2.5.7
2.5.7
2020.0.3
amigoscode/${project.artifactId}:${project.version}
org.springframework.boot
spring-boot-dependencies
${spring.boot.dependencies.version}
import
pom
org.springframework.cloud
spring-cloud-dependencies
${spring.cloud-version}
pom
import
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-maven-plugin
${spring.boot.maven.plugin.version}
repackage
com.google.cloud.tools
jib-maven-plugin
3.1.4
eclipse-temurin:17@sha256:2b47a8ea946ce1e5365a1562414ed576e378b7b670cadff3fb98ebecf2890cdc
arm64
linux
amd64
linux
latest
package
build
org.apache.maven.plugins
maven-compiler-plugin
3.8.0
17
17