Wednesday, 26 February 2025

Microservices in java

🔍 What are Microservices?

Microservices is an architectural style application. It has collection of loosely coupled, independently deployable services, and each service is responsible for a specific business function.

Instead of building a monolithic application, microservices allow you to divide functionality (like User, Order, Notification, etc.) into separate components that:

  • Microservices are developed, deployed, and scaled independently

  • Microservices communicate via lightweight protocols (e.g., HTTP/REST, gRPC, Kafka)

  • Microservices can be written in different programming languages (polyglot)

  • Microservices are organized around business capabilities


✅ Key Characteristics

FeatureDescription
DecentralizedEach service has its own logic and database.
ScalableServices can be scaled independently.
ResilientIf any service is failed then don't affect the whole system.
DeployableServices can be deployed independently without full app restart.
Technology-agnosticYou can use different tools or languages for different services.

🛠️ Microservices in Java – Tools & Technologies
LayerCommon Tools
FrameworkSpring Boot, Micronaut, Quarkus
API GatewaySpring Cloud Gateway, Zuul, NGINX
Service DiscoveryEureka, Consul, Zookeeper
CommunicationREST (WebClient), RestTemplateKafka, gRPC, RabbitMQ
ConfigurationSpring Cloud Config, Consul
SecuritySpring Security + OAuth2/JWT
ResilienceResilience4j, Hystrix
ObservabilitySleuth, Zipkin, Prometheus + Grafana, ELK stack
Packaging/DeploymentDocker, Kubernetes, Jenkins, Helm

🧩 Example Microservices Setup in Java (Spring Boot)

springboot-microservices-starter/ │ ├── config-server/ # Centralized config management ├── api-gateway/ # Gateway routing requests ├── discovery-server/ # Eureka service registry │ ├── user-service/ # Handles user data (MS SQL, Redis) ├── order-service/ # Handles orders ├── notification-service/ # Sends emails or notifications (Kafka consumer) │ ├── kafka-config/ # Kafka producer/consumer utilities ├── docker/ # Docker Compose setup ├── k8s/ # Kubernetes YAML files

🔁 Communication Between Services

  1. Synchronous:

    • REST API using WebClient or RestTemplate

    • gRPC for high-performance binary communication

  2. Asynchronous:

    • RabbitMQ or Kafka for event-driven architecture


✅ Advantages of Microservices

  • ✅ Better scalability and flexibility

  • ✅ Faster development and deployment cycles

  • ✅ Improved fault isolation and maintainability

  • ✅ Independent tech stacks and database per service


⚠️ Challenges of Microservices

ChallengeSolution
Distributed loggingUse centralized logging (ELK, Fluentd)
Service communicationAPI Gateway, Circuit Breakers
Data consistencyEvent-driven architecture (Kafka), Sagas
SecurityJWT, OAuth2, centralized authentication service
Deployment complexityDocker + Kubernetes, CI/CD pipelines

✅ When to Use Microservices
Recommended If...Avoid If...
You need scalabilityYour app is small/simple
You want independent team deliveryYou have tight resources
You expect rapid evolution of componentsYou're unfamiliar with DevOps tooling

🔚 Conclusion

Microservices in Java (especially using Spring Boot) is a powerful way to build scalable, modular, and maintainable applications. It is widely used in enterprise systems, fintech, e-commerce, and cloud-native applications.
---------------------------------

2).How many ways communicate spring boot with microservices

Spring Boot can communicate with other microservices in multiple ways, depending on the architecture style, protocol, and purpose. Below are all the major ways Spring Boot microservices can communicate with each other, grouped by synchronous vs asynchronous communication:


🔹 1. Synchronous Communication (Request/Response)

In synchronous communication, one service waits for the response from another. 

✅ a. RESTful HTTP using RestTemplate (legacy but still used)

  • RestTemplate is a synchronous HTTP client to call other microservices.

  • Common for simple, blocking calls.

java
RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject("http://user-service/users/1", String.class);

✅ b. RESTful HTTP using WebClient (preferred for non-blocking)

  • Part of Spring WebFlux, supports reactive programming.

  • Recommended over RestTemplate for non-blocking I/O.

java
WebClient webClient = WebClient.create(); Mono<String> response = webClient.get() .uri("http://user-service/users/1") .retrieve() .bodyToMono(String.class);

✅ c. Feign Client (Declarative REST client)

  • Part of Spring Cloud OpenFeign.

  • Auto-load-balances with Eureka + Ribbon.

  • Simplifies REST call with interface and annotations.

✅ What is Feign?

Feign is a declarative HTTP client developed by Netflix and integrated into Spring Cloud. Instead of manually writing code to call REST APIs using RestTemplate or WebClient, you define an interface and annotate it with @FeignClient.

java
@FeignClient(name = "user-service") public interface UserClient { @GetMapping("/users/{id}") User getUserById(@PathVariable("id") Long id); }
(OR)
@FeignClient(name = "service-name", url = "http://localhost:8080") public interface MyFeignClient { @GetMapping("/api/data") String getData(); }


2. Asynchronous Communication (Event-Driven)

Services communicate using events, without waiting for a response.

✅ a. Apache Kafka

  • Distributed event-streaming platform.

  • Suitable for real-time data pipelines.

Producer

java
kafkaTemplate.send("user-events", userCreatedEvent);

Consumer

java
@KafkaListener(topics = "user-events", groupId = "order-service") public void listen(UserEvent event) { // Process event }

✅ b. RabbitMQ (AMQP)

  • Message broker for asynchronous messaging.

  • Spring Boot supports it with spring-boot-starter-amqp.

java
rabbitTemplate.convertAndSend("exchange", "routing.key", message);

🔹 3. Service Discovery (for REST/Kafka)

Helps microservices find each other dynamically.

✅ a. Eureka Server (Spring Cloud Netflix Eureka)

  • Services register with Eureka.

  • Enables dynamic discovery for REST, Feign, Kafka, etc.

yaml
# application.yml eureka: client: service-url: defaultZone: http://localhost:8761/eureka/

🔹 4. API Gateway Communication

All calls go through a central API Gateway for routing, filtering, auth, etc.

✅ a. Spring Cloud Gateway

  • Reactive API gateway for routing requests to downstream services.

  • Can integrate with discovery and load balancing.

yaml
spring: cloud: gateway: routes: - id: user-service uri: lb://USER-SERVICE predicates: - Path=/users/**

🔹 5. gRPC (Binary Protocol)

High-performance communication using protocol buffers.

  • Faster and more efficient than REST.

  • Requires proto files and code generation.

java
UserGrpc.UserBlockingStub stub = UserGrpc.newBlockingStub(channel); UserResponse response = stub.getUser(UserRequest.newBuilder().setId("1").build());

🔹 6. RSocket

Reactive protocol over WebSocket/TCP/HTTP for bidirectional streaming.

  • Low-latency, supports request-response and streaming.

  • Useful in reactive microservices.

java
RSocketRequester rsocketRequester = ... rsocketRequester.route("user.get").data("1").retrieveMono(User.class);

🔹 7. GraphQL Federation

Communicate via GraphQL queries if you use federated services.

  • Each service exposes a schema.

  • Central gateway composes full schema.


🔹 8. Shared Database / Event Store (Not recommended for true microservices)

  • Services read/write to the same database or event log (e.g., Event Sourcing).

  • Used only when data ownership or CQRS applies.


🔹 9. File-based Communication / FTP / Shared Storage

  • Rare, but useful for legacy integration.


✅ Summary Table

MethodTypeUse CaseTech Stack
RestTemplateSyncSimple REST callsSpring MVC
WebClientSyncNon-blocking callsSpring WebFlux
Feign ClientSyncDeclarative REST + Load BalancingSpring Cloud
KafkaAsyncEvent-driven, real-time messagingKafka, Spring Kafka
RabbitMQAsyncMessage QueuesSpring AMQP
gRPCSync/AsyncHigh-performance binary messagingSpring Boot + gRPC
RSocketAsyncReactive streaming communicationRSocket + Spring Boot
EurekaDiscoveryDynamic service registration/discoverySpring Cloud Netflix Eureka
Spring GatewaySyncCentral API GatewaySpring Cloud Gateway

2) Kubernetes DNS with Spring Boot microservices, Docker, and RestTemplate — with no Eureka or service registry.

