0% found this document useful (0 votes)
127 views29 pages

1-Spring Boot MS Bank App Step by Setp Jan 25

The document provides a comprehensive guide on microservices architecture, highlighting its advantages such as easier maintenance, scalability, and fault isolation, while also addressing challenges like consistency and debugging. It details the implementation of a bank application using Spring Cloud, including the setup of microservices for cards, loans, and accounts, along with their respective configurations and endpoints. Additionally, it covers the creation of a config server and Eureka server for service discovery and configuration management.

Uploaded by

suresh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
127 views29 pages

1-Spring Boot MS Bank App Step by Setp Jan 25

The document provides a comprehensive guide on microservices architecture, highlighting its advantages such as easier maintenance, scalability, and fault isolation, while also addressing challenges like consistency and debugging. It details the implementation of a bank application using Spring Cloud, including the setup of microservices for cards, loans, and accounts, along with their respective configurations and endpoints. Additionally, it covers the creation of a config server and Eureka server for service discovery and configuration management.

Uploaded by

suresh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 29

Microservice cheetsheet step by step:

--------------------------------------
Microservices Architecture?

=> Microservices architecture allows to avoid monolith application for


large system.

=> It provide loose coupling between collaborating processes which running


independently in different environments with tight cohesion.

Adv MS? :)
=> Smaller code base is easy to maintain.
=> Easy to scale as individual component.
=> Technology diversity i.e. we can mix libraries, databases, frameworks etc.
=> Fault isolation i.e. a process failure should not bring whole system down.
=> Better support for smaller and parallel team.
=> Independent deployment
=> Deployment time reduce

Microservices Challenges :(

=> Difficult to achieve strong consistency across services


=> ACID transactions do not span multiple processes.
=> Distributed System so hard to debug and trace the issues
=> Greater need for end to end testing
=> Required cultural changes in across teams like Dev and Ops

What is Spring Cloud?


=> building blocks for Cloud and Microservices
=> provides microservices infrastructure like provide use services
such as Service Discovery, Configuration server and Monitoring.
=> provides several other open source projects like Netflix OSS.
=> provides PaaS like Cloud Foundry, AWS and Heroku.
=> uses Spring Boot style starters

bank app
card
loan
account

Port Numbers for microservice:


------------------------------
card :9090
loan :8090
account : 8080
configserver: 8071
Eureka server: 8070
api gateway: 8072
Zipkin: 9411

Card:
http://localhost:9090/api/contact-info
http://localhost:9090/api/fetch?mobile=7088993300

loans:
http://localhost:8090/api/fetch?mobile=7088993300
http://localhost:8090/api/contact-info

account:
http://localhost:8080/api/fetch?mobile=7088993300
http://localhost:8080/api/contact-info
http://localhost:8080/api/accountsdetails?mobile=7088993300

http://localhost:8070/

spring boot microservice projectstep by step:


-------------------------------------------
web, actuator, h2, jpa, lombok
cards: 9090
loans: 8090
accounts: 8080

Card
private int cardId;
private String cardNumber;
private LocalDate issueDate;
private int totalLimit;
private String mobile;

Loan
private int loanId;
private String mobile;
private String loanNumber;
private String loanType;
private int totalLoan;
private int amountPaid;
private int outstandingAmount;

Account
private int accId;
private String name;
private double balance;
private String email;
private String mobile;

@Override
public void run(String... args) throws Exception {
//Card(String cardNumber, LocalDate issueDate, int totalLimit, String
mobile)
cardRepo.save(new Card(getCardNumber(), LocalDate.now(),
10000,"7788993300"));
cardRepo.save(new Card(getCardNumber(), LocalDate.now(),
20000,"7988223300"));
}
private String getCardNumber(){
long val=new Random().nextLong(1000_0000_0000_000L);
Long value=1000_0000_0000_0000L+val;
return value.toString();
}

