Módulo 01 — Fundamentos

¿Qué es Eureka y Service Discovery?

Entiende el problema que Eureka resuelve, el patrón Service Discovery y cómo encaja en tu arquitectura de microservicios LingoPlay.

El problema de las IPs dinámicas
Client-side vs Server-side discovery
Arquitectura Eureka Server/Client
Flujo de registro y heartbeat

El problema que Eureka resuelve

En un sistema de microservicios, los servicios se crean, escalan y mueven constantemente. Sus IPs y puertos cambian. ¿Cómo sabe el API Gateway dónde está el Game Service en este momento?

EUREKA SERVER Service Registry :8761 auth-service 10.0.0.2:8081 game-service 10.0.0.3:8082 score-service 10.0.0.4:8083 register register register api-gateway :8080 Eureka Client fetch registry ♥ heartbeat cada 30s → Eureka sabe quién está vivo Sin heartbeat por 90s → servicio marcado como DOWN y removido

Client-side vs Server-side Discovery

AspectoClient-side (Eureka + Ribbon/LoadBalancer)Server-side (AWS ALB, Nginx)
¿Quién resuelve?El propio cliente (Gateway, Feign)Un componente externo (proxy/LB)
RegistryEureka ServerGeneralmente integrado al LB
Caché localSí — cada cliente tiene copia del registroNo — el LB centraliza
AcoplamientoCliente debe conocer EurekaCliente solo conoce la URL del LB
EscalabilidadMuy alta — no hay cuello de botellaEl LB puede ser bottleneck
Uso en Spring CloudEureka + Spring Cloud LoadBalancerAWS ELB, Kubernetes Service
💡
Spring Cloud usa Client-side Discovery

Cada cliente (API Gateway, Feign Client) descarga el registro de Eureka y elige la instancia localmente. Esto elimina el cuello de botella del servidor de descubrimiento en las llamadas de producción.

Flujo completo de Eureka

game-service arranca
POST /eureka/apps/GAME-SERVICE
Registrado en el registry
game-service
→ cada 30s →
PUT /eureka/apps/GAME-SERVICE/{id}
Heartbeat OK ♥
api-gateway
→ cada 30s →
GET /eureka/apps
Caché local actualizada
Request cliente
lb://GAME-SERVICE
→ caché local →
10.0.0.3:8082
Mini Quiz — Módulo 1

En el modelo Client-side Discovery de Eureka, ¿quién decide a qué instancia enviar un request cuando hay múltiples instancias de game-service?

A
El Eureka Server — actúa como proxy y redirige el tráfico
B
El cliente (Gateway/Feign) — usa su caché local del registro y el LoadBalancer para elegir
C
La base de datos MySQL — almacena las IPs de los servicios disponibles
D
El API Gateway directamente vía DNS
Módulo 02 — Servidor

Eureka Server — El registro central

Configura el servidor Eureka que actuará como directorio de todos tus microservicios en LingoPlay.

Dependencias y setup
application.yml completo
Dashboard web
Configuración de seguridad

Dependencias Maven

xml — pom.xml Eureka Server
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.0</version> </parent> <dependencies> <!-- Eureka Server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!-- Seguridad opcional para el dashboard --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Actuator para health check --> <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>2023.0.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

Clase principal

Java — EurekaServerApplication.java
@SpringBootApplication @EnableEurekaServer // ← Esta anotación activa el servidor public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

application.yml — Configuración completa

yaml — application.yml Eureka Server
server: port: 8761 spring: application: name: eureka-server security: user: name: ${EUREKA_USER:admin} password: ${EUREKA_PASS:secret} eureka: instance: hostname: localhost client: # El servidor NO se registra a sí mismo registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://localhost:8761/eureka/ server: # Tiempo de espera antes de eliminar instancias sin heartbeat eviction-interval-timer-in-ms: 5000 # Modo de desarrollo: desactiva la protección contra fallos de red # En producción: true (evita que una falla de red borre todos los servicios) enable-self-preservation: false # Umbral para activar self-preservation (85% por defecto) renewal-percent-threshold: 0.85 # Tiempo máximo sin heartbeat antes de expirar (segundos) response-cache-update-interval-ms: 3000 management: endpoints: web: exposure: include: health,info,eureka endpoint: health: show-details: always
⚠️
Self-Preservation Mode — Qué es y cuándo activarlo