Here's a complete step-by-step guide to use Kubernetes DNS with Spring Boot microservices, Docker, and RestTemplate — with no Eureka or service registry.


✅ Goal

You want:

  • Multiple Spring Boot microservices

  • Communication via RestTemplate

  • Service Discovery via Kubernetes DNS

  • Packaged in Docker containers

  • Deployed to Kubernetes


🧱 Microservices Architecture Example

Let's assume two services:

  1. user-service (Provider)

  2. order-service (Consumer that calls user-service using RestTemplate)


🔧 1. Spring Boot Configuration

✅ In order-service

java
@Service public class OrderService { private final RestTemplate restTemplate; public OrderService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public String fetchUserDetails(Long userId) { String url = "http://user-service/users/" + userId; return restTemplate.getForObject(url, String.class); } }

💡 http://user-service is the Kubernetes DNS name. You don't need Eureka.


✅ Configuration Class for RestTemplate

java
@Configuration public class AppConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); // ❌ Do NOT use @LoadBalanced } }

Do not use @LoadBalanced — it’s needed only when using Spring Cloud LoadBalancer or Eureka.


application.yml

You only need basic configuration:

yaml
spring: application: name: order-service

🐳 2. Dockerfile for Both Services

dockerfile
FROM openjdk:17-jdk-slim VOLUME /tmp COPY target/*.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]

🧱 Build and Push

bash
docker build -t your-dockerhub/order-service . docker push your-dockerhub/order-service docker build -t your-dockerhub/user-service . docker push your-dockerhub/user-service

☸️ 3. Kubernetes Deployment & Service YAMLs


✅ user-service.yaml

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 1 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: your-dockerhub/user-service ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: user-service spec: selector: app: user-service ports: - protocol: TCP port: 80 targetPort: 8080

✅ order-service.yaml

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 1 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: your-dockerhub/order-service ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: order-service spec: selector: app: order-service ports: - protocol: TCP port: 80 targetPort: 8080

🌐 4. Deploy and Verify in Kubernetes

bash
kubectl apply -f user-service.yaml kubectl apply -f order-service.yaml

🔍 Test DNS:

From order-service pod:

bash
kubectl exec -it <order-service-pod> -- curl http://user-service/users/1

🧠 How DNS Works in Kubernetes

Kubernetes creates DNS entries for every service:

  • http://user-service = internal service name

  • Optional full DNS: http://user-service.default.svc.cluster.local

So your RestTemplate will automatically resolve the DNS via internal service discovery.


✅ Summary: Configurations Needed

AreaConfiguration
Spring Boot App  Use RestTemplate with http://<service-name>
RestTemplate Bean  No @LoadBalanced, just plain new RestTemplate()
Service Naming  Match Feign or REST URLs to Kubernetes Service names
Docker  Build Docker images of each service
Kubernetes  Deployments + Services for each microservice
Service Discovery  Handled by Kubernetes DNS (no Eureka, Consul, etc.)

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

If we use Java 17, make HTTP calls (GET, POST, PUT, DELETE) communicate to microservices

✅ Full Java 17 Code – Clean Microservice Communication

1. Product.java – POJO to represent request/response

java
public class Product { private String name; private int price; public Product() {} public Product(String name, int price) { this.name = name; this.price = price; } // Getters and Setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }

2. MicroserviceHttpClient.java – main class to call services

java
import java.net.URI; import java.net.http.*; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse.BodyHandlers; import com.fasterxml.jackson.databind.ObjectMapper; public class MicroserviceHttpClient { private static final HttpClient client = HttpClient.newHttpClient(); private static final ObjectMapper mapper = new ObjectMapper(); public static void main(String[] args) throws Exception { performGET(); performPOST(); performPUT(); performDELETE(); } // GET request (fetch product) public static void performGET() throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(new URI("http://localhost:8081/api/products/101")) .GET() .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println("GET Response:\n" + response.body()); } // POST request (create product) public static void performPOST() throws Exception { Product product = new Product("Laptop", 75000); String requestBody = mapper.writeValueAsString(product); HttpRequest request = HttpRequest.newBuilder() .uri(new URI("http://localhost:8081/api/products")) .header("Content-Type", "application/json") .POST(BodyPublishers.ofString(requestBody)) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println("POST Response:\n" + response.body()); } // PUT request (update product) public static void performPUT() throws Exception { Product updatedProduct = new Product("Updated Laptop", 78000); String requestBody = mapper.writeValueAsString(updatedProduct); HttpRequest request = HttpRequest.newBuilder() .uri(new URI("http://localhost:8081/api/products/101")) .header("Content-Type", "application/json") .PUT(BodyPublishers.ofString(requestBody)) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println("PUT Response:\n" + response.body()); } // DELETE request (delete product) public static void performDELETE() throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(new URI("http://localhost:8081/api/products/101")) .DELETE() .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println("DELETE Response:\n" + response.body()); } }

✅ Dependencies (for Jackson):

Add the following to your pom.xml if using Maven:

xml

<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.17.1</version> </dependency>

✅ Summary

  • ✅ Java 17 standard HTTP client — no Spring

  • ✅ Object-based JSON using Jackson (Product class)

  • ✅ Covers GET, POST, PUT, DELETE with clean structure

  • ✅ No hardcoded JSON strings

    🧩 MICROservices DESIGN PATTERNS


    1. Aggregator / API Gateway Pattern

    What:
    An API Gateway acts as a single entry point for all clients. It aggregates multiple microservices' results and returns a single response.

    Why:

    • Simplifies client interactions

    • Reduces client-side complexity

    • Adds cross-cutting concerns like authentication, rate-limiting, and logging

    When to Use:

    • When you have multiple microservices but want the client to make a single request

    • When mobile/web apps shouldn’t manage multiple service endpoints

    Example:
    Client → API Gateway → [User Service + Order Service + Inventory Service] → Aggregated Response


    2. Circuit Breaker Pattern

    What:

    The Circuit Breaker Pattern is a design pattern used in microservice and distributed system architectures to detect failures and prevent an application from repeatedly trying to execute an operation that is likely to fail.

    It temporarily blocks access to a failing service after a defined number of consecutive failures and allows retry only after a cooldown period, thereby improving system resilience, stability, and fault tolerance.

    Why:

    • Prevents cascading failures

    • Improves fault tolerance

    • Provides fallback mechanisms

    When to Use:

    • When calling a remote service that may become unresponsive

    • During timeouts and repeated failures

    Example:
    Hystrix, Resilience4j libraries can be used
    If Inventory Service is down → Circuit opens → Short-circuits future requests → Returns default message


    3. Chain of Responsibility Pattern

    What:
    Each microservice handles a part of a request and passes it to the next in a sequence.

    Why:

    • Breaks down processing into manageable steps

    • Makes the system extensible and decoupled

    When to Use:

    • When processing requires multiple sequential tasks

    • For order fulfillment, data validation chains, etc.

    Example:
    Order Microservice → Inventory → Payment → Notification


    4. Asynchronous Messaging Pattern

    What:
    Microservices communicate via messaging systems (e.g., Kafka, RabbitMQ) instead of direct HTTP.

    Why:

    • Decouples services

    • Improves scalability

    • Supports event-driven architecture

    When to Use:

    • When real-time sync is not required

    • When handling high traffic or batch jobs

    Example:
    User registers → Auth Service publishes "UserCreated" event → Email Service consumes event and sends welcome mail


    5. Database per Service Pattern

    What:
    Database per Service is a microservices architecture pattern where each service owns and manages its own database schema, rather than sharing a common one across multiple services.

    Why:

    • Promotes loose coupling

    • Prevents cross-service data dependencies

    • Improves autonomy

    When to Use:

    • In all microservice architectures

    • Avoid shared database schemas

    Example:

    • User Service uses PostgreSQL

    • Order Service uses MySQL

    • Inventory Service uses MongoDB


    6. Event Sourcing Pattern

    What:
    Event Sourcing is a design pattern where every change to the application state is stored as a sequence of events, rather than storing just the current state. The current state is derived by replaying these events in order.

    Why:

    • Full history of changes

    • Enables audit logs, rollback, and replay

    • Improves scalability and traceability

    When to Use:

    • In complex domain models

    • When audit/history is required

    Example:
    Instead of just saving "Order Delivered", save events like:
    OrderPlaced → OrderPacked → OrderShipped → OrderDelivered


    7. Shared Data Pattern (Anti-pattern)

    What:
    The Shared Data Pattern (considered an anti-pattern in microservices) occurs when multiple services access the same database or schema, leading to tight coupling between services and potential data integrity issues.

    Why It's Bad:

    • Tight coupling

    • Schema changes break multiple services

    • Leads to performance issues and data integrity problems

    Avoid it by:

    • Using Database per Service

    • Communicating via APIs or messaging

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

1) Session Management in Microservices

In Microservices architecture, session management is challenging because services are stateless by design—they do not retain any user session information between requests. To maintain authentication and user context across requests and services, specific strategies must be used. 

Common approaches are

1)Token-Based Authentication (JWT)
Uses JSON Web Tokens (JWT) to carry session information between client and services in a stateless way.
🔧 Flow:
User logs in via authentication service.
Server returns a signed JWT to the client.
Client includes JWT in every request header (Authorization: Bearer <token>).
Services validate and extract user info from the token.

2)Centralized Session Store (e.g., Redis, DB)
Stores session data in a centralized backend (like Redis), with session ID shared across services.
🔧 Flow:
User logs in; server generates a session ID and stores user session data in Redis.
Session ID is sent to client (usually via cookie or header).
Each microservice uses the session ID to fetch session data from Redis.

3)OAuth2 / OpenID Connect with API Gateway
Uses an authorization server (like Keycloak or Okta) with an API Gateway (like Spring Cloud Gateway) that performs authentication and passes identity context downstream.
🔧 Flow:
User authenticates via OAuth2 provider.
API Gateway receives token and validates it.
Gateway forwards user identity to microservices via headers or claims.
Microservices trust the gateway and act based on forwarded identity.

4)Cookie-Based Sessions (Rare in Microservices)
Traditional method using browser cookies to store session ID and maintain state.
✅ Pros:
Easy to implement in monolithic or frontend-centric apps.

Best Practice:
Use JWT with API Gateway for stateless, scalable session handling.


2) Service Discovery in Microservices

Service discovery allows microservices to find and communicate with each other dynamically without hardcoding IPs.
There are two types: Client-side (e.g., Eureka) and Server-side (e.g., Kubernetes, Consul).
Services register with a service registry, and consumers query it to find instances.
This enables scaling, resilience, and dynamic load balancing.

3) How to Communicate Between Microservices

Microservices communicate using REST APIs, gRPC, or message brokers like Kafka or RabbitMQ.
For synchronous communication, services use HTTP calls (via RestTemplate, WebClient, or Feign).
For asynchronous communication, they publish/subscribe to event streams.
Service discovery and API Gateway often help route and manage service-to-service communication.
Security, retries, timeouts, and circuit breakers (like Resilience4j) should be implemented for reliability.