Skip to content
Home Java Eureka UP, Gateway 502 Drops — Spring Boot Microservices

Eureka UP, Gateway 502 Drops — Spring Boot Microservices

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Spring Boot → Topic 14 of 15
Intermittent 502 from Gateway despite Eureka UP.
🔥 Advanced — solid Java foundation required
In this tutorial, you'll learn
Intermittent 502 from Gateway despite Eureka UP.
  • Microservices with Spring Boot and Spring Cloud is a core concept for building distributed systems that are resilient and scalable.
  • Spring Boot builds the 'what' (the logic), while Spring Cloud manages the 'how' (the communication and coordination).
  • Service Discovery (Eureka) and API Gateways (Spring Cloud Gateway) are the entry-level requirements for any microservices architecture.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer
  • Core concept: decomposing applications into independently deployable services with Spring Boot (individual services) and Spring Cloud (coordination layer)
  • Service Discovery (Eureka): services register and discover each other by logical name, not hardcoded IPs
  • API Gateway (Spring Cloud Gateway): single entry point for routing, auth, rate limiting
  • Circuit Breakers (Resilience4j): isolate failures to prevent cascading crashes
  • Performance insight: Eureka heartbeats add ~2ms per instance; gateway adds ~5-10ms per request
  • Production insight: without healthchecks on depends_on, your gateway routes to dead containers silently
🚨 START HERE

5-Minute Microservices Debug Cheat Sheet

Commands and actions for the most common production microservices failures.
🟡

Service not registering with Eureka

Immediate ActionCheck if eureka.client.serviceUrl.defaultZone is correct and reachable.
Commands
curl -v http://<eureka-host>:8761/eureka/apps/<service-name> | jq .
docker logs <eureka-container> --tail 50
Fix NowRestart the service. If still fails, verify the service can reach Eureka's host/port (firewall rules).
🟡

Gateway routes but returns 500 or timeout

Immediate ActionBypass the gateway: call the service directly on its server:port.
Commands
curl -w '%{http_code}' http://<service-ip>:<port>/actuator/health
docker compose logs gateway-service --tail 100 | grep ERROR
Fix NowIf direct call works, increase gateway's connection timeout for that route (spring.cloud.gateway.routes.default-filters).
🟡

Feign client call fails with RetryableException

Immediate ActionCheck the target service's load and thread pool. Likely the service is overwhelmed.
Commands
curl http://<service-ip>:<port>/actuator/metrics/jvm.threads.live
curl http://<service-ip>:<port>/actuator/health | jq .components.db.status
Fix NowIncrease server.tomcat.threads.max on the downstream service. Add a circuit breaker around the Feign call.
Production Incident

The Silent Gateway Drop: When Eureka Says UP but Traffic Says DOWN

A payment service that was 'healthy' in Eureka but actually dead, causing 50% checkout failures for 20 minutes before anyone noticed.
SymptomIntermittent HTTP 502 errors from the API Gateway to the payment service. Clients saw random payment failures. Eureka dashboard showed both payment-service instances as UP (green).
AssumptionThe load balancer was misconfigured or the gateway had a bug.
Root causeThe payment service's actuaator/health endpoint returned 200 OK even when the database connection pool was exhausted. Eureka's healthcheck only polls the default health endpoint — it doesn't validate the service can process requests. The two instances were alive but unable to handle any real transactions.
FixConfigured a custom health indicator in the payment service that checked both DB connectivity and pool availability. Then set Eureka's healthcheck to use that custom endpoint. Also added a gateway-level circuit breaker for the payment route (Resilience4j time limiter + circuit breaker) so the gateway would stop routing after timeouts.
Key Lesson
Eureka health =/= service health. Always use custom health indicators that test real dependencies.Gateway-level circuit breakers are cheaper than service-level ones — they protect the entry point first.Monitor actual success rates, not just UP/DOWN status.
Production Debug Guide

Symptom → Action matrix for common microservices problems