@RequestParam @Pattern(regexp="(^$|[0-9]{10})",message = "Mobile number must be 10


digits") String mobileNumber

Step 1: creating card microservice:


---------------------------------
Choose : web,lombok, h2, spring data, actuator

repo layer:
-------------

@Data
@NoArgsConstructor
@Entity
@Table(name = "card")
public class Card {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int cardId;
private String cardNumber;
private LocalDate issueDate;
private int totalLimit;
private String mobile;

public Card(String cardNumber, LocalDate issueDate, int totalLimit, String


mobile) {
this.cardNumber = cardNumber;
this.issueDate = issueDate;
this.totalLimit = totalLimit;
this.mobile = mobile;
}
}

@Repository
public interface CardRepo extends JpaRepository<Card,Integer> {
Card findByMobile(String mobile);
}

create CardDto
-------------
@Data
@NoArgsConstructor
public class CardDto {
//....
}

create DtoConverter
---------------------
public class DtoConverter {
public static CardDto entityToDto(Card card){
//
}
public static Card dtoToEntity(CardDto cardDto){
//
}
}

service layer:
-------------
public interface CardService {
public CardDto findByMobileNumber(String mobile);
}

@Service
@Transactional
public class CardServiceImpl implements CardService{
@Autowired
private CardRepo cardRepo;
@Override
public CardDto findByMobileNumber(String mobile) {
Card card= cardRepo.findByMobile(mobile);
return DtoConverter.entityToDto(card);
}
}

Reading property file using @ConfigurationProperties annotation


-------------------------------------------------------------

@Data
@ConfigurationProperties(prefix = "info")
public class InfoDto {
private String message;
private String name;
}

@EnableConfigurationProperties(InfoDto.class)

info:
message: "Welcome to busycoder card default profile"
name: "Raj: Product Owner card default profile"

controller layer:
-------------
@RequestMapping(path = "api")
@RestController
public class CardController {

@Autowired
private CardService cardService;
@Autowired
private InfoDto infoDto;

@GetMapping(path = "fetch")
public CardDto findByMobileNumber(@RequestParam(name="mobile") String mobile){
return cardService.findByMobileNumber(mobile);
}
@GetMapping("contact-info")
public InfoDto getInfoDto(){
return infoDto;
}
}

logic to insert 2 cards:


------------------------
@SpringBootApplication
public class CardsApplication implements CommandLineRunner {

@Autowired
private CardRepo cardRepo;

public static void main(String[] args) {


SpringApplication.run(CardsApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
//Card(String cardNumber, LocalDate issueDate, int totalLimit, String
mobile)
cardRepo.save(new Card(getCardNumber(), LocalDate.now(),
10000,"7788993300"));
cardRepo.save(new Card(getCardNumber(), LocalDate.now(),
20000,"7988223300"));
}
private String getCardNumber(){
long val=new Random().nextLong(1000_0000_0000_000L);
Long value=1000_0000_0000_0000L+val;
return value.toString();
}
}

application.yaml
-------------------
server:
port: 9090

spring:
profiles:
active:
- "qa"
jpa:
show-sql: true
application:
name: cards
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
info:
env:
enabled: true
build:
version: "1.0"
info:
message: "Welcome to busycoder card default profile"
name: "Raj: Product Owner card default profile"

application-prod.yml
---------------------
build:
version: "2.0"
info:
message: "Welcome to busycoder card dev profile"
name: "Ravi: Product Owner card dev profile"

application-qa.yml
---------------------
build:
version: "2.0"
info:
message: "Welcome to busycoder card qa profile"
name: "Tarun: Product Owner card qa profile"

Step 2: creating loan microservice:


---------------------------------
Choose : web,lombok, h2, spring data, actuator

creating repo layer


------------------------

@Data
@NoArgsConstructor
@Entity
@Table(name = "loan_table")
public class Loan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int loanId;
private String mobile;
private String loanNumber;
private String loanType;
private int totalLoan;
private int amountPaid;
private int outstandingAmount;
}

public interface LoanRepo extends JpaRepository<Loan, Integer> {


public Loan findByMobile(String mobile);
}

create LoanDto and converter:


-----------------------------
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoanDto {
//
}

public class DtoConverter {


public static Loan dtoToEntity(LoanDto loanDto){
//
}

public static LoanDto entityToDto(Loan loan){


//
}
}

creating server layer


-------------------------

public interface LoanService {


public LoanDto findByMobile(String mobile);
}

