Spring Cloud के साथ Microservices: बड़े Apps को छोटे टुकड़ों में तोड़ना
Spring Cloud के साथ microservices बनाने के लिए beginner-friendly guide — API Gateway, Eureka service discovery, Config Server, Resilience4j circuit breakers, load balancing, Feign clients, WebClient, Micrometer और Zipkin के साथ distributed tracing, और inter-service authentication को cover करता है।
Microservices क्या हैं और आपको क्यों परवाह होनी चाहिए?
कल्पना करें कि आप एक विशाल restaurant चलाते हैं जहाँ एक विशालकाय kitchen सब कुछ संभालती है: appetizers, main courses, desserts, drinks, और billing। अगर dessert chef बीमार हो जाए, तो पूरा restaurant बंद हो जाता है। यह एक monolith है — एक बड़ा application जो सब कुछ करता है।
अब इसके बजाय एक food court की कल्पना करें। वहाँ एक pizza counter है, एक burger counter, एक juice bar, और एक billing kiosk। हर counter स्वतंत्र रूप से चलता है। अगर juice bar खराब हो जाए, तो बाकी सब serve करते रहते हैं। यह microservices है — कई छोटी, स्वतंत्र services जो हर एक एक काम अच्छी तरह करती हैं।
Spring Cloud Java developers को इन छोटी services को बनाने, जोड़ने, और manage करने के लिए एक toolbox देता है। चलिए हर tool को देखते हैं।
Monolith बनाम Microservices: एक साथ-साथ तुलना
| पहलू | Monolith (बड़ा Restaurant) | Microservices (Food Court) |
|---|---|---|
| Deployment | एक बड़ा JAR — पूरा deploy करें | हर service स्वतंत्र रूप से deploy होती है |
| Scaling | सब कुछ scale करें भले ही सिर्फ़ एक हिस्सा busy हो | केवल busy counter (service) को scale करें |
| Failure | एक bug पूरे app को crash कर सकता है | एक service fail हो, दूसरी चलती रहती हैं |
| Team ownership | हर कोई एक ही codebase में काम करता है | हर team अपनी service owns करती है |
| Tech stack | एक भाषा/framework में locked | हर service अलग tech इस्तेमाल कर सकती है |
| Complexity | पहले सरल, बढ़ने पर तकलीफ़देह | पहले दिन से ज़्यादा moving parts |
अंगूठे का नियम: छोटे project के लिए monolith से शुरू करें। microservices पर तब जाएँ जब आपकी team और traffic इतनी बड़ी हो जाए कि monolith bottleneck बन जाए।
API Gateway (Spring Cloud Gateway)
API Gateway को एक hotel के front desk की तरह सोचें। Guests (clients) सीधे kitchen या housekeeping में नहीं जाते। वे front desk पर जाते हैं, जो उन्हें सही department पर route करता है। Gateway HTTP requests के लिए यही करता है।
Spring Cloud Gateway आपकी सभी microservices के सामने बैठता है। यह संभालता है:
- Routing —
/api/orders/**को order-service पर भेजें,/api/users/**को user-service पर - Load balancing — requests को एक service के कई instances पर फैलाएँ
- Filters — forward करने से पहले headers जोड़ें, rate-limit करें, log करें, या authenticate करें
- Single entry point — Clients को केवल एक URL जानने की ज़रूरत है
Gateway Configuration (application.yml)
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- id: inventory-service
uri: lb://INVENTORY-SERVICE
predicates:
- Path=/api/inventory/**
filters:
- StripPrefix=1
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- id: notification-service
uri: lb://NOTIFICATION-SERVICE
predicates:
- Path=/api/notifications/**
filters:
- StripPrefix=1
lb:// prefix gateway को बताता है कि load balancing का इस्तेमाल करे और service को service registry (Eureka) से नाम से देखे। StripPrefix=1 forward करने से पहले /api को हटा देता है, इसलिए /api/orders/123 order-service तक पहुँचने पर /orders/123 बन जाता है।
Custom Global Filter (Java)
आप cross-cutting logic जोड़ सकते हैं — जैसे हर request को log करना — एक global filter का इस्तेमाल करके:
@Component
public class LoggingFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
String method = exchange.getRequest().getMethod().name();
log.info("Incoming request: {} {}", method, path);
return chain.filter(exchange).then(Mono.fromRunnable(() ->
log.info("Response status: {}", exchange.getResponse().getStatusCode())
));
}
@Override
public int getOrder() {
return -1; // Run before other filters
}
}
Service Discovery (Eureka)
एक food court में, front desk को कैसे पता चले कि आज कौन-से counters खुले हैं? कोई एक live list रखता है। यही Eureka करता है। हर microservice शुरू होने पर खुद को Eureka के साथ register करता है, और Eureka track रखता है कि कौन-सी services जीवित हैं और कहाँ रहती हैं (IP + port)।
Service discovery के बिना, आपको http://192.168.1.10:8080 जैसे URLs को hardcode करना पड़ता। अगर वह server move हो या आप एक दूसरा instance जोड़ें, सब कुछ टूट जाता है। Eureka के साथ, services बस कहती हैं "मैं ORDER-SERVICE हूँ" और दूसरी services उन्हें नाम से ढूँढ लेती हैं।
Eureka Server Setup करना
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
# Eureka Server application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # Server does not register with itself
fetch-registry: false
Service को Register करना (Eureka Client)
हर microservice Eureka client dependency और कुछ config lines जोड़ता है:
# order-service application.yml
spring:
application:
name: ORDER-SERVICE
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
अब Eureka जानता है: "ORDER-SERVICE 192.168.1.10:8081 पर चल रहा है, और 192.168.1.11:8081 पर भी।" दूसरी services इसे नाम से ढूँढ सकती हैं।
केंद्रीकृत Configuration (Spring Cloud Config Server)
कल्पना करें कि हर food court counter के पास अपनी recipe book है। अगर आपको सभी recipes में salt level बदलना हो, तो आपको हर counter पर एक-एक करके जाना पड़ेगा। यह तकलीफ़देह है।
Spring Cloud Config Server एक साझा recipe book की तरह है जो एक जगह (आमतौर पर एक Git repository) में store की गई है। सभी services अपना configuration इस केंद्रीय स्थान से खींचती हैं। एक बार बदलें, और हर service इसे उठा लेती है।
Config Server Setup
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
# Config Server application.yml
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repo
default-label: main
search-paths: '{application}'
Client-Side Config (order-service)
# order-service application.yml
spring:
application:
name: order-service
config:
import: optional:configserver:http://localhost:8888
Config server Git repo में order-service.yml नाम की file ढूँढता है और उसे serve करता है। आप profiles भी इस्तेमाल कर सकते हैं: order-service-dev.yml, order-service-prod.yml।
Runtime पर Config Refresh करना
Beans को @RefreshScope से mark करें और POST /actuator/refresh hit करें — bean service को restart किए बिना अपना config फिर से पढ़ता है:
@RestController
@RefreshScope
public class OrderController {
@Value("${order.max-items:50}")
private int maxItems;
@GetMapping("/orders/max-items")
public int getMaxItems() {
return maxItems;
}
}
Circuit Breaker (Resilience4j)
एक circuit breaker आपके घर के fuse जैसा है। अगर एक wire से बहुत ज़्यादा बिजली बहती है, तो fuse आपके appliances को आग पकड़ने से बचाने के लिए टूट जाता है। एक बार जब आप समस्या ठीक कर लेते हैं, तो आप fuse वापस on कर देते हैं।
Microservices में, अगर inventory-service down है, तो order-service को इसे requests से hammer करना बंद करना चाहिए। यह resources को बर्बाद करेगा और चीज़ों को और बुरा बनाएगा। इसके बजाय, circuit breaker "trip" करता है और तुरंत एक fallback response देता है।
तीन States
| State | क्या होता है | सादृश्य |
|---|---|---|
| CLOSED | सब सामान्य है। Requests service तक जाती हैं। | Fuse सही है। बिजली सामान्य रूप से बहती है। |
| OPEN | बहुत ज़्यादा failures! Requests तुरंत fallback के साथ reject होती हैं। | Fuse उड़ गया है। कोई बिजली नहीं बहती। Appliances सुरक्षित हैं। |
| HALF-OPEN | कुछ test requests जाने दें यह देखने के लिए कि service recover हुई है या नहीं। | आप fuse वापस on करते हैं और एक appliance test के लिए plug करते हैं। |
Flow: CLOSED (सामान्य) → failures जमा होते हैं → OPEN (requests block) → wait timer खत्म होता है → HALF-OPEN (कुछ test करें) → अगर tests pass → वापस CLOSED। अगर tests fail → वापस OPEN।
Resilience4j Configuration
# application.yml
resilience4j:
circuitbreaker:
instances:
inventoryService:
sliding-window-size: 10 # Look at the last 10 calls
failure-rate-threshold: 50 # Trip if 50% of calls fail
wait-duration-in-open-state: 30s # Stay OPEN for 30 seconds
permitted-number-of-calls-in-half-open-state: 5 # Test with 5 calls
automatic-transition-from-open-to-half-open-enabled: true
retry:
instances:
inventoryService:
max-attempts: 3
wait-duration: 2s
timelimiter:
instances:
inventoryService:
timeout-duration: 5s # Give up after 5 seconds
Java Code में Circuit Breaker
@Service
public class OrderService {
private final InventoryClient inventoryClient;
public OrderService(InventoryClient inventoryClient) {
this.inventoryClient = inventoryClient;
}
@CircuitBreaker(name = "inventoryService", fallbackMethod = "inventoryFallback")
@Retry(name = "inventoryService")
@TimeLimiter(name = "inventoryService")
public CompletableFuture<Boolean> checkStock(String sku, int quantity) {
return CompletableFuture.supplyAsync(() ->
inventoryClient.isInStock(sku, quantity)
);
}
// Fallback: runs when circuit is OPEN or call fails after retries
public CompletableFuture<Boolean> inventoryFallback(String sku, int quantity, Throwable t) {
// Log the error and return a safe default
log.warn("Inventory service unavailable for SKU {}. Assuming out of stock.", sku);
return CompletableFuture.completedFuture(false);
}
}
जब circuit OPEN होता है, inventoryFallback method बिना inventory-service को call करने की कोशिश किए तुरंत चलता है। यह आपकी service और संघर्ष कर रही downstream service दोनों की रक्षा करता है।
Client-Side Load Balancing
अगर आपके food court में pizza counter बहुत popular है, तो आप एक दूसरा pizza counter खोलते हैं। अब front desk को तय करना है: "इस customer को counter A पर भेजें या counter B पर?" यह load balancing है।
Spring Cloud requests को एक service के कई instances पर distribute करने के लिए Spring Cloud LoadBalancer (Ribbon का successor) का इस्तेमाल करता है। यह Eureka के साथ automatically काम करता है — बस hardcoded URL के बजाय service name का इस्तेमाल करें।
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced // This annotation enables client-side load balancing
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
@Service
public class OrderService {
private final WebClient.Builder webClientBuilder;
public OrderService(WebClient.Builder webClientBuilder) {
this.webClientBuilder = webClientBuilder;
}
public Mono<InventoryResponse> checkInventory(String sku) {
// "INVENTORY-SERVICE" is looked up from Eureka, load-balanced
return webClientBuilder.build()
.get()
.uri("http://INVENTORY-SERVICE/inventory/{sku}", sku)
.retrieve()
.bodyToMono(InventoryResponse.class);
}
}
@LoadBalanced annotation Spring को बताता है कि request को intercept करे, Eureka से INVENTORY-SERVICE के सभी instances देखे, और एक strategy (default से round-robin) का इस्तेमाल करके एक चुने।
Inter-Service Communication
एक food court में, counters को एक-दूसरे से बात करनी होती है। Burger counter inventory counter से पूछ सकता है: "क्या हमारे पास अभी भी buns हैं?" Services के communicate करने के दो मुख्य तरीके हैं:
1. Feign Clients (Declarative REST)
Feign आपको दूसरी service को call करने देता है जैसे कि आप एक local Java method call कर रहे हों। आप एक interface लिखते हैं, और Feign आपके लिए HTTP client बनाता है।
// Define the client — looks like a regular Java interface
@FeignClient(name = "INVENTORY-SERVICE")
public interface InventoryClient {
@GetMapping("/inventory/{sku}")
InventoryResponse getInventory(@PathVariable String sku);
@PostMapping("/inventory/reserve")
ReservationResponse reserveStock(@RequestBody ReservationRequest request);
}
// Use it — just inject and call like any Spring bean
@Service
public class OrderService {
private final InventoryClient inventoryClient;
public OrderService(InventoryClient inventoryClient) {
this.inventoryClient = inventoryClient;
}
public OrderResponse placeOrder(OrderRequest request) {
// This looks like a local method call, but it is an HTTP GET to inventory-service
InventoryResponse stock = inventoryClient.getInventory(request.getSku());
if (stock.getQuantity() < request.getQuantity()) {
throw new InsufficientStockException("Not enough stock for: " + request.getSku());
}
// Reserve the stock
inventoryClient.reserveStock(new ReservationRequest(
request.getSku(), request.getQuantity()
));
return new OrderResponse("Order placed successfully");
}
}
अपनी main class में Feign enable करें:
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
2. WebClient (Reactive/Non-Blocking)
WebClient modern, non-blocking विकल्प है। इसका इस्तेमाल तब करें जब आपको reactive streams चाहिए या कई calls parallel में करना चाहते हैं:
@Service
public class OrderService {
private final WebClient.Builder webClientBuilder;
public OrderService(@LoadBalanced WebClient.Builder webClientBuilder) {
this.webClientBuilder = webClientBuilder;
}
// Sequential call
public Mono<InventoryResponse> checkInventory(String sku) {
return webClientBuilder.build()
.get()
.uri("http://INVENTORY-SERVICE/inventory/{sku}", sku)
.retrieve()
.bodyToMono(InventoryResponse.class);
}
// Parallel calls — check inventory AND get user info at the same time
public Mono<OrderSummary> getOrderSummary(String sku, String userId) {
Mono<InventoryResponse> inventory = webClientBuilder.build()
.get()
.uri("http://INVENTORY-SERVICE/inventory/{sku}", sku)
.retrieve()
.bodyToMono(InventoryResponse.class);
Mono<UserResponse> user = webClientBuilder.build()
.get()
.uri("http://USER-SERVICE/users/{id}", userId)
.retrieve()
.bodyToMono(UserResponse.class);
// Both calls happen at the same time!
return Mono.zip(inventory, user, (inv, usr) ->
new OrderSummary(usr.getName(), inv.getQuantity(), inv.getPrice())
);
}
}
किसका इस्तेमाल कब करें?
| Feature | Feign Client | WebClient |
|---|---|---|
| Style | Declarative (interface-based) | Programmatic (builder-based) |
| Blocking? | हाँ (default से) | नहीं (non-blocking/reactive) |
| Parallel calls | कठिन (threads चाहिए) | Mono.zip() के साथ आसान |
| इसके लिए सबसे अच्छा | सरल service-to-service REST calls | High-throughput reactive systems |
Distributed Tracing (Micrometer + Zipkin)
जब एक food court में एक customer खराब खाने की शिकायत करता है, तो आप कैसे trace करते हैं कि किस counter ने इसे बनाया? आपको एक receipt number चाहिए जो order के साथ हर counter पर जाए।
Microservices में, एक single user request 5 services से गुज़र सकती है। अगर कुछ ग़लत होता है, तो आपको जानना ज़रूरी है: "यह कहाँ fail हुआ?" Distributed tracing हर request को एक अद्वितीय Trace ID देता है और हर service hop पर इसे track करता है।
मुख्य Concepts
- Trace — एक request की पूरी यात्रा (पूरे receipt की तरह)। एक Trace ID।
- Span — यात्रा में एक step (एक counter के काम की तरह)। हर span की Span ID होती है।
- Parent-Child — अगर order-service inventory-service को call करता है, तो inventory span order span का child है।
Tracing Setup करना
हर service में ये dependencies जोड़ें:
<!-- Micrometer Tracing + Brave bridge -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!-- Send traces to Zipkin -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
# application.yml (add to every service)
management:
tracing:
sampling:
probability: 1.0 # Sample 100% of requests (use 0.1 for 10% in production)
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
logging:
pattern:
level: "%5p [${spring.application.name},%X{traceId},%X{spanId}]"
इस logging pattern के साथ, हर log line में service name, trace ID, और span ID शामिल होती है:
INFO [order-service,abc123def456,span789] Placing order for SKU: WIDGET-001
INFO [inventory-service,abc123def456,span012] Checking stock for SKU: WIDGET-001
INFO [notification-service,abc123def456,span345] Sending confirmation email
ध्यान दें कि trace ID abc123def456 सभी तीन services में समान है। आप इसे Zipkin में search कर सकते हैं और पूरी request यात्रा को एक timeline के रूप में visualize देख सकते हैं।
Inter-Service Authentication
एक food court में, कोई भी kitchen में नहीं चल सकता। Staff ID badges पहनते हैं यह साबित करने के लिए कि वे कौन हैं। Microservices को यही चाहिए — जब order-service inventory-service को call करता है, inventory-service को verify करना होता है कि caller वैध है और कोई random बाहरी नहीं।
Approach 1: JWT Token Propagation
User का JWT token service से service में पास होता है। हर service इसे validate करती है।
// Feign RequestInterceptor — automatically forward the JWT
@Component
public class AuthFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// Get the current request's Authorization header
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
String authHeader = attributes.getRequest().getHeader("Authorization");
if (authHeader != null) {
template.header("Authorization", authHeader);
}
}
}
}
Approach 2: Internal Services के लिए API Key
उन service-to-service calls के लिए जो user से शुरू नहीं होते (जैसे, scheduled jobs), एक shared API key का इस्तेमाल करें:
// Sending side — add API key to outgoing requests
@Component
public class InternalApiKeyInterceptor implements RequestInterceptor {
@Value("${internal.api-key}")
private String apiKey;
@Override
public void apply(RequestTemplate template) {
template.header("X-Internal-API-Key", apiKey);
}
}
// Receiving side — validate API key on incoming requests
@Component
public class ApiKeyFilter extends OncePerRequestFilter {
@Value("${internal.api-key}")
private String expectedApiKey;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String apiKey = request.getHeader("X-Internal-API-Key");
// Allow if valid API key OR if already authenticated via JWT
if (expectedApiKey.equals(apiKey) || isAlreadyAuthenticated()) {
filterChain.doFilter(request, response);
} else {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{"error": "Invalid or missing API key"}");
}
}
private boolean isAlreadyAuthenticated() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return auth != null && auth.isAuthenticated()
&& !(auth instanceof AnonymousAuthenticationToken);
}
}
Internal Endpoints के लिए Spring Security Config
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/**").permitAll()
.requestMatchers("/internal/**").hasRole("SERVICE")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
}
सब कुछ एक साथ रखना: पूरा Architecture
यहाँ बताया गया है कि एक सामान्य microservices setup में सारे टुकड़े एक साथ कैसे fit होते हैं:
[Client / Browser]
|
[API Gateway :8080]
/ | / | [Order :8081] [User :8082] [Notification :8083]
| |
[Inventory :8084] |
| |
[Eureka :8761] (Service Discovery)
[Config :8888] (Central Config)
[Zipkin :9411] (Distributed Tracing)
- Client API Gateway को
POST /api/ordersभेजता है - Gateway Eureka में ORDER-SERVICE ढूँढता है, एक instance पर route करता है
- Order-service Feign के ज़रिए INVENTORY-SERVICE को call करता है (load-balanced, circuit-breaker protected)
- Order-service confirmation भेजने के लिए NOTIFICATION-SERVICE को call करता है
- हर hop Micrometer + Zipkin द्वारा same Trace ID के साथ trace होता है
- सभी services Config Server से config खींचती हैं
Microservices के लिए Best Practices
- प्रति service एक database — कभी भी services के बीच database share न करें। हर service अपना data owns करती है।
- API-first design — कोड लिखने से पहले अपने REST contracts (OpenAPI/Swagger) परिभाषित करें।
- Health checks — Spring Boot Actuator के
/actuator/healthendpoint का इस्तेमाल करें। Eureka इसका इस्तेमाल यह जानने के लिए करता है कि आपकी service जीवित है या नहीं। - Graceful degradation — हमेशा एक fallback रखें। अगर downstream service down है, तो cached data या safe default return करें।
- केंद्रीकृत logging — हर log line में Trace ID इस्तेमाल करें। Logs को एक केंद्रीय जगह (ELK stack, Grafana Loki) पर भेजें।
- सब कुछ containerize करें — हर service को अपनी Docker image मिले। Orchestrate करने के लिए Kubernetes या Docker Compose का इस्तेमाल करें।
- Testing automate करें — Unit tests + integration tests + contract tests (Spring Cloud Contract)।
Starter Dependencies (pom.xml)
Spring Cloud microservice के लिए आपको जिन मुख्य dependencies की ज़रूरत है वे यहाँ हैं:
<properties>
<java.version>21</java.version>
<spring-cloud.version>2024.0.1</spring-cloud.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Client (Service Discovery) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Config Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- Feign Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Resilience4j Circuit Breaker -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- Micrometer Tracing -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!-- Zipkin Reporter -->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<!-- Actuator (health, metrics) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
अक्सर पूछे जाने वाले प्रश्न
मुझे monolith से microservices पर कब switch करना चाहिए?
एक नए project के लिए microservices से शुरू न करें। एक अच्छी तरह से structured monolith से शुरू करें। असली pain points पर पहुँचने पर switch करें: codebase एक team के लिए बहुत बड़ा है, deployments बहुत समय लेते हैं क्योंकि एक बदलाव को सब कुछ redeploy करने की ज़रूरत है, या app के अलग हिस्सों को स्वतंत्र रूप से scale करना है। अगर 3 developers की एक team एक नया product बना रही है, तो monolith लगभग हमेशा सही विकल्प है।
अगर Eureka server down हो जाए तो क्या होता है?
Services registry को locally cache करती हैं। अगर Eureka अस्थायी रूप से down होता है, तो services अभी भी cached copy से एक-दूसरे के बारे में जानती हैं। हालाँकि, कोई नई services register नहीं कर सकतीं और कोई health updates नहीं होते। Production में, multiple Eureka instances (एक cluster) चलाएँ ताकि अगर एक down हो, तो दूसरी काम करती रहें। आप eureka.client.service-url.defaultZone को multiple Eureka URLs पर point करके peer awareness configure कर सकते हैं।
Circuit breaker retry से कैसे अलग है?
एक retry कहता है: "वह fail हुआ? फिर try करें।" एक circuit breaker कहता है: "हाल ही में बहुत सारी चीज़ें fail हुई हैं। कुछ देर के लिए try करना बंद करें।" वे एक साथ काम करते हैं। आप आमतौर पर पहले 2-3 बार retry करते हैं। अगर failures कई calls में जारी रहती हैं, तो circuit breaker open trip करता है और failing service को सभी calls बंद कर देता है। इसे इस तरह सोचें: retry कभी-कभी hiccups (network blip) को संभालता है, circuit breaker लगातार outages (service मिनटों के लिए down) को संभालता है।
क्या मैं inter-service communication के लिए REST के बजाय Kafka या RabbitMQ का इस्तेमाल कर सकता हूँ?
हाँ, और अक्सर आपको करना चाहिए। REST (synchronous) "मुझे अभी उत्तर चाहिए" के लिए अच्छा है — जैसे order place करने से पहले जाँचना कि item in stock है या नहीं। Message queues (asynchronous) "जब भी मौक़ा मिले यह करें" के लिए बेहतर हैं — जैसे confirmation email भेजना या analytics update करना। अधिकांश असली systems दोनों का इस्तेमाल करते हैं: queries और commands के लिए REST जिन्हें तुरंत response चाहिए, और events और notifications के लिए message queues। Spring Cloud Stream कम कोड बदलावों के साथ Kafka या RabbitMQ का इस्तेमाल करना आसान बनाता है।
मैं कई microservices में database transactions को कैसे संभालूँ?
यह microservices में सबसे कठिन समस्याओं में से एक है। आप services के बीच traditional database transaction का इस्तेमाल नहीं कर सकते क्योंकि हर service का अपना database है। इसके बजाय, Saga pattern का इस्तेमाल करें। एक saga local transactions का एक sequence है। अगर step 3 fail हो, तो आप steps 1 और 2 को undo करने के लिए "compensating transactions" चलाते हैं। उदाहरण के लिए: Order-service एक order बनाता है (step 1), inventory-service stock reserve करता है (step 2)। अगर payment fail हो (step 3), तो inventory-service stock release करता है (compensate step 2) और order-service order cancel करता है (compensate step 1)। Spring Cloud एक built-in saga framework प्रदान नहीं करता, लेकिन Axon Framework या Eventuate Tram जैसी libraries मदद कर सकती हैं।