Microservices Notes
Microservices Notes
Advantages
Development is easy
2) Deployment is easy
Performance
4) Easy Testing
5) Easy Debugging
6) KT is easy
Disadvantages
Single point of failure
2) Whole Application Re-Deployment
3) Scalability (Increasing & Decreasing resources based on demand)
4) Reliability (Strong)
5) Availability (Zero Downtime)
If we develop the functionalities in multiple services/apis then it is called Microservices Architecture.
Based Application.
Every Microservice will have its own goal
Advantages
Loosely coupled
Fast Development
Quick Releases
4) Flexibility
5) Scalability
6) No Single Point of failure
Technology independence
Challenges
1) Bounded context (identifying the number of services to develop)
A lot of configurations
3) Visibility
Testing is difficult
5) Debugging
Microservices Architecture
Microservices is an architectural design pattern to develop our applications.
There is no fixed architecture for Microservices Based Applications
People are customizing Microservices Architecture according to their requirement.
http://localhost:8761/
Step-2 ) Create Spring Boot Application with Admin Server
1) Create Boot application with the following dependencies
a) web-starter
b) devtools
c) admin-server (code centric)
2) Configure the @EnableAdminServer annotation in the boot start class
3) Configure Embedded Container Port Number (we can use any port)
4) Execute the application and access it in the browser.
http://localhost:port/
Step-3) Download & Run Zipkin Server
1) Download the zipkin jar from the below URL
https://search.maven.org/remote_content?g=io.zipkin&a=zipkin-server&v=LATEST&c=exec
2) Run the zipkin server jar file using the command below
eureka-discovery-client
b) admin-client
c) zipkin client
d) sleuth (It is for logging)
web-starter
f) devtools
g) actuator
2) Configure the @EnableDiscoveryClient annotation in the startup class (It will search and register with Eureka)
eureka-discovery-client
(2) admin-client
(3) zipkin client
(4) sleuth (It is for logging)
(5) web-starter
devtools
actuator
feign-client
2) Configure @EnableDiscoveryClient & @EnableFeignClients annotations at the start class
3) Create FeginClient to access WELCOME-API
@FeignClient(name = "WELCOME-API")
public interface WelcomeApiClient {
/welcome
public String invokeWelcomeApi();
}
4) Create Rest Controller with required methods
5) Configure below properties in application.yml
server port
admin server url
actuator endpoints
application name
server
9091
spring
application
GREET-API
boot:
admin
client
http://localhost:1111/
Eureka:
client
service-url
http://localhost:8761/eureka/
management
endpoints
web
exposure
*
6) Run the application and check the Eureka Dashboard, Admin Server Dashboard, and Zipkin Dashboard
Step-6 :: Develop API-Gateway Application
1) Create boot application with the below dependencies
cloud-gateway
eureka-client
web-starter
devtools
Configure @EnableDiscoveryClient annotation at the boot start class
3) Configure Server Port & API Routings in application.yml file
server
port: 3333
spring
application
API-GATEWAY
cloud
gateway
discovery:
locator
enabled: true
true
routes
one
uri: lb://WELCOME-API
predicates:
/welcome
two
lb://GREET-API
predicates
/greet
4) Run the application and test it.
We can access Client sent request information using Filter
The client request information we can use to validate that request
Create the filter below in API Gateway (It will execute for every request)
@Component
public class MyPreFilter implements GlobalFilter {
Logger logger = LoggerFactory.getLogger(MyPreFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
filter() method executed....
Access request information
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
Set<String> keySet = headers.keySet();
keySet.forEach(key -> {
List<String> values = headers.get(key);
System.out.println(key + "::" + values);
});
return chain.filter(exchange);
}
}
When we send a request to the REST API using POSTMAN, it will send a POSTMAN Token in the request header.
Using this token we can differentiate requests that come from other apps or from POSTMAN.
Load Balancing
If we run our application on a Single Server then all requests will be sent to a single server.
Burden will be increased on the server
When the burden increases, request processing gets delayed.
Sometimes our server might crash due to heavy load
To overcome the above problems, we will use the Load Balancing concept.
@RestController
public class WelcomeRestController {
@Autowired
private Environment env;
@GetMapping("/welcome")
public String welcomeMsg() {
String port = env.getProperty("server.port");
Welcome to Ashok IT..!! (Port ::
return msg;
}
}
Run Welcome API with 3 instances
Right Click on API
-> Run As -> Run Configurations
Select Application
-> Arguments
-> VM Arguments (-Dserver.port=8082)
Apply & Run it
Check Eureka Dashboard
What is Cache?
1) Cache is a temporary storage
When our application wants to access the same data frequently, then we will use cache memory.
Cache will improve the performance of our application by reducing database calls.
4) Note: Database calls are always costly which will take more time to execute.
5) To reduce the number of round trips between the application and the database, we will use 'Cache'.
Redis Cache
Redis is one of the distributed caches available in the market.
Redis will store data in key-value pair
Multiple Applications can connect with Redis Cache at a time...
The open source, in-memory data store used by millions of developers as a database, cache, streaming
engine, and message broker.
Redis Setup
1) Download Redis Software
2) URLhttps://redis.io/download/#redis-downloads
3) Run 'redis-server.exe' file
4) Note: By default it runs on '6379' port number
5) Run 'Redis-cli.exe' file
6) Type 'ping' command in Redis CLI
7) Note: The server will respond with 'PONG' as a response.
<dependency>
org.springframework.boot
spring-boot-starter-data-redis
<exclusions>
io.lettuce
lettuce-core
</dependency>
<dependency>
redis.clients
jedis
</dependency>
Configuration
public class RedisConfig {
@Bean
public JedisConnectionFactory getJedisConnection() {
JedisConnectionFactory factory = new JedisConnectionFactory();
// factory.setHostName(hostName);
// factory.setPassword(password);
// factory.setPort(port);
return factory;
}
Bean
@Primary
public RedisTemplate<String, User> getRedisTemplate(JedisConnectionFactory factory) {
RedisTemplate<String, User> rt = new RedisTemplate<>();
rt.setConnectionFactory(factory);
return rt;
}
}
package in.ashokit.binding;
import java.io.Serializable;
import lombok.Data;
@Data
public class User implements Serializable{
private Integer uid;
private String name;
private Integer age;
}
RestController
public class UserRestController {
private HashOperations<String, Integer, User> hashOps;
public UserRestController(RedisTemplate<String, User> redisTemplate) {
hashOps = redisTemplate.opsForHash();
}
/user
public String storeData(@RequestBody User user) {
hashOps.put("PERSONS", user.getUid(), user);
return "success";
}
@GetMapping("/user/{uid}")
public User getData(@PathVariable Integer uid) {
User value = (User) hashOps.get("PERSONS", uid);
return value;
}
@GetMapping("/users")
public List<User> getAllUsers() {
return hashOps.values("PERSONS");
}
@DeleteMapping("/user/{uid}")
public String deleteUser(@PathVariable Integer uid) {
hashOps.delete("PERSONS", uid);
User Deleted
}
}
Actuator Endpoints
URL : http://localhost:8080/actuator
Note: /health is a default endpoint which we can access directly
We can expose other actuator endpoints using the below property.
application.yml
management
endpoints
web
exposure
*
Note: To expose endpoints using application.properties we will use the below property
management.endpoints.web.exposure.include=*
management
endpoints
web
exposure
*
endpoint:
shutdown
enabled: true
We can avoid hard coded values by configuring app properties in this application.properties file
3) properties file will represent data only in key-value format
i. Ex:
ii. server.port = 9090
/views/
.jsp
4) properties file will represent data in sequential format
5) properties file will be supported by only java
For every profile we need to create a separate properties file
@RestController
public class WelcomeRestController {
messages.welcome
private String welcomeMsg;
messages.greet
private String greetMsg;
@GetMapping("/welcome")
public String welcomeMsg() {
return welcomeMsg;
}
GetMapping("/greet")
public String greetMsg() {
return greetMsg;
}
}
Application messages and REST endpoint URLs are not recommended to be hardcoded in Java classes.
Because if we change any message or any URL then we have to compile and package the entire application.
To avoid this problem we will configure messages and URLs in the application.properties file or in
application.yml file
When we change the application.properties file or application.yml file, we do not need to compile and build.
entire project.
1) Create a GitHub repository and keep configuration properties in the GitHub repo
Note: We need to use the application name for configuration properties/yml file name
Ex:
Welcome
welcome-dev.yml
welcome-prod.yml
admin.yml
admin-dev.yml
admin-prod.yml
reports.yml
reports-dev.yml
reports-prod.yml
https://github.com/ashokitschool/configuration_properties.git
a) config-server
b) actuator
2) Write @EnableConfigServer annotation at boot start class
3) Configure below properties in application.yml file
spring
cloud
config
server
git:
https://github.com/ashokitschool/configuration_properties
clone-on-start: true
management
security
enabled: false
Microservice To Load Config Properties using Config Server (Config Client App)
1) Create Boot application with the below dependencies
a) config-client
b) web-starter
cloud-bootstrap
2) Configure application name, application port, config-server-url, profile
app-name
spring
application
welcome
cloud
config
http://localhost:8080
b) application.yml (server port)
server
port: 9090
3) Create Rest Controller with required methods
@RestController
@RefreshScope
public class WelcomeRestController {
Config Server Not Working
private String msg;
@GetMapping("/")
public String getWelcomeMsg() {
return msg;
}
}
Run the application and test it.
}
@RestController
public class DemoRestController {
private Logger logger = LoggerFactory.getLogger(DemoRestController.class);
@GetMapping("/")
public String doAction() {
Action in progress
try {
Division by zero!
} catch (Exception e) {
logger.error("Exception Occurred ::" + e, e);
throw new ArithmeticException(e.getMessage());
}
return msg;
}
@ExceptionHandler(value=ArithmeticException.class)
public ResponseEntity<ExceptionInfo> handleAE(ArithmeticException ae) {
ExceptionInfo exception = new ExceptionInfo();
exception.setMsg(ae.getMessage());
AIT0004
return new ResponseEntity<>(exception, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Spring Security
1) To implement security for our applications, Spring provided the 'security' module.
To use Spring Security in our project 'spring-security-starter' we need to add in pom.xml file
<dependency>
org.springframework.boot
spring-boot-starter-security
admin
admin@123
web-starter
b) webflux
c) lombok
--------------------------------
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CustomerEvent {
private String name;
private Date createDate;
}
---------------------------------
@RestController
public class CustomerRestController {
@GetMapping(value = "/event", produces = "application/json")
public ResponseEntity<Mono<CustomerEvent>> getEvent() {
CustomerEvent event = new CustomerEvent("Ashok", new Date());
Mono<CustomerEvent> customerMono = Mono.just(event);
return new ResponseEntity<Mono<CustomerEvent>>(customerMono, HttpStatus.OK);
}
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseEntity<Flux<CustomerEvent>> getEvents() {
// creating binding object with data
CustomerEvent event = new CustomerEvent("Ashok", new Date());
creating stream for binding object
Stream<CustomerEvent> customerStream = Stream.generate(() -> event);
// create flux object using stream
Flux<CustomerEvent> cflux = Flux.fromStream(customerStream);
// setting response interval
Flux<Long> intervalFlux = Flux.interval(Duration.ofSeconds(5));
// combine interval flux and customer event flux
Flux<Tuple2<Long, CustomerEvent>> zip = Flux.zip(intervalFlux, cflux);
// Getting Tuple value as T2
Flux<CustomerEvent> fluxMap = zip.map(Tuple2::getT2);
//sending response
return new ResponseEntity<>(fluxMap, HttpStatus.OK);
}
}
1) Component
2) Metadata
3) Template
4) Data Binding
Module
6) Service
7) Dependency Injection
8) Directive
9) Pipes
10) An Angular application is a collection of components. In components, we will write logic to send data to
template and capture data from template. Components are TypeScript classes.
11) Metadata is nothing but data about the data. It provides information about components.
12) The template is a view where we will write our presentation logic. In an Angular application, the template is an HTML.
Mocking
Mocking is the process of creating a substitute object for the real object.
Using Mock Objects we can perform isolated unit testing
@Service
public class WelcomeService {
public String getMsg() {
Good Morning
return msg;
}
}
-----------------------------------------------------------
@RestController
public class WelcomeRestController {
@Autowired
private WelcomeService service;
@GetMapping("/welcome")
public String welcomeMsg() {
String msg = service.getMsg();
return msg;
}
}
@WebMvcTest(value = WelcomeRestController.class)
public class WelcomeRestControllerTest {
@MockBean
private WelcomeService service;
@Autowired
private MockMvc mockMvc;
@Test
public void welcomeMsgTest() throws Exception {
defining mock object behavior
when(service.getMsg()).thenReturn("Welcome to Ashok IT");
// preparing request
MockHttpServletRequestBuilder reqBuilder = MockMvcRequestBuilders.get("/welcome");
sending request
MvcResult mvcResult = mockMvc.perform(reqBuilder).andReturn();
get the response
MockHttpServletResponse response = mvcResult.getResponse();
// validate response status code
int status = response.getStatus();
assertEquals(200, status);
}
}
a. web-starter
b. data-jpa
c. h2
d. project lombok
e. devtools
Create Entity class
@Data
@Entity
Table name = "BOOK_DTLS"
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
BOOK_ID
private Integer bookId;
Column(name = "BOOK_NAME")
private String bookName;
BOOK_PRICE
private Double bookPrice;
}
jdbc:h2:mem:testdb
sa
sa
org.h2.Driver
update
true
Run the boot application and insert the data using POST Request
Create Angular application
$ ng new bookapp
Create Book class to represent json response in object format
$ ng generate class Book
export class Book {
number
string
number
constructor(a:number,b:string,c:number){
this.bookId = a;
b
this.bookPrice = c;
}
}
Import HttpClientModule & FormsModule in AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, FormsModule, HttpClientModule
],
providers: [],
[AppComponent]
})
export class AppModule { }
getData() {
this.http.get<Book[]>("http://localhost:8080/books", {responseType: 'json'})
.subscribe(data => {
this.books = data;
});
}
onInsertClick() {
this.http.post("http://localhost:8080/book", this.book, {responseType:"text"})
.subscribe(data => {
this.msg = data;
});
}
}
Write presentation logic in template
<div>
Book Details
<input type="button" value="Get Data" (click)="getData()"/>
<table border="1">
<tr>
Book Id
Book Name
Book Price