@Service
public class LoanServiceImpl implements LoanService{
@Autowired
private LoanRepo loanRepo;
@Override
public LoanDto findByMobile(String mobile) {
return DtoConverter.entityToDto(loanRepo.findByMobile(mobile));
}
}

creating controller layer


-------------------------
@RequestMapping(path = "api")
@RestController
@AllArgsConstructor
public class LoanController {
private final LoanService loanService;
private final InfoDto infoDto;

@GetMapping("fetch")
public LoanDto getByMobile( @RequestParam(name="mobile") String mobile){
return loanService.findByMobile(mobile);
}
@GetMapping("contact-info")
public InfoDto getInfoDto(){
return infoDto;
}
}

creating bootstrap class


---------------------------
@SpringBootApplication
public class LoansApplication implements CommandLineRunner {

@Autowired
private LoanRepo loanRepo;

public static void main(String[] args) {


SpringApplication.run(LoansApplication.class, args);
}

@Override
public void run(String... args) throws Exception {

//public Loan(String mobile, String loanNumber, String loanType, int


totalLoan, int amountPaid, int outstandingAmount)
loanRepo.save(new Loan("7788993300","ASBAS11","house",
2000,1000,1000));

loanRepo.save(new Loan("7788223300","AAMS11","education",
2000,1000,1000));

}
}

application.yml
-----------------
server:
port: 8090
spring:
profiles:
active:
- qa
jpa:
show-sql: true
application:
name: loans
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
info:
env:
enabled: true
build:
version: "1.0"
info:
message: "Welcome to busycoder loans default profile"
name: "Raj: Product Owner loans default profile"
application-prod.yml
--------------------

build:
version: "2.0"
info:
message: "Welcome to busycoder loans dev profile"
name: "Ravi: Product Owner loans dev profile"

application-qa.yml
-----------
build:
version: "2.0"
info:
message: "Welcome to busycoder loans qa profile"
name: "Tarun: Product Owner loans qa profile"

Step 3: creating accounts microservice


------------------------------------
Choose : web,lombok, h2, spring data, actuator

dto: copy other dto from loan and card microservice


------
@Data
public class AccountInfoDto {
private AccountDto accountDto;
private CardDto cardDto;
private LoanDto loanDto;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "info")
public class AppInfoDto {
private String message;
private String name;
}

creating entities
-----------------
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "account_table")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int accId;
private String name;
private double balance;
private String email;
private String mobile;
}

@Repository
public interface AccountRepo extends JpaRepository<Account, Integer> {
public Account findByMobile(String mobile);
}

Create AccountDto and converter:


-------------------------------
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AccountDto {
//
}

public class DtoConvertor {

public static AccountDto entityToDto(Account account){


//
}

public static Account dtoToEntity(AccountDto accountDto){


//
}
}

creating service layer


----------------------
public interface AccountService {
public List<AccountDto> getAll();
public AccountDto getByMobile(String mobile);
public AccountInfoDto getAccountDetails(String mobile);
public String addAccount(AccountDto accountDto);
}

@Service
@AllArgsConstructor
public class AccountServiceImpl implements AccountService{

private final AccountRepo accountRepo;

@Override
public List<AccountDto> getAll() {
return accountRepo.findAll().stream()
.map(DtoConvertor::entityToDto)
.collect(Collectors.toList());
}
@Override
public AccountDto getByMobile(String mobile) {
return DtoConvertor.entityToDto(accountRepo.findByMobile(mobile));
}

@Override
public AccountInfoDto getAccountDetails(String mobile) {
//somehow we should able to call the
// loans and cards ms and get the related inforation
//http://localhost:8090/loans?mobile=7088993300
// http://localhost:9090/cards?mobile=7088993300
AccountInfoDto accountInfoDto=new AccountInfoDto();
accountInfoDto.setAccountDto(getByMobile(mobile));

return accountInfoDto;
}

@Override
public String addAccount(AccountDto accountDto) {
Account account=DtoConvertor.dtoToEntity(accountDto);
accountRepo.save(account);
accountDto.setAccId(account.getAccId());
return "account is added successfully";
}
}

creating proxy service to invoke loan and card microservice


----------------------------------------------------------
apply on bootstrap class
@EnableFeignClients("com.accounts.proxyservice")