En producción con enable-self-preservation: true (default), si Eureka deja de recibir heartbeats de más del 85% de sus clientes de golpe, no los elimina del registro — asume que es un problema de red, no que los servicios murieron. Evita eliminar servicios válidos por un fallo temporal de red. En desarrollo, desactívalo para que los servicios "muertos" se eliminen rápido.

Configurar Spring Security en el Server

Java — EurekaSecurityConfig.java
@Configuration @EnableWebSecurity public class EurekaSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.ignoringRequestMatchers("/eureka/**")) .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .httpBasic(Customizer::withDefaults); return http.build(); } }
💡
Dashboard web en http://localhost:8761

Eureka incluye un dashboard visual donde puedes ver todos los servicios registrados, su estado (UP/DOWN), número de instancias, y la información de cada una. En producción, protégelo con autenticación básica como se muestra arriba.

Mini Quiz — Módulo 2

¿Por qué el Eureka Server tiene registerWithEureka: false y fetchRegistry: false?

A
Por seguridad — evita que otros servicios conozcan la IP del servidor
B
El servidor contiene también un cliente Eureka; estas propiedades le indican que no se registre ni consulte a sí mismo
C
Son propiedades de rendimiento que reducen el uso de memoria del servidor
D
Solo se usan en modo desarrollo; en producción se eliminan
Módulo 03 — Clientes

Eureka Client — Registrar microservicios

Configura cada microservicio de LingoPlay para que se registre en Eureka y participe en el Service Discovery.

Dependencia Eureka Client
application.yml por servicio
Metadata e instancias
Health checks con Actuator

Dependencia del cliente

xml — pom.xml — microservicio cliente
<!-- Eureka Client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- Actuator — para healthcheck en Eureka --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
💡
¡No necesitas @EnableEurekaClient!

Desde Spring Cloud 2020+, con solo tener la dependencia en el classpath el cliente se activa automáticamente. La anotación @EnableEurekaClient o @EnableDiscoveryClient son opcionales (aunque puedes incluirlas para ser explícito).

Configuración de cada microservicio — LingoPlay

yaml — game-service/application.yml
server: port: 8082 # 0 = puerto aleatorio (múltiples instancias) spring: application: name: game-service # ← Nombre con el que se registra en Eureka eureka: client: # URL del Eureka Server (con usuario:password si tiene seguridad) service-url: defaultZone: http://admin:secret@localhost:8761/eureka/ # Cada cuánto el cliente obtiene el registro actualizado de Eureka registry-fetch-interval-seconds: 30 # Habilitar healthcheck via Actuator (más preciso que heartbeat) healthcheck: enabled: true instance: # Usar IP en lugar de hostname (importante en Docker/contenedores) prefer-ip-address: true # Cuánto espera Eureka antes de considerar el servicio muerto sin heartbeat lease-expiration-duration-in-seconds: 30 # Con qué frecuencia envía heartbeat al servidor lease-renewal-interval-in-seconds: 10 # ID único de instancia (útil para múltiples instancias del mismo servicio) instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} # URL del healthcheck (Eureka la consulta para saber si está UP) health-check-url-path: /actuator/health status-page-url-path: /actuator/info # Metadatos personalizados visibles en el dashboard metadata-map: version: "1.0.0" team: "backend" zone: "us-east-1" management: endpoints: web: exposure: include: health,info endpoint: health: show-details: always

Tabla de configuraciones de todos los servicios LingoPlay

Serviciospring.application.nameserver.portRol
Eureka Servereureka-server8761Registry
API Gatewayapi-gateway8080Enrutador + LB
Auth Serviceauth-service8081JWT + OAuth2
Game Servicegame-service8082Lógica de juego
Score Servicescore-service8083Puntuaciones
Admin Serviceadmin-service8084Backoffice

Múltiples instancias — Escalar un servicio

bash — Escalar game-service
# Opción 1: Puerto aleatorio (port: 0 en application.yml) # Cada instancia elige un puerto libre automáticamente java -jar game-service.jar java -jar game-service.jar # Segunda instancia en otro puerto # Opción 2: Puerto explícito java -jar game-service.jar --server.port=8082 java -jar game-service.jar --server.port=8092 java -jar game-service.jar --server.port=8093 # Con Docker Compose: scale docker compose up --scale game-service=3 -d # Las 3 instancias aparecen en Eureka como: # GAME-SERVICE game-service:abc123 10.0.0.3:8082 UP # GAME-SERVICE game-service:def456 10.0.0.3:8092 UP # GAME-SERVICE game-service:ghi789 10.0.0.3:8093 UP # El Gateway distribuye el tráfico entre las 3 automáticamente (round-robin)
Mini Quiz — Módulo 3

Configuras server.port: 0 en game-service. ¿Cuál es el propósito de esta configuración?

A
Desactiva el servicio para que solo funcione internamente
B
El SO asigna un puerto libre aleatorio, permitiendo múltiples instancias sin conflictos
C
Impide que el servicio se registre en Eureka
D
Solo funciona si Eureka está corriendo antes de iniciar el servicio
Módulo 04 — Load Balancing

Spring Cloud LoadBalancer con Eureka

Aprende cómo el LoadBalancer distribuye el tráfico entre instancias usando el registro de Eureka, y cómo usar OpenFeign para llamadas entre servicios.

Spring Cloud LoadBalancer
Estrategias round-robin y random
WebClient + lb://
OpenFeign declarativo

Spring Cloud LoadBalancer

Es el reemplazo moderno de Netflix Ribbon (deprecated). Se integra con Eureka para distribuir tráfico entre instancias del mismo servicio.

xml — pom.xml — LoadBalancer
<!-- Incluido automáticamente con spring-cloud-starter-netflix-eureka-client --> <!-- Pero puedes añadirlo explícitamente: --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!-- Para OpenFeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

WebClient con Load Balancer

