π Netflix Eureka Explained: The Secret Behind Microservice Communication
Learn Service Discovery, Heartbeats, Health Checks, Load Balancing, and Self-Preservation Mode with Real-World Spring Boot Examples

Microservices solve many problems that monolithic applications struggle with, but they introduce a new challenge
How do services find each other?
When your Rating Service needs to talk to the Hotel Service, it has to know exactly where to find it. You can get away with hardcoding URLs in tiny projects, but as your system grows, that turns into a massive headache. Netflix Eureka was built to cure exactly that headache.
So What Was The Problem Before Eureka ?
Imagine a simple microservices architecture:
User Service β localhost:8081
Hotel Service β localhost:8082
Rating Service β localhost:8083
If Rating Service wants to call Hotel Service, a common approach is:
@FeignClient(url = "http://localhost:8082")
This works initially but creates several problems at scale .
What If Port Changes?
If Hotel Service moves from:
8082 β 9090
every service that depends on it must be updated.
Production Environments
In production, services run on different machines and Port mapping are dynamic :
Hotel Service β 10.10.1.4
Rating Service β 10.10.1.11
Hardcoding IP addresses becomes difficult to maintain as well as scale.
What happens when multiple instances are involved ?
To handle traffic, organizations often run multiple instances of the same service:
Hotel Service Instance 1 β 10.10.1.4
Hotel Service Instance 2 β 10.10.1.5
Hotel Service Instance 3 β 10.10.1.6
Now the question becomes:
Which instance should the client call?
Here Comes Netflix Eureka
To fix this, Netflix created Eureka, a Service Discovery Server. You can picture it as a dynamic, automated phonebook for your microservices.
So, instead of having to memorize...
10.10.1.4:8082
services remember:
HOTEL-SERVICE
Eureka handles the mapping between service names and actual network locations.
Eureka Architecture
Without Eureka:
Rating Service --- localhost:8082
The dependency is hardcoded.
With Eureka:
Every service registers itself with Eureka and can discover other services through it.
Eureka Is Like DNS for Microservices
When you visit:
google.com
you don't remember Google's IP address. DNS translates:
google.com --- 142.250.193.78
Similarly, Eureka translates:
HOTEL-SERVICE --- 10.1.5.44:8080
This allows services to communicate using logical names rather than physical addresses.
How Service Registration Works
When a microservice starts, it registers itself with Eureka.
Suppose Hotel Service starts.
Step 1: Read Service Name
spring:
application:
name: HOTEL-SERVICE
Step 2: Locate Eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
Step 3: Register
The service sends a registration request containing:
{
"serviceName": "HOTEL-SERVICE",
"host": "localhost",
"port": 8082,
"status": "UP"
}
Step 4: Eureka Stores It
HOTEL-SERVICE
localhost:8082
UP
The service is now available for discovery.
Remember, Eureka Doesn't discover services.
A common misconception is that Eureka searches for services.
No, it doesn't. Services find Eureka and tell it:
"I am HOTEL-SERVICE"
"I am running on port 8082"
Eureka simply stores this information inside its registry.
Let's take an inside look at how the registry looks from its core:
Map<String, List<ServiceInstance>>
Example:
{
"HOTEL-SERVICE": [
instance1,
instance2,
instance3
],
"USER-SERVICE": [
instance1
]
}
Service Discovery in Action
Now suppose Rating Service needs to call Hotel Service.
Instead of using a URL:
@FeignClient(name = "HOTEL-SERVICE")
Note: Feing Client is a declarative REST client
Feign performs the following steps:
Ask Spring Cloud LoadBalancer.
LoadBalancer asks Eureka.
Eureka returns available instances.
LoadBalancer selects one instance.
The request is sent.
The consumer never needs to know the actual IP address.
Eureka and Load Balancing
Assume Hotel Service has three instances:
8082 -- 8083 -- 8084
Eureka stores all of them.
Requests may be distributed as:
Request 1 β 8082 --- Request 2 β 8083
Request 3 β 8084 --- Request 4 β 8082
This enables client-side load balancing and allows applications to scale horizontally.
Heartbeats and Lease Renewal
Registration alone isn't enough. What happens if a service registers and then crashes?
Eureka would still believe it exists. To solve this, services periodically send Heartbeats.
A heartbeat simply means:
"I'm still alive."
By default:
eureka:
instance:
lease-renewal-interval-in-seconds: 30
A heartbeat is sent every 30 seconds.
Lease Expiration
Eureka also defines how long it should wait before considering a service dead.
eureka:
instance:
lease-expiration-duration-in-seconds: 90
If no heartbeat is received for 90 seconds, Eureka removes the instance from its registry.
A common production rule is:
Lease Expiration β 3 Γ Heartbeat Interval
Self-Preservation Mode
One of Eureka's most important features is Self-Preservation Mode.
Imagine 1,000 services suddenly stop sending heartbeats.
Did all services crash?. Probably not.
More likely: Network Issue
If Eureka immediately removed all services, the entire system could become unavailable.
Instead, Eureka enters Self-Preservation Mode and temporarily keeps registry entries until it can determine whether the problem is actually service failure or just a networking issue.This protects distributed systems from large-scale outages.
Health Checks vs Heartbeats
Many developers confuse these concepts.
Heartbeat
Answers:
Is the service process alive?
Health Check
Answers:
Is the service actually healthy?
A service can still be running while its database is completely unavailable.
In that case:
Heartbeat = Alive
Health = Unhealthy
Integrating Spring Boot Actuator
Spring Boot Actuator provides health information through:
/actuator/health
Example response:
{
"status": "UP"
}
To allow Eureka to use health information:
eureka:
client:
healthcheck:
enabled: true
Now Eureka considers the actual application health instead of only checking whether the process is running.
Custom Health Indicators
Real-world applications depend on resources such as the following:
- MySQL, Redis, Kafka, External APIs
Custom Health Indicators allow these dependencies to participate in health checks.
@Component
public class DatabaseHealthIndicator
implements HealthIndicator {
@Override
public Health health() {
boolean databaseUp = checkDatabase();
if(databaseUp) {
return Health.up().build();
}
return Health.down().build();
}
}
If the database fails, Actuator reports the following:
{
"status": "DOWN"
}
and Eureka can stop routing traffic to that instance.
Eureka Server Setup
Creating a Eureka Server is surprisingly simple.
Main Class
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(
ServiceRegistryApplication.class,
args
);
}
}
Configuration
server:
port: 8761
spring:
application:
name: SERVICE-REGISTRY
eureka:
client:
register-with-eureka: false
fetch-registry: false
Why These Properties?
register-with-eureka: false
The Eureka Server should not register itself.
fetch-registry: false
The Eureka Server should not fetch a registry from another Eureka Server because it is the registry.
Once you understand service registration, discovery, heartbeats, lease renewal, health checks, and self-preservation mode, Eureka stops being a configuration exercise and becomes a powerful distributed systems concept.