@FeignClient(name = "card-service", url = "http://localhost:9090")


public interface CardServiceProxy {
@GetMapping(path = "api/fetch")
public CardDto findByMobileNumber(@RequestParam(name="mobile") String mobile);
}

@FeignClient(name = "loan-service", url = "http://localhost:8090")


public interface LoanServiceProxy {
@GetMapping(path = "api/fetch")
public LoanDto getByMobile(@RequestParam(name="mobile") String mobile);
}

creating controller layer


----------------------

@RequestMapping(path = "api")
@RestController
@AllArgsConstructor
public class AccountController {
private final AccountService accountService;
private final InfoDto appInfoDto;
@GetMapping("contact-info")
public InfoDto appInfo(){
return appInfoDto;
}

@GetMapping("fetchall")
public List<AccountDto> getAll(){
return accountService.getAll();
}

@GetMapping("fetch")
public AccountDto getByMobile(@RequestParam(name="mobile") String mobile){
return accountService.getByMobile(mobile);
}

@GetMapping("accountsdetails")
public AccountInfoDto getAccountDetails(@RequestParam(name = "mobile") String
mobile){
return accountService.getAccountDetails(mobile);
}
@PostMapping(path = "add")
public String addAccount(@RequestBody AccountDto accountDto){
return accountService.addAccount(accountDto);
}
}

bootstrap class
-----------------
@EnableConfigurationProperties(AppInfoDto.class)
@SpringBootApplication
public class AccountsApplication implements CommandLineRunner {

@Autowired
private AccountService accountService;

public static void main(String[] args) {


SpringApplication.run(AccountsApplication.class, args);
}

@Override
public void run(String... args) throws Exception {

accountService.addAccount(new Account("raj",1000,"raj@gmail.com",
"7788993300"));

accountService.addAccount(new Account("ekta",1000,"ekta@gmail.com",
"7788223300"));

}
}

step 4: create config server:


--------------------------
Choose : lombok,config server,actuator

apply
-----
@EnableConfigServer to the bootstrap class

application.yml
----------------
server:
port: 8071
spring:
application:
name: configserver
cloud:
config:
server:
git:
uri: file:///C:/configfiles
clone-on-start: true
default-label: master

configfiles-main

now try:
---------
http://localhost:8071/accounts/default
http://localhost:8071/loans/default
http://localhost:8071/cards/default

Step 5: read property files from config server


-----------------------------------------------
1. change in every project add: config client dependency

2. now add configuration


spring:
config:
import: optional:configserver:http://localhost:8071

http://localhost:8080/api/contact-info

3. what if config property changes?

@RefreshScope on top of the restcontroller

in client application use refresh endpoint

http://localhost:8080/actuator/refresh

Step 6: Configure eureka server:


-------------------------------
create new project with: eureka server, config client, actuator

1. apply annotation on bootstrap class


@EnableEurekaServer

2. url pattern
http://localhost:8070/

3. application.yml configuration for eureka server


--------------------------
server:
port: 8070
eureka:
instance:
hostname: localhost
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

spring:
application:
name: "eurekaserver"
config:
import: "optional:configserver:http://localhost:8071/"
management:
endpoints:
web:
exposure:
include: "*"
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
endpoint:
health:
probes:
enabled: true

4. configure eureka client in all the projects accounts, cards and loans
--------------------------------------------------------------------------
add eureka client dep to all projects

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8070/eureka/

5. now check all service must be registed with eureka server


6. now replace hard coded url in Openfeign service to logical names and run the
examples
give logical name of service
@FeignClient("loans")

step 7.gateway routing and cross cutting concern in


microservicve using "spring cloud gateway"
-----------------------------------------------
step 1:

choose eureka client, config server client, actuator, api gateway


<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

property file configuration:


--------------------------
step 2:
management:
endpoint:
gateway:
enabled: true

spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true

Java configuration give more flexiblity to define routes:


---------------------------------------------------

@Bean
public RouteLocator busycoderRouteConfig(RouteLocatorBuilder
routeLocatorBuilder) {
return routeLocatorBuilder.routes()
.route(p -> p
.path("/busycoder/accounts/**")
.filters( f -> f.rewritePath("/busycoder/accounts/(?
<segment>.*)","/${segment}")
.addResponseHeader("X-Response-Time",
LocalDateTime.now().toString()))
.uri("lb://ACCOUNTS"))
.route(p -> p
.path("/busycoder/loans/**")
.filters( f -> f.rewritePath("/busycoder/loans/(?
<segment>.*)","/${segment}")
.addResponseHeader("X-Response-Time",
LocalDateTime.now().toString()))
.uri("lb://LOANS"))
.route(p -> p
.path("/busycoder/cards/**")
.filters( f -> f.rewritePath("/busycoder/cards/(?
<segment>.*)","/${segment}")
.addResponseHeader("X-Response-Time",
LocalDateTime.now().toString()))
.uri("lb://CARDS")).build();
}

Configuring global filter:


---------------------------

@Component
public class LoggingFilter implements GlobalFilter {
private Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
logger.info("Path of the request received -> {}",
exchange.getRequest().getPath());
return chain.filter(exchange);
}

configuration.yml
--------------------
server:
port: 8072
spring:
config:
import: optional:configserver:http://localhost:8071
application:
name: gatewayserver

eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8070/eureka/

management:
endpoints:
web:
exposure:
include: "*"
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
endpoint:
gateway:
enabled: true
health:
probes:
enabled: true
step 7.Configure resilence 4j to bank application
-----------------------------------------------
We can apply circuitbreaker pattern to api gateway
and to indidual microservice

Implementing circuitBreaker pattern in the account microservice:


----------------------------------------------------------------
integration circuitBreaker with feign client

step 1: add dep to account ms

<dependency>
<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

step 2:
spring:
cloud:
openfeign:
circuitbreaker:
enabled: true

resilience4j:
circuitbreaker:
configs:
default:
sliding-window-size: 10
permitted-number-of-calls-in-half-open-state: 2
failure-rate-threshold: 50 #percentage
wait-duration-in-open-state: 10s

now we have to define fallback for feign client


--------------------------------------------
@FeignClient(name = "CARDS", fallback = CardFallBack.class)
public interface CardServiceProxy {
@GetMapping(path = "api/fetch")
public CardDto findByMobileNumber(@RequestParam(name="mobile") String mobile);
}

@Component
public class CardFallBack implements CardServiceProxy{
@Override
public CardDto findByMobileNumber(String mobile) {
return new CardDto();
}
}
@Component
public class LoanFallBack implements LoanServiceProxy{
@Override
public LoanDto getByMobile(String mobile) {
return new LoanDto();
}
}

@FeignClient(name = "LOANS", fallback = LoanFallBack.class)


public interface LoanServiceProxy {
@GetMapping(path = "api/fetch")
public LoanDto getByMobile(@RequestParam(name="mobile") String mobile);
}

Now observe the circuitBreaker urls

http://localhost:8080/actuator
http://localhost:8080/actuator/circuitbreakerevents

Now stop loan ms and observe the behaviour of the application

Applying circuitbreaker to api gateway


----------------------------------
step 1: add depdendency to the app
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-
resilience4j</artifactId>
</dependency>

setp 2: config
resilience4j:
circuitbreaker:
configs:
default:
sliding-window-size: 10
permitted-number-of-calls-in-half-open-state: 2
failure-rate-threshold: 50 #percentage
wait-duration-in-open-state: 10s

step 3:
.route(p -> p
.path("/busycoder/accounts/**")
.filters( f -> f.rewritePath("/busycoder/accounts/(?<segment>.*)","/${segment}")
.addResponseHeader("X-Response-Time", LocalDateTime.now().toString())
.circuitBreaker(config -> config.setName("accountCircuitBreaker")
.setFallbackUri("forward:/contactSupport")))
.uri("lb://ACCOUNTS"))

Observe circuitBreaker design pattern:

http://localhost:8072/actuator/circuitbreakers
http://localhost:8072/actuator/circuitbreakerevents?name=accountCircuitBreaker

now put a breakpoint to the contact-info endpoint of account service


504 status code
TimeoutException: Did not observe any item or terminal signal within 1000ms in
'circuitBreaker'

run many time "status": 503,"error": "Service Unavailable",

Step 4: creating fallback controller:


@RestController
public class FallbackController {
@RequestMapping("/contactSupport")
public Mono<String> contactSupport() {
return Mono.just("An error occurred. Please try after some time or contact
support team!!!");
}
}

Http timeout configuration:


------------------------------
sometime service is very slow and we will not get immediate response
ex: run contact-info endpoint with breakpoint
it keep waiting for the response, a thread is block for the response

how to overcome? we can define timeout configuration:

if we try same with api gateway we get different response due to

.circuitBreaker(config -> config.setName("accountCircuitBreaker")


.setFallbackUri("forward:/contactSupport")))
with default timeout configuration of 1sec

how how to customized it? add configuration to the gateway service

spring:
cloud:
gateway:
discovery:
locator:
enabled: false
lower-case-service-id: true
httpclient:
connect-timeout: 1000
response-timeout: 2s

retry pattern to the pattern to api gateway:


----------------------------------------

.route(p -> p
.path("/busycoder/loans/**")
.filters( f -> f.rewritePath("/busycoder/loans/(?<segment>.*)","/${segment}")
.addResponseHeader("X-Response-Time", LocalDateTime.now().toString())
.retry(retryConfig -> retryConfig.setRetries(3)
.setMethods(HttpMethod.GET)
.setBackoff(Duration.ofMillis(100),Duration
.ofMillis(1000),2,true)))
.uri("lb://LOANS"))

@RestController
public class CircuitBreakerController {
private Logger logger =
LoggerFactory.getLogger(CircuitBreakerController.class);

@GetMapping("/sample-api")
//@Retry(name = "sample-api", fallbackMethod = "hardcodedResponse")
//@CircuitBreaker(name = "default", fallbackMethod = "hardcodedResponse")
//@RateLimiter(name="default")
@Bulkhead(name="sample-api")
//10s => 10000 calls to the sample api
public String sampleApi() {
logger.info("Sample api call received");
// ResponseEntity<String> forEntity = new
RestTemplate().getForEntity("http://localhost:8080/some-dummy-url",
// String.class);
// return forEntity.getBody();
return "sample-api";
}

public String hardcodedResponse(Exception ex) {


return "fallback-response";
}
}

resilience4j.retry.instances.sample-api.maxRetryAttempts=5
resilience4j.retry.instances.sample-api.waitDuration=1s
resilience4j.retry.instances.sample-api.enableExponentialBackoff=true

#resilience4j.circuitbreaker.instances.default.failureRateThreshold=90
resilience4j.ratelimiter.instances.default.limitForPeriod=2
resilience4j.ratelimiter.instances.default.limitRefreshPeriod=10s

resilience4j.bulkhead.instances.default.maxConcurrentCalls=10
resilience4j.bulkhead.instances.sample-api.maxConcurrentCalls=10

Spring boot zipkine and sluth:


--------------------------------
What is the need to distributed log tracing?

complex call chains


how you debug the problem?
how you trace request accorss microservice?
Distributed log tracking is require

Observability and OpenTelemetry:


------------------------------
Monitoring vs Observability?
monitoring is reactive while Observability is proactive
monitoring is subset of Observability

Observability?
how well do we understand what is happing in the system?
Step 1: gather data : materix logs or traces
step 2: get intelligence : AI/Ops and anomaly detection

OpenTelemetry: collection of tools, api and sdk to instruments, generate, collect


and export telemetry data (materix logs or traces)

all applications have materix logs or traces


why do we need to have a separate standared for each one of these
opentelemetry: how about one standared for materix logs or traces

Step 1:
docker pull openzipkin/zipkin:2.23
docker run -p 9411:9411 openzipkin/zipkin:2.23

Step 2: add dependency to each projects


---------------------------------------
<!-- SB3 : Micrometer
> OpenTelemetry
> Zipkin
-->

<!-- Micrometer - Vendor-neutral application observability facade.


Instrument your JVM-based application code without vendor lock-in.
Observation (Metrics & Logs) + Tracing.
-->

<!--Open Telemetry as Bridge (RECOMMENDED) -->


<!-- Open Telemetry - Simplified Observability (metrics, logs, and traces) -->

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>

<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
</dependency>

Step 3: add tracing sampling to each project:


---------------------------------------
management:
tracing:
sampling:
probability: 1.0

logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"

for feign we need to add addtional depdendency:


------------------------------------
<!-- Enables tracing of REST API calls made using Feign - V3 ONLY-->

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
</dependency>

management.tracing.sampling.probability=1.0
logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]

spring.config.import=optional:configserver:

##spring.zipkin.baseUrl=http://localhost:9411/
##management.zipkin.tracing.endpoint=http://localhost:9411/api/v2/spans

8. Spring boot grafana and prometheus


----------------------------------------

Prometheus: Hello world configuration:


__________________________________

Step 1: create spring boot application with actuator, and prometheus dep
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

server:
port: 8080
management:
endpoints:
web:
base-path: /actuator
exposure:
include: "*"
endpoint:
prometheus:
enabled: true
metrics:
enabled: true

step 2: download sw
Download and configure Prometheus: run on port : 9090
https://prometheus.io/download/

download grafana:
wget https://dl.grafana.com/enterprise/release/grafana-enterprise-9.5.2.linux-
amd64.tar.gz
https://grafana.com/grafana/download/9.5.2?platform=windows

Step 3: configure spring boot application monitoring to Prometheus:


search google (configure prometheus to monitor itself)

prometheus.yml
-----------------
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.

# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'codelab-monitor'

# A scrape configuration containing exactly one endpoint to scrape:


# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped
from this config.
- job_name: 'prometheus'

# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s

static_configs:
- targets: ['localhost:9090']

- job_name: 'spring-actuator'
metrics_path: '/actuator/prometheus'
scrape_interval: 5s
static_configs:
- targets: ['localhost:8080']

Start prometheus
./prometheus

4.start grafana:
bin/grafana-server
http://localhost:9090
up

grafana dashboard
http://localhost:3000/

Dashboard-> new import -> grafana dashboard id -->put that id---> ui is created

search google SpringBoot APM Dashboard


Microservice security:
-----------------------
spring sec:
basics auth
jwt auth

OAuth2? is mainly used for delegated authorization?


spring sec of ms

OAuth2 ( jpa -> hibeante, eclipselink)


------------------------------
Protocol that allow a user to grant limited access to there
resouces on one site to the another side
without exposing there credential

Open ID connect
----------------
OAuth2 was designed for authorization
Open ID connect is build on top of Oauth2

Open ID connect: id token to the request


OAuth2
http

what type of request flows is supported by Oauth2?


--------------------------------------------
1. Authroization code grant (front end + back end)
2. client credential grant (backend application )
service -----------------service2

3. Resouce owner passaward grant type (back application)


4. Implicit code grant (js application)

OAuth keyclock integration:


---------------------------
https://www.keycloak.org/getting-started/getting-started-docker

Step 1: configure keycloak server using docker

https://www.keycloak.org/

docker run -d -p 7080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin


quay.io/keycloak/keycloak:25.0.1 start-dev

step 2: register client application with keycloak server:


---------------------------------------------------
client -> create client -> openid connect

provide:
cc: client credential

client id: busycoder-cc

client name: busycoder-cc-app


enable client authentication--->auth flow --> service accounts roles (other dont
select)
two application try to communicate each other

copy client secret:

client secret: ptgcrpzwMshj7lne8y5Oz6jmEB3usVSO

step 3: getting access token form auth server in client credential grant flow:
------------------------------------------------------------------------------
go to relem setting-->open endpoint section
http://localhost:7080/realms/master/.well-known/openid-configuration

select and create new post request to following url to get token:

http://localhost:7080/realms/master/protocol/openid-connect/token

grant_type: client_credentials
client_id: busycoder-cc
client_secret:
scope: openid email profile

understand token formate

step 4: Securing gateway server as resource server:

add following dependencies:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

step 5: add configuration to customized security

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity
serverHttpSecurity) {
serverHttpSecurity.authorizeExchange(exchanges ->
exchanges.pathMatchers(HttpMethod.GET).authenticated()
.pathMatchers("/busycoder/accounts/**").authenticated()
.pathMatchers("/busycoder/cards/**").authenticated()
.pathMatchers("/busycoder/loans/**").authenticated())
.oauth2ResourceServer(oAuth2ResourceServerSpec ->
oAuth2ResourceServerSpec
.jwt(Customizer.withDefaults()));
serverHttpSecurity.csrf(csrfSpec -> csrfSpec.disable());
return serverHttpSecurity.build();
}
}

step 6: resourceserver will fetch certificates from auth server


resourceserver will validate token using this certificates

spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: "http://localhost:7080/realms/master/protocol/openid-
connect/certs"

step 7: now try calling api

Select auth type: oauth2


add authorization data to : request headers

configure new token:


token : clientcredentials _ccesstoken
grant type: client credential
Auth access url: http://localhost:7080/realms/master/protocol/openid-connect/token
client id: busycoder-cc
Client Secret: Gs4tq3qUpT7S41qf9B3NXnvRsnhDb3BI
Scope: openid email profile
Client Authentication: send client credential in body

use token type: access token

step 8: implementation authorization inside gateway server using roles


-----------------------------------------------------------------------

Step 1: go to relm roles--> create new roles


ACCOUNTS, CARDS, LOANS

Step 2: go to client -> busycoder-cc -->service account role--> assign role


ACCOUNTS

Step 3: get fresh access token and verify new role jwt.io now you can see new role
under realm_access

Step 4: Now we need to extract role in our java code


------------

public class KeycloakRoleConverter implements Converter<Jwt,


Collection<GrantedAuthority>> {
@Override
public Collection<GrantedAuthority> convert(Jwt source) {
Map<String, Object> realmAccess = (Map<String, Object>)
source.getClaims().get("realm_access");
if (realmAccess == null || realmAccess.isEmpty()) {
return new ArrayList<>();
}
Collection<GrantedAuthority> returnValue = ((List<String>)
realmAccess.get("roles"))
.stream().map(roleName -> "ROLE_" + roleName)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return returnValue;
}
}

Step 5: use roles:


-------------
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity
serverHttpSecurity) {
serverHttpSecurity.authorizeExchange(exchanges ->
exchanges.pathMatchers(HttpMethod.POST).authenticated()
.pathMatchers("/busycoder/accounts/**").hasRole("ACCOUNTS")
.pathMatchers("/busycoder/cards/**").hasRole("CARDS")
.pathMatchers("/busycoder/loans/**").hasRole("LOANS"))
.oauth2ResourceServer(oAuth2ResourceServerSpec ->
oAuth2ResourceServerSpec.jwt(jwtSpec ->

jwtSpec.jwtAuthenticationConverter(grantedAuthoritiesExtractor())));
serverHttpSecurity.csrf(csrfSpec -> csrfSpec.disable());
return serverHttpSecurity.build();
}

private Converter<Jwt, Mono<AbstractAuthenticationToken>>


grantedAuthoritiesExtractor() {
JwtAuthenticationConverter jwtAuthenticationConverter =
new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter
(new KeycloakRoleConverter());
return new
ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
}

Step 6: now we can access account resource but for others we get 403 error

Authorization code grant type flow:


---------------------------------------

client -> create client -> openid connect

provide:
cc: client credential
client id: busycoder-ac

client name: busycoder-cc-app2

enable client authentication--->auth flow -->standard flow (other dont select)


two application try to communicate each other

copy client secret:

client secret: VA7R6c0IiwXySnMjBkvptGIdDRIx1IbM

Access settings
Root URL blank
Home URL blank
Valid redirect URIs *
Valid post logout redirect URIs blank
Web origins *
Admin URL blank
7. ELK
=========
Step 1: download tools
---------------------------
https://www.elastic.co/downloads/past-releases/elasticsearch-6-5-1
https://www.elastic.co/downloads/past-releases/kibana-6-5-1
https://www.elastic.co/downloads/past-releases/logstash-6-5-1

Step 2:

Start elasticsearch(9200)
-------------------
./elasticsearch port No: localhost:9200

start kibana(5601)
--------------
Uncomment the file kibana.yml to point to the elasticsearch instance.
elasticsearch url: http://localhost:9200

./bin/kibana

logstash
-------------
Create a configuration file named logstash.conf
bin/logstash -f bin/logstash.conf

http://localhost:9200/_cat/indices/?v
http://localhost:9200/logstash-2022.08.02/_search

logstash-*

You might also like