Service A cannot resolve service B by name (UnknownHostException)Check Eureka dashboard: is service B registered? Verify both services are on the same Eureka server. Also confirm the spring.application.name matches the name used in @FeignClient.
Gateway returns 503 Service Unavailable intermittentlyCheck the gateway's routing configuration (application.yml). Run curl against the service directly (bypass gateway) to isolate the issue. Look at the gateway logs for routing failures.
Circuit breaker opens permanentlyInspect the downstream service's response times and error rates. Check Resilience4j metrics via /actuator/health. Usually the root cause is a slow or crashing dependency; fix that first.
Distributed tracing shows a request takes 10s across services but each service reports <100msLook at the gaps between spans. The delay is likely serialization, network congestion, or thread pool exhaustion at the client side. Enable Zipkin Kafka collector to reduce span reporting overhead.

Microservices with Spring Boot and Spring Cloud is a fundamental concept in modern Java development. Moving away from the 'Monolithic' architecture—where every feature lives in a single code base—to a distributed system allows for independent scaling, faster deployment cycles, and technological flexibility. However, it introduces the 'Distributed System Tax': complexity in networking, security, and data consistency.

In this guide, we'll break down exactly what Microservices with Spring Boot and Spring Cloud is, why it was designed this way, and how to use it correctly in real projects by leveraging Service Discovery, Configuration Management, and API Gateways. We focus on the Spring Cloud 2023.x release train (Leyton), ensuring your stack is ready for modern cloud environments.

By the end, you'll have both the conceptual understanding and practical code examples to use Microservices with Spring Boot and Spring Cloud with confidence, moving from a single JAR to a resilient, interconnected ecosystem.

What Is Microservices with Spring Boot and Spring Cloud and Why Does It Exist?

Microservices with Spring Boot and Spring Cloud is a core feature-set for building resilient distributed systems. While Spring Boot makes it trivial to create a standalone 'service' (like an Order Service or User Service), Spring Cloud provides the glue. It solves the problem of 'Service Discovery' (how Service A finds Service B without hardcoding IP addresses) and 'Circuit Breaking' (how to stop a failure in one service from cascading and crashing your entire ecosystem).

It exists because managing 50+ independent services manually is impossible; you need an automated infrastructure to handle the overhead. By using declarative tools like OpenFeign, Service A can call Service B just by using its name, while Eureka handles the dynamic IP mapping in the background.

io/thecodeforge/orderservice/OrderServiceApplication.java · JAVA
1234567891011121314151617181920212223242526272829303132333435363738
package io.thecodeforge.orderservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * io.thecodeforge Standard: High-Availability Microservice Setup
 * @EnableDiscoveryClient: Registers this service with Eureka/Consul so others can find it.
 * @EnableFeignClients: Enables declarative REST clients to call other services via name.
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// --- Declarative Client Example ---
package io.thecodeforge.orderservice.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * Using Feign for type-safe inter-service communication.
 * Hardcoded URLs are replaced by logical service names.
 */
@FeignClient(name = "inventory-service")
public interface InventoryClient {
    @GetMapping("/api/inventory/{skuCode}")
    boolean isInStock(@PathVariable("skuCode") String skuCode);
}
▶ Output
INFO: Registering application ORDER-SERVICE with eureka with status UP
INFO: Feign Client 'inventory-service' initialized for OrderService.
💡Key Insight:
The most important thing to understand about Microservices with Spring Boot and Spring Cloud is the problem it was designed to solve. Always ask 'why does this exist?' before asking 'how do I use it?' In this case, it's about managing distribution complexity and ensuring high availability through abstraction.

Common Mistakes and How to Avoid Them

When learning Microservices with Spring Boot and Spring Cloud, most developers hit the same set of gotchas. A major one is the 'Distributed Monolith,' where services are so tightly coupled that you can't update one without updating them all.

Another is neglecting 'Observability'—if a request fails across four different services, how do you trace it? Using tools like Micrometer Tracing (the successor to Sleuth) and Zipkin is non-negotiable for production debugging. Furthermore, failing to implement Circuit Breakers means that if your Payment Service hangs, your entire Checkout process will also hang until the connection times out.