Java — WebClient balanceado (reactivo)
@Configuration public class WebClientConfig { @Bean @LoadBalanced // ← Habilita resolución lb:// con Eureka public WebClient.Builder webClientBuilder() { return WebClient.builder(); } } @Service public class GameServiceClient { private final WebClient webClient; public GameServiceClient(WebClient.Builder builder) { // lb://game-service → Eureka resuelve a IP:puerto real this.webClient = builder.baseUrl("lb://game-service").build(); } public Mono<JuegoDTO> obtenerJuego(Long id) { return webClient.get() .uri("/juegos/{id}", id) .retrieve() .bodyToMono(JuegoDTO.class); } } // También con RestTemplate (bloqueante): @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }

OpenFeign — La forma declarativa

OpenFeign te permite llamar a otros microservicios como si fueran interfaces Java locales. Integrado con Eureka y LoadBalancer automáticamente.

Java — Feign Client completo
// 1. Activar Feign en la clase principal @SpringBootApplication @EnableFeignClients public class ScoreServiceApplication { ... } // 2. Definir el cliente Feign @FeignClient( name = "game-service", // Nombre en Eureka (lb:// automático) fallback = GameClientFallback.class // Fallback si el servicio falla ) public interface GameServiceClient { @GetMapping("/juegos/{id}") JuegoDTO obtenerJuego(@PathVariable("id") Long id); @PostMapping("/juegos/{id}/iniciar") SesionDTO iniciarJuego( @PathVariable("id") Long id, @RequestBody IniciarJuegoRequest req ); @GetMapping("/juegos") Page<JuegoDTO> listarJuegos( @RequestParam int page, @RequestParam int size ); } // 3. Fallback @Component public class GameClientFallback implements GameServiceClient { @Override public JuegoDTO obtenerJuego(Long id) { // Respuesta por defecto cuando game-service no responde throw new ServiceUnavailableException("Game service no disponible"); } @Override public SesionDTO iniciarJuego(Long id, IniciarJuegoRequest req) { throw new ServiceUnavailableException("No se puede iniciar el juego ahora"); } } // 4. Uso en un servicio @Service public class ScoreService { private final GameServiceClient gameClient; public ScoreDTO calcularScore(Long juegoId, Long usuarioId) { // ¡Feign maneja la llamada HTTP internamente! JuegoDTO juego = gameClient.obtenerJuego(juegoId); return new ScoreDTO(juego.getNombre(), usuarioId, 100); } }

Configuración de Feign

yaml — Feign + timeout
feign: client: config: default: # Aplica a todos los clientes connectTimeout: 3000 readTimeout: 5000 loggerLevel: BASIC # NONE | BASIC | HEADERS | FULL game-service: # Configuración específica por cliente connectTimeout: 1000 readTimeout: 2000 circuitbreaker: enabled: true # Integrar con Resilience4j

Estrategias de Load Balancing

Java — Estrategia personalizada (Random)
// Por defecto: RoundRobinLoadBalancer // Alternativa: RandomLoadBalancer @Configuration @LoadBalancerClient(name = "game-service", configuration = RandomLBConfig.class) public class LoadBalancerConfig {} public class RandomLBConfig { @Bean public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer( Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new RandomLoadBalancer( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name ); } }
Mini Quiz — Módulo 4

¿Cuál es la diferencia principal entre usar @FeignClient y WebClient con @LoadBalanced para llamar a otro microservicio?

A
Feign es declarativo (interfaz Java, sin HTTP manual); WebClient es programático y reactivo (Mono/Flux)
B
Feign no usa Eureka; WebClient sí necesita el registro de Eureka
C
WebClient solo funciona con HTTP/2; Feign solo con HTTP/1.1
D
Solo WebClient soporta timeouts y reintentos
Módulo 05 — Alta Disponibilidad

Eureka en Alta Disponibilidad

Configura un clúster de Eureka con múltiples nodos para eliminar el punto único de fallo en producción.

Clúster de 3 nodos Eureka
Peer Awareness
Configuración por perfil
Comportamiento ante fallos

¿Por qué alta disponibilidad en Eureka?

Un único Eureka Server es un punto único de fallo (SPOF). Si cae, los servicios no pueden registrarse ni descubrir nuevos servicios (aunque los ya cacheados localmente siguen funcionando un tiempo). En producción, siempre usa al menos 2 nodos.

eureka-1
:8761
UP
eureka-2
:8762
UP
eureka-3
:8763
UP

Los 3 nodos se replican el registro entre sí — cualquiera puede caer sin perder disponibilidad

Configuración por perfiles — Clúster de 3 nodos

yaml — application.yml — HA Eureka
spring: application: name: eureka-server --- # Perfil: nodo 1 spring: config: activate: on-profile: peer1 server: port: 8761 eureka: instance: hostname: eureka1 client: registerWithEureka: true # En HA sí se registran entre sí fetchRegistry: true serviceUrl: defaultZone: http://eureka2:8762/eureka/,http://eureka3:8763/eureka/ --- # Perfil: nodo 2 spring: config: activate: on-profile: peer2 server: port: 8762 eureka: instance: hostname: eureka2 client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://eureka1:8761/eureka/,http://eureka3:8763/eureka/ --- # Perfil: nodo 3 spring: config: activate: on-profile: peer3 server: port: 8763 eureka: instance: hostname: eureka3 client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/

Clientes apuntando a todos los nodos

yaml — Microservicio apuntando al clúster
eureka: client: service-url: # Lista de todos los nodos Eureka — si uno cae, usa los otros defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/,http://eureka3:8763/eureka/

Docker Compose para el clúster

yaml — docker-compose HA
services: eureka1: build: ./eureka-server hostname: eureka1 ports: ["8761:8761"] environment: SPRING_PROFILES_ACTIVE: peer1 networks: [eureka-net] eureka2: build: ./eureka-server hostname: eureka2 ports: ["8762:8762"] environment: SPRING_PROFILES_ACTIVE: peer2 networks: [eureka-net] eureka3: build: ./eureka-server hostname: eureka3 ports: ["8763:8763"] environment: SPRING_PROFILES_ACTIVE: peer3 networks: [eureka-net] networks: eureka-net: driver: bridge
Mini Quiz — Módulo 5

Tienes un clúster Eureka de 3 nodos. El nodo eureka2 cae. ¿Qué ocurre con los servicios que estaban registrados?

A
Todos los servicios se dan de baja y deben registrarse de nuevo
B
El sistema deja de funcionar completamente hasta que eureka2 vuelva
C
El sistema sigue funcionando: eureka1 y eureka3 tienen el registro replicado y atienden las consultas
D
eureka1 se convierte automáticamente en líder y eureka3 en respaldo pasivo
Módulo 06 — Contenedores

Eureka en Docker y Perfiles

Configura Eureka correctamente en entornos Docker donde los hostname e IPs son dinámicos, y usa perfiles de Spring Boot para distintos entornos.

prefer-ip-address en Docker
Perfil docker vs local
Variables de entorno
Healthcheck en Compose

El problema de Docker con Eureka

🔥
El error más común con Docker

Por defecto, Eureka registra los servicios con su hostname. Dentro de Docker, el hostname es el ID del contenedor (ej: a3f7b2c1d0e9). Otros servicios no pueden resolver ese hostname. El Gateway recibe esa dirección y falla. Solución: prefer-ip-address: true.

yaml — Configuración correcta para Docker
eureka: instance: # ✅ Usa la IP del contenedor en vez del hostname irresoluble prefer-ip-address: true # Alternativa: usar el nombre del servicio Docker como hostname # hostname: ${spring.application.name} # ID único para cada instancia (evita colisiones al escalar) instance-id: ${spring.application.name}:${server.port}:${random.uuid}

Perfiles: local vs docker

yaml — application.yml con perfiles
# Configuración base (todos los entornos) spring: application: name: game-service server: port: 8082 --- # Perfil: local (desarrollo) spring: config: activate: on-profile: local eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: false lease-renewal-interval-in-seconds: 10 --- # Perfil: docker spring: config: activate: on-profile: docker datasource: url: jdbc:mysql://mysql:3306/lingoplay_db # mysql = nombre del servicio Docker eureka: client: service-url: defaultZone: http://eureka:8761/eureka/ # eureka = nombre del servicio Docker instance: prefer-ip-address: true
yaml — docker-compose.yml — LingoPlay completo
services: eureka: build: ./eureka-server container_name: lingoplay-eureka ports: ["8761:8761"] environment: SPRING_PROFILES_ACTIVE: docker networks: [lingoplay-net] healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8761/actuator/health"] interval: 15s timeout: 5s retries: 5 start_period: 30s api-gateway: build: ./api-gateway ports: ["8080:8080"] environment: SPRING_PROFILES_ACTIVE: docker EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8761/eureka/ depends_on: eureka: condition: service_healthy # Espera a que Eureka esté listo networks: [lingoplay-net] game-service: build: ./game-service environment: SPRING_PROFILES_ACTIVE: docker EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: http://eureka:8761/eureka/ depends_on: eureka: condition: service_healthy mysql: condition: service_healthy networks: [lingoplay-net] deploy: replicas: 2 # 2 instancias del game service networks: lingoplay-net: driver: bridge
Mini Quiz — Módulo 6

En Docker Compose, el game-service tiene defaultZone: http://eureka:8761/eureka/. ¿Por qué eureka (sin IP) en lugar de localhost:8761?

A
Es exactamente igual; eureka y localhost:8761 apuntan al mismo lugar
B
Spring Cloud exige usar nombres de servicio por restricciones de su configuración
C
Docker Compose tiene DNS interno que resuelve el nombre del servicio a la IP del contenedor en la red virtual
D
Por seguridad, Docker bloquea conexiones a localhost entre contenedores
Módulo 07 — Diagnóstico

Diagnóstico, API REST y errores comunes

Aprende a usar la API REST de Eureka, interpretar el dashboard y resolver los problemas más frecuentes.

API REST de Eureka
Dashboard — qué significa cada campo
Errores comunes y soluciones
Logs de diagnóstico

API REST de Eureka

Eureka REST API
$ curl http://localhost:8761/eureka/apps <applications> <application> <name>GAME-SERVICE</name> <instance> <instanceId>game-service:8082:abc123</instanceId> <ipAddr>10.0.0.3</ipAddr> <port enabled="true">8082</port> <status>UP</status> <healthCheckUrl>http://10.0.0.3:8082/actuator/health</healthCheckUrl> </instance> </application> </applications> $ curl http://localhost:8761/eureka/apps/GAME-SERVICE # Solo las instancias de game-service $ curl -H "Accept: application/json" http://localhost:8761/eureka/apps # Respuesta en JSON en lugar de XML $ curl -X DELETE http://localhost:8761/eureka/apps/GAME-SERVICE/game-service:8082:abc123 # Dar de baja una instancia manualmente $ curl -X PUT http://localhost:8761/eureka/apps/GAME-SERVICE/game-service:8082:abc123/status?value=OUT_OF_SERVICE # Poner fuera de servicio sin eliminar (mantenimiento)

Errores más comunes

Connection refused a :8761
CAUSA: Eureka no está corriendo
El microservicio intenta registrarse pero Eureka no está disponible. En Docker: usa depends_on con condition: service_healthy. En local: arranca Eureka primero.
EMERGENCY! EUREKA MAY BE INCORRECT
CAUSA: Self-preservation activado
Eureka recibe menos del 85% de heartbeats esperados y activa el modo de protección. En desarrollo: enable-self-preservation: false. En producción: investigar pérdida de conectividad.
No instances available for game-service
CAUSA: Caché local desactualizada
El cliente tiene una caché vacía o desactualizada de Eureka. Espera 30s (registry-fetch-interval). En Docker verifica que prefer-ip-address: true esté activo.
Servicio en DOWN aunque está corriendo
CAUSA: Healthcheck fallando
Eureka consulta /actuator/health y recibe error (BD caída, dependencia fallida). Revisa que el endpoint Actuator esté expuesto y que todas las dependencias del servicio estén UP.
Hostname del contenedor irresoluble
CAUSA: prefer-ip-address: false en Docker
El Gateway recibe "a3f7b2c1d0e9:8082" que no puede resolver. Solución: prefer-ip-address: true en todos los microservicios cuando corren en Docker.
Instancias duplicadas en el registry
CAUSA: instance-id no único
Al escalar, múltiples instancias tienen el mismo instance-id. Solución: usar random.uuid en el instance-id: ${app.name}:${port}:${random.uuid}.

Activar logs de diagnóstico

yaml — Logs de Eureka
logging: level: com.netflix.eureka: DEBUG com.netflix.discovery: DEBUG org.springframework.cloud.netflix.eureka: DEBUG org.springframework.cloud.loadbalancer: TRACE
Mini Quiz — Módulo 7

El dashboard de Eureka muestra un servicio en estado DOWN. El proceso Java del servicio sigue corriendo. ¿Cuál es la causa más probable?

A
Eureka tiene un bug y marca servicios DOWN erróneamente
B
El servicio no está enviando heartbeats
C
El endpoint /actuator/health reporta DOWN porque una dependencia (BD, otro servicio) está fallando
D
Toda la aplicación está en mantenimiento programado
Módulo 08 — Referencia

Cheat Sheet — Referencia rápida

Todas las propiedades, anotaciones y patrones de Eureka en un solo lugar.

Propiedades del Eureka Server

eureka.client.registerWithEureka
false (en el server)
Evita que el servidor se registre a sí mismo.
eureka.client.fetchRegistry
false (en el server)
Evita que el servidor descargue su propio registro.
eureka.server.enable-self-preservation
true (prod) / false (dev)
Protege el registro en caso de partición de red. Desactiva en desarrollo.
eureka.server.eviction-interval-timer-in-ms
60000 (default)
Cada cuánto Eureka revisa y elimina instancias sin heartbeat.
eureka.server.renewal-percent-threshold
0.85
% mínimo de heartbeats para no activar self-preservation.
eureka.server.response-cache-update-interval-ms
30000
Cada cuánto actualiza la caché de respuestas del servidor.

Propiedades del Eureka Client

eureka.client.service-url.defaultZone
http://localhost:8761/eureka/
URL del Eureka Server. Múltiples separadas por coma para HA.
eureka.client.registry-fetch-interval-seconds
30
Cada cuánto el cliente descarga el registro actualizado.
eureka.client.healthcheck.enabled
true
Usa /actuator/health como indicador de estado (más preciso que solo heartbeat).
eureka.instance.prefer-ip-address
true (Docker) / false (local)
Usa IP en lugar de hostname. Esencial en Docker para que otros servicios puedan conectar.
eureka.instance.lease-renewal-interval-in-seconds
30
Frecuencia del heartbeat. Reduce a 10s en desarrollo para detección rápida.
eureka.instance.lease-expiration-duration-in-seconds
90
Tiempo sin heartbeat antes de que Eureka elimine la instancia.
eureka.instance.instance-id
${app}:${port}:${random.uuid}
ID único por instancia. Esencial al escalar con múltiples réplicas.
eureka.instance.metadata-map.*
clave: valor
Metadatos personalizados visibles en el dashboard y consumibles por otros servicios.

Anotaciones y beans clave

@EnableEurekaServer

Activa el servidor Eureka. Va en la clase principal junto con @SpringBootApplication.

@EnableDiscoveryClient

Activa el cliente Eureka. Opcional desde Spring Cloud 2020 (auto-configurado si la dependencia está en el classpath).

@FeignClient(name = "servicio")

Define un cliente HTTP declarativo que usa Eureka para resolver el nombre del servicio.

@EnableFeignClients

Activa el escaneo de @FeignClient en el contexto. Va en la clase principal.

@LoadBalanced (en WebClient.Builder)

Habilita la resolución lb:// con Spring Cloud LoadBalancer al usar WebClient.

@LoadBalancerClient(name, config)

Configura una estrategia de balanceo personalizada para un servicio específico.

DiscoveryClient

Bean inyectable para consultar el registro de Eureka programáticamente y obtener instancias.

EurekaClient

API específica de Netflix para interactuar directamente con Eureka (más baja que DiscoveryClient).

Checklist de producción

✅ Clúster de al menos 2 nodos

Nunca un solo Eureka en producción. Mínimo 2, ideal 3 nodos en zonas de disponibilidad distintas.

✅ Self-preservation: true

En producción mantén activada la protección contra particiones de red.

✅ Autenticación en el dashboard

Protege el dashboard con Spring Security (HTTP Basic o OAuth2) — expone información interna.

✅ prefer-ip-address: true en Docker

Sin esto, los clientes no pueden conectar a los servicios en entornos containerizados.

✅ healthcheck habilitado

Conecta Eureka con Actuator para un estado de salud preciso basado en dependencias reales.

✅ instance-id con random.uuid

Garantiza IDs únicos al escalar múltiples réplicas del mismo servicio.

✅ depends_on con service_healthy

En Docker Compose, los microservicios deben esperar a que Eureka esté healthy antes de arrancar.

✅ Timeouts en Feign/WebClient

Sin timeouts, una llamada colgada a un servicio lento puede agotar el thread pool completo.

☁️

¡Eureka dominado!

Completaste el curso de Spring Cloud Eureka. Tu arquitectura de microservicios LingoPlay ahora tiene un service discovery robusto y bien configurado.

Certificado de finalización
Spring Cloud Netflix Eureka
8 módulos · Server · Client · LoadBalancer · OpenFeign · HA · Docker
🎉 ☁️ 🚀
🚀 Spring Cloud Config

Centraliza la configuración de todos tus microservicios en un solo repositorio Git.

📊 Micrometer + Zipkin

Distributed tracing para seguir un request a través de todos tus servicios.

⚡ Kubernetes

En K8s, el Service Discovery lo maneja nativamente Kubernetes sin necesitar Eureka.

🔄 Spring Cloud Bus

Propaga cambios de configuración a todos los microservicios en tiempo real via mensajería.