Mastering Eureka: Service Registration & Discovery for Resilient Microservices
This guide explains why learning the classic Eureka service registry remains valuable, compares architectures with and without Eureka, and provides step‑by‑step code for both non‑clustered and clustered setups, including self‑protection mode, load‑balanced clients, and how to retrieve registration information via DiscoveryClient.
Service Registration/Discovery – Eureka
Although Nacos is the mainstream service registry, the classic Eureka remains worth learning for several reasons: early microservice projects used it, and many later solutions are inspired by its design, making the transition to Nacos easier.
Some early distributed microservice projects used Eureka, so you may encounter it in real work.
Later service‑registry technologies reference Eureka’s design; understanding Eureka simplifies learning Nacos and deepens comprehension.
Introducing Eureka
Without Eureka, a typical architecture is:
Service Consumer →
RestTemplate(direct call) → Service Provider
Example controller without Eureka:
@RestController
@RequestMapping("/member/consumer")
@Slf4j
public class MemberConsumerController {
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://localhost:10001";
@Autowired
private RestTemplate restTemplate;
@PostMapping("/save")
public Result<Member> save(Member member) {
return restTemplate.postForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
@GetMapping("/get/{id}")
public Result<Member> getMemberById(@PathVariable("id") Long id) {
return restTemplate.getForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/get/" + id, Result.class);
}
}Drawbacks of this simple structure:
High concurrency in enterprise projects can overwhelm a single consumer.
A single provider leads to poor availability.
Providers are usually deployed as a cluster, so multiple service instances exist.
The consumer cannot hard‑code URLs; it needs dynamic discovery.
Even after discovery, the consumer must decide which instance to call, introducing load‑balancing concerns.
Eureka solves all these problems.
Project After Introducing Eureka
With Eureka, the architecture becomes:
Service Provider (cluster) registers/renews/cancels with Eureka Server (cluster).
Service Consumer registers itself, discovers services via Eureka Server, then performs remote calls.
Analysis:
The provider is deployed as a cluster for high availability.
Eureka Server can also be clustered to avoid a single point of failure.
Eureka consists of two components: Eureka Server and Eureka Client.
Eureka Server stores registration information of all healthy instances, visible via its UI.
Eureka Client registers with the server, sends heartbeats (default 30 s), and includes a built‑in round‑robin load balancer; missing heartbeats (default 90 s) cause the server to remove the instance.
Service Governance
Eureka implements service governance.
Traditional RPC frameworks make managing inter‑service dependencies complex.
Service governance provides registration, discovery, load balancing, and fault tolerance.
Service Registration and Discovery
Eureka follows a client‑server design; the server acts as the registry.
Microservices connect to Eureka Server, maintain heartbeats, and are monitored for health.
When a service starts, it registers its address and metadata under an alias.
Consumers retrieve the actual address via the alias and invoke the service.
Eureka Server (non‑cluster) Implementation
pom.xml dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>application.yml configuration:
# Server port
server:
port: 9001
# Eureka Server settings
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/Main class:
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}Eureka Client (non‑cluster) Implementation
pom.xml dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>application.yml configuration:
# Server port
server:
port: 10001
spring:
application:
name: member-service-provider-10001
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: zy
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.zy88.springcloud.entity
# Eureka client settings
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:9001/eurekaMain class:
@EnableEurekaClient
@SpringBootApplication
public class MemberApplication {
public static void main(String[] args) {
SpringApplication.run(MemberApplication.class, args);
}
}Eureka Self‑Protection Mode
By default, Eureka Client sends heartbeats; if the server does not receive a heartbeat for 90 s, it removes the instance. When many heartbeats are missed (e.g., network issues), the server enables self‑protection to keep all instances registered, favoring availability over strict accuracy. This is an AP‑style CAP trade‑off.
To disable self‑protection (not recommended for production):
eureka:
server:
enable-self-preservation: falseEureka Server (cluster) Implementation
Why a cluster?
Microservice RPC needs high availability.
A single registry failure makes the whole system unavailable.
Running multiple Eureka servers provides load balancing and fault tolerance.
application.yml for a clustered server (instance 9001):
server:
port: 9001
# Eureka Server settings
eureka:
instance:
hostname: eureka9001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka9002.com:9002/eureka/Update the hosts file (e.g., C:\Windows\System32\drivers\etc\hosts) to map the hostnames to IPs.
Eureka Client – Provider (cluster) Implementation
Modify the provider’s
application.ymlto register with both servers:
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka9001.com:9001/eureka,http://eureka9002.com:9002/eurekaEnsure all instances share the same
spring.application.name(e.g.,
member-service-provider) for unified discovery.
Eureka Client – Consumer (cluster) Implementation
Update the consumer controller to use the service alias instead of a hard‑coded URL:
@RestController
@RequestMapping("/member/consumer")
@Slf4j
public class MemberConsumerController {
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
@Autowired
private RestTemplate restTemplate;
@PostMapping("/save")
public Result<Member> save(Member member) {
return restTemplate.postForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);
}
@GetMapping("/get/{id}")
public Result<Member> getMemberById(@PathVariable("id") Long id) {
return restTemplate.getForObject(MEMBER_SERVICE_PROVIDER_URL + "/member/get/" + id, Result.class);
}
}Add a load‑balanced
RestTemplatebean so that Eureka/Ribbon can choose an instance automatically:
@Configuration
public class CustomizationBean {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}Notes:
The @LoadBalanced annotation enables Ribbon’s client‑side load balancing (default round‑robin, configurable).
With Ribbon integrated, the consumer calls the service name without worrying about host or port.
DiscoveryClient – Retrieving Eureka Registration Information
Enable discovery with
@EnableDiscoveryClientand inject
DiscoveryClientto list services and instances:
@EnableDiscoveryClient
@RestController
@RequestMapping("/member/consumer")
@Slf4j
public class MemberConsumerController {
public static final String MEMBER_SERVICE_PROVIDER_URL = "http://MEMBER-SERVICE-PROVIDER";
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/discovery")
public Object discovery() {
List<String> services = discoveryClient.getServices();
for (String service : services) {
log.info("Service name = {}", service);
List<ServiceInstance> instances = discoveryClient.getInstances(service);
for (ServiceInstance instance : instances) {
log.info("id= {}, host= {}, port= {}, uri= {}", instance.getServiceId(), instance.getHost(), instance.getPort(), instance.getUri());
}
}
return discoveryClient;
}
// save and get methods omitted for brevity
}Access
http://localhost/member/consumer/discoveryto view the registration details, as shown in the screenshots below:
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.