src/main/resources/application.yml · YAML
12345678910111213141516171819202122232425
# Centralized Config Example via Spring Cloud Config
spring:
  application:
    name: order-service
  config:
    import: "optional:configserver:http://forge-config-server:8888"
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://order-service
          predicates:
            - Path=/api/order/**

# Production-Grade Resilience4j Circuit Breaker Config
resilience4j.circuitbreaker:
  instances:
    inventoryService:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      waitDurationInOpenState: 10s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
▶ Output
// Route configured: Requests to /api/order/** reach order-service instances via Load Balancer.
// Resilience4j: Circuit breaker 'inventoryService' is monitoring service health.
⚠ Watch Out:
The most common mistake with Microservices with Spring Boot and Spring Cloud is using it when a simpler alternative would work better. Always consider whether the added complexity is justified. Don't build microservices for a 2-person startup unless you expect massive, immediate scale. The operational cost is high.

Configuring Service Discovery with Eureka and Load Balancer

Service Discovery is the backbone of microservices communication. Without it, you're hardcoding IPs and ports, which breaks the moment you scale or redeploy. Netflix Eureka is the battle-tested service registry. Each service registers with its instance ID, hostname, port, and health status. The Spring Cloud LoadBalancer (replacement for Netflix Ribbon) picks a healthy instance using round-robin or custom rules.

Here's the catch: Eureka uses a heartbeat mechanism (30s by default). If a service misses three heartbeats, Eureka removes it. But a service can be 'UP' in Eureka while being completely broken — that's why custom health indicators are critical. Always implement a health endpoint that checks database, message queue, and external API availability.

Another pitfall: self-preservation mode. If Eureka loses too many heartbeats network-wide (e.g., due to a transient network partition), it stops evicting services to protect against false removals. That means your gateway might route to dead instances for minutes. Tune eureka.server.renewalPercentThreshold based on your cluster size.

io/thecodeforge/gateway/GatewayConfig.java · JAVA
1234567891011121314151617181920212223242526
package io.thecodeforge.gateway;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class GatewayConfig {

    @Bean
    @LoadBalanced
    public RestTemplate loadBalancedRestTemplate() {
        return new RestTemplate();
    }
}

// In application.yml:
// spring:
//   cloud:
//     gateway:
//       routes:
//         - id: inventory-route
//           uri: lb://inventory-service
//           predicates:
//             - Path=/api/inventory/**
▶ Output
// LoadBalanced RestTemplate resolves service names to actual instances.
// Gateway routes to 'lb://inventory-service' using client-side load balancing.
Mental Model
How Eureka Works Under the Hood
Eureka is a gossip-based registry — each service tells Eureka 'I'm alive' every 30 seconds, and Eureka tells everyone else where the service is.
  • Registration: Service sends POST /eureka/apps/INSTANCE with metadata (host, port, status).
  • Renewal: Service sends PUT /eureka/apps/APP/INSTANCE every 30s (heartbeat).
  • Eviction: Eureka removes instances that miss 3 consecutive heartbeats (90s window).
  • Self-preservation: If heartbeats drop >15% network-wide, Eureka stops evictions to avoid false negatives during network partitions.
📊 Production Insight
Eureka self-preservation can mask real outages for minutes.
In a production incident, a network hiccup caused 20% heartbeat loss. Eureka entered self-preservation, kept routing to dead instances. The fix: tune eureka.server.renewalPercentThreshold to 0.75 and implement proactive health checks.
Rule: never rely solely on Eureka status — combine with client-side circuit breakers.
🎯 Key Takeaway
Service Discovery removes hardcoded URLs but introduces heartbeat latency (30s) and self-preservation risks.
Always pair Eureka with custom health indicators and circuit breakers.
Failure to do so = routing traffic to dead services.

API Gateway: The Front Door to Your Microservices

An API Gateway is a single entry point that handles cross-cutting concerns: authentication, rate limiting, request/response transformation, and routing. Spring Cloud Gateway is the current standard — it's reactive (built on WebFlux) and non-blocking, meaning it can handle thousands of concurrent requests with minimal threads.

Why not a plain load balancer? A load balancer distributes traffic but doesn't understand HTTP semantics. A gateway can inspect paths, rewrite URLs, add headers, enforce rate limits per client, and handle CORS. It's also the perfect place to implement token validation (JWT) before requests reach your services, reducing duplicate auth logic.

Key configuration pitfall: route order matters. The gateway evaluates routes in the order they're defined. A generic catch-all route before a specific one will shadow all requests. Always declare specific routes first, then a fallback.

Performance note: Spring Cloud Gateway adds ~5-10ms per request in most cases. If you need sub-millisecond latency, consider running a dedicated sidecar proxy like Envoy instead. But for 95% of applications, the gateway is fine.

io/thecodeforge/gateway/src/main/resources/application.yml · YAML
12345678910111213141516171819202122232425
# Spring Cloud Gateway Configuration
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1
            - RemoveRequestHeader=Cookie
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - name: CircuitBreaker
              args:
                name: orderCircuitBreaker
                fallbackUri: forward:/fallback/orders
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
▶ Output
// Routes defined in order of priority.
// Circuit breaker with fallback for order routes.
// Rate limiter: 10 requests/second per user (Redis-backed).
⚠ Gateway Anti-Pattern
Don't put business logic in the gateway. It should only route, filter, and transform. Business logic belongs in the services. Violating this makes the gateway a distributed monolith bottleneck and impossible to maintain.
📊 Production Insight
In production, a misconfigured rate limiter can silently drop legitimate traffic. We had a rate limiter set to 5 req/s per client, but the frontend sent 6 requests in one second on page load. 17% of initial page loads failed with 429. The fix: use a burst capacity 2x the replenish rate and add a Retry-After header.
Rule: always test rate limit scenarios with real user traffic patterns.
🎯 Key Takeaway
An API Gateway centralises auth, rate limiting, and routing — but adds 5-10ms latency and requires careful route ordering.
Without it, every service duplicates cross-cutting logic.
With it, you risk overloading the gateway if not tuned for throughput.

Resilience: Circuit Breakers, Retries, and Bulkheads

In a distributed system, failures are not exceptions — they're the default. Resilience4j is the de facto library for Spring Boot applications. It provides three core patterns: - Circuit Breaker: monitors failures and opens the circuit when a threshold is hit, preventing further calls to a failing service. After a wait duration, it half-opens and tests the water. - Retry: automatically retries failed calls with exponential backoff, but must be used with an idempotent API (e.g., GET, PUT with idempotency key). - Bulkhead: limits concurrent calls to a service, preventing resource exhaustion from spilling over.

The biggest production mistake: configuring retry without a circuit breaker. If the downstream service is down, retries just amplify the load and delay the failure. Retries are for transient errors (timeouts, 503s), not for persistent failures.

Another trap: thread pool vs semaphore isolation. Resilience4j offers both. Thread pool isolation creates a separate thread pool per circuit breaker (isolated but resource-heavy). Semaphore isolation is lightweight but shares threads with the caller — a blocking downstream can still starve your application. Prefer thread pool isolation for critical paths.

Metrics to monitor: resilience4j.circuitbreaker.state.{name} and resilience4j.circuitbreaker.calls.{name} in Micrometer. Alert on OPEN state lasting more than 5 minutes.

src/main/resources/application.yml · YAML
12345678910111213141516171819202122232425
# Resilience4j circuit breaker with retry and bulkhead
resilience4j.circuitbreaker:
  instances:
    inventoryService:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      waitDurationInOpenState: 10s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10

resilience4j.retry:
  instances:
    inventoryService:
      maxRetryAttempts: 3
      waitDuration: 500ms
      retryExceptions:
        - org.springframework.web.client.HttpServerErrorException

resilience4j.bulkhead:
  instances:
    inventoryService:
      maxConcurrentCalls: 5
      maxWaitDuration: 100ms
▶ Output
// Circuit breaker opens after 50% failure rate in 10 calls.
// Retries only on 5xx errors, not 4xx.
// Bulkhead limits concurrent calls to 5, with 100ms max wait.
💡Resilience4j vs Hystrix
Hystrix is in maintenance mode since 2018. Resilience4j is the active successor: no Netflix dependencies, modular, works with Spring Cloud 2023.x, and supports reactive types out of the box. Migrate away from Hystrix if you haven't already.
📊 Production Insight
We had a payment service that was slow but not failing (2s average response). The circuit breaker didn't open because failure rate was <50%. But it caused thread pool exhaustion across three upstream services. The fix: add a time limiter (resilience4j.timelimiter) to cap the response time at 500ms and treat timeout as a failure in the circuit breaker.
Rule: always pair circuit breakers with time limiters to handle slow responses, not just errors.
🎯 Key Takeaway
Resilience4j offers circuit breaker, retry, bulkhead, and time limiter. They must be combined, not used in isolation.
Retry without circuit breaker amplifies failure load.
Time limiters convert slow responses into fast failures the circuit breaker can detect.
🗂 Monolith vs Microservices
Key trade-offs for architecture decisions
AspectMonolithic ArchitectureMicroservices (Spring Cloud)
DeploymentSingle unit; all or nothing deployment.Independent units; deploy features separately.
ScalingScale the whole app (Vertical/Horizontal).Scale only the bottleneck service (Granular).
Fault ToleranceOne bug can crash the entire process.Circuit breakers isolate failures to one service.
Tech StackLocked into one language/framework.Polyglot-friendly; use the best tool for each service.
Data ManagementSingle shared Database (Strong Consistency).Database per Service (Eventual Consistency).

🎯 Key Takeaways

  • Microservices with Spring Boot and Spring Cloud is a core concept for building distributed systems that are resilient and scalable.
  • Spring Boot builds the 'what' (the logic), while Spring Cloud manages the 'how' (the communication and coordination).
  • Service Discovery (Eureka) and API Gateways (Spring Cloud Gateway) are the entry-level requirements for any microservices architecture.
  • Never skip observability; distributed tracing (Micrometer) and centralized logging (ELK/Prometheus) are your only hope when debugging a request across multiple service boundaries.
  • Adopt the 'Database per Service' rule to ensure truly independent deployments.

⚠ Common Mistakes to Avoid

    Overusing Microservices when a monolith suffices
    Symptom

    Team spends months building distributed system for a simple CRUD app, leading to operational overhead and slower iteration.

    Fix

    Start with a monolith. Extract microservices only when you hit clear scaling bottlenecks or team coordination issues.

    Hardcoding Service URLs
    Symptom

    After auto-scaling, new instances get new IPs. Hardcoded URLs cause connection failures and manual configuration updates.

    Fix

    Use a Service Registry (Eureka) and client-side load balancing. Define Feign clients with service names, not IPs.

    Ignoring Cascading Failures — No Circuit Breakers
    Symptom

    A slow payment service holds up all upstream threads. The entire checkout flow becomes unresponsive under load.

    Fix

    Implement circuit breakers on all inter-service calls. Configure time limiters to convert slow responses into fast failures.

    Sharing a Single Database Across Services
    Symptom

    Changes to the shared schema require coordinated deployments across multiple services, negating independent deployment.

    Fix

    Adopt Database per Service pattern. Use event-driven communication (Kafka/RabbitMQ) for eventual consistency.

Interview Questions on This Topic

  • QExplain the 'Service Registry' pattern. How does Eureka distinguish between a service being 'Down' vs a 'Network Partition'?SeniorReveal
    The Service Registry pattern decouples service consumers from providers. Services register with their IP/port on startup and periodically send heartbeats. Eureka uses a 'self-preservation' mode: if heartbeats drop below a threshold network-wide, it stops evicting instances, assuming a network partition rather than actual failures. This prevents false positives during network issues. To distinguish: check if only one service instance stops heartbeating (likely down) vs many instances simultaneously (likely partition). Also use custom health indicators that test real dependencies, not just the default health endpoint.
  • QWhat is the role of an API Gateway (like Spring Cloud Gateway) regarding cross-cutting concerns like Authentication and Rate Limiting?Mid-levelReveal
    The API Gateway acts as the single entry point and enforces cross-cutting policies before requests reach backend services. For authentication, the gateway validates JWT tokens, extracts user context, and forwards it via headers. For rate limiting, it tracks per-client usage (often with Redis) and returns 429 when limits exceed. This avoids duplicating auth/rate limit logic in every service. The gateway can also transform request/response formats, route to different service versions, and implement circuit breakers at the edge. Key trade-off: the gateway adds ~5-10ms latency and can become a bottleneck if not scaled.
  • QHow do you ensure data consistency across multiple microservices without using distributed transactions (2PC)? Discuss the SAGA Pattern.SeniorReveal
    The SAGA pattern coordinates a sequence of local transactions across services, with compensating transactions to undo failures. There are two implementation styles: choreography (each service publishes events and triggers the next step) and orchestration (a central coordinator tells each service what to do). For example, in an order saga: Order Service creates an order (pending), publishes 'OrderCreated' event. Payment Service listens, processes payment, publishes 'PaymentCompleted'. Inventory Service reserves stock, publishes 'InventoryReserved'. If inventory fails, Payment Service must execute a compensating transaction (refund). Tools like Axon, Eventuate, or state machines (e.g., Spring Statemachine) help implement sagas correctly. Key challenges: idempotency (handling duplicate events), monitoring of in-flight sagas, and handling timeouts.
  • QExplain the 'Circuit Breaker' states (Closed, Open, Half-Open) in Resilience4j and how they protect a Spring Boot system.Mid-levelReveal
    Resilience4j circuit breaker has three states: - Closed: Normal operation. Calls pass through. A sliding window counts failures and successes. If failure rate exceeds the threshold (e.g., >50% in last 10 calls), it transitions to Open. - Open: Calls are rejected immediately with a CircuitBreakerOpenException. After a configured wait duration (e.g., 10s), it transitions to Half-Open. - Half-Open: A test request is allowed. If it succeeds, the circuit closes. If it fails, it goes back to Open and the wait duration restarts. This prevents cascading failures by failing fast when a downstream service is unhealthy, giving it time to recover. In Spring Boot, integrate with @CircuitBreaker annotation and expose metrics via Micrometer for monitoring.
  • QWhat is 'Client-Side Load Balancing' (LoadBalancer library) and how does it differ from a traditional Hardware Load Balancer?SeniorReveal
    Client-side load balancing distributes requests from the client (service) to multiple instances without a central L7 load balancer. The service retrieves the list of instances from a service registry (Eureka) and picks one using a rule (round-robin, random, least connections). Spring Cloud LoadBalancer (the new default, replacing Ribbon) caches this list and refreshes it periodically. Difference from hardware load balancer (e.g., F5, ALB): - Client-side: no extra network hop, lower latency, simpler infrastructure, but every client must implement load balancing logic. - Server-side: centralised, easier to manage, supports advanced features (sticky sessions, health checks at TCP/HTTP level), but adds latency and cost. In practice, many teams combine both: use a cloud load balancer (ALB/NLB) in front of the gateway, and client-side load balancing between internal services.

Frequently Asked Questions

Do I need both Eureka and an API Gateway?

Yes, they serve different purposes. Eureka handles service registration and discovery (internal service-to-service communication). The API Gateway is the external entry point that routes client requests to services using Eureka's registry. You can skip the gateway for internal-only services, but for any external-facing system, the gateway is essential for security and cross-cutting concerns.

Can I use Consul instead of Eureka?

Absolutely. Spring Cloud supports Consul, ZooKeeper, and Kubernetes-native service discovery. Consul adds key-value store and health checking beyond Eureka. Switch by changing the dependency from spring-cloud-starter-netflix-eureka-client to spring-cloud-starter-consul-discovery. The Feign and Gateway integration works the same way.

How many services is too many for Spring Cloud?

Spring Cloud can scale to hundreds of services on a single Eureka server if properly tuned. But at serious scale (thousands of instances), you'll need to move to a service mesh (Istio, Linkerd) and use Spring Cloud primarily for business logic. Monitor Eureka's heartbeat load and consider partitioning services into multiple Eureka servers or use Kubernetes-native discovery for larger clusters.

Should I share configuration across services via Spring Cloud Config?

Yes, for non-sensitive configuration. Use a Git-backed config server for versioning and audit trails. But never store secrets (passwords, API keys) in the config repo — use a vault (HashiCorp Vault, AWS Secrets Manager) and integrate via spring-cloud-config's encryption or the bootstrap.properties approach. Spring Cloud Config is optional; many teams manage config via environment variables in containers.

What's the best way to handle distributed tracing?

Use Micrometer Tracing (successor to Spring Cloud Sleuth) with Zipkin for trace collection and Jaeger for visualisation. Add spring-cloud-starter-sleuth (or micrometer-tracing-bridge-otel for OpenTelemetry) and configure a sampler (e.g., 10% of requests in production). Tune the span reporting endpoint: use Kafka as a buffer instead of direct HTTP to avoid blocking the application when Zipkin is slow. Without distributed tracing, debugging a request crossing 5+ services is nearly impossible.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousSpring Boot with DockerNext →Spring Boot Caching with Redis
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged