@EnableEurekaClient y registro automático
La conversión de una aplicación Spring Boot en un cliente Eureka es un proceso directo que permite al microservicio registrarse automáticamente en el servidor de descubrimiento. Esta capacidad es fundamental para que los servicios puedan encontrarse entre sí sin necesidad de conocer sus direcciones exactas.
Configuración de dependencias
Para habilitar las funcionalidades de cliente Eureka, es necesario agregar la dependencia específica al proyecto. En aplicaciones basadas en Maven, esta configuración se realiza en el archivo pom.xml
:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Para proyectos que utilizan Gradle, la configuración equivalente en el archivo build.gradle
sería:
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
Registro automático por autoconfiguración
Una característica destacable de Spring Cloud es que el registro automático se activa simplemente por la presencia del starter en el classpath. No es necesario agregar anotaciones específicas en versiones modernas de Spring Cloud, ya que la autoconfiguración se encarga de detectar automáticamente la dependencia y configurar el cliente Eureka.
@SpringBootApplication
@RestController
public class MicroserviceApplication {
@GetMapping("/")
public String home() {
return "Microservicio funcionando correctamente";
}
public static void main(String[] args) {
SpringApplication.run(MicroserviceApplication.class, args);
}
}
La anotación @EnableEurekaClient (opcional)
Aunque el registro automático funciona sin anotaciones adicionales, la anotación **@EnableEurekaClient**
sigue estando disponible para casos específicos donde se requiere mayor control sobre la configuración:
@SpringBootApplication
@EnableEurekaClient
@RestController
public class MicroserviceApplication {
@GetMapping("/info")
public String info() {
return "Microservicio registrado en Eureka";
}
public static void main(String[] args) {
SpringApplication.run(MicroserviceApplication.class, args);
}
}
Esta anotación resulta útil cuando necesitas personalizar comportamientos específicos del cliente Eureka o cuando trabajas con configuraciones más complejas que requieren un control explícito sobre el registro.
Configuración básica del cliente
Para que el registro automático funcione correctamente, es imprescindible proporcionar la configuración mínima en el archivo application.yml
:
spring:
application:
name: producto-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
La propiedad spring.application.name
es fundamental porque define el identificador del servicio que aparecerá en el registro de Eureka. Este nombre será utilizado posteriormente por otros microservicios para localizar y comunicarse con este servicio específico.
Verificación del registro
Una vez que el microservicio se ejecuta con esta configuración, automáticamente se registra en el servidor Eureka especificado. Puedes verificar que el registro se ha realizado correctamente accediendo al dashboard de Eureka en http://localhost:8761
y comprobando que tu servicio aparece listado en la sección "Instances currently registered with Eureka".
El proceso de registro incluye información como:
- Nombre del servicio (definido por
spring.application.name
) - Dirección IP y puerto donde se ejecuta el microservicio
- Estado de salud del servicio
- Metadatos adicionales configurables según las necesidades
Configuración avanzada de instancia
Spring Cloud permite personalizar cómo se registra la instancia del servicio mediante propiedades adicionales:
eureka:
instance:
instanceId: ${spring.application.name}:${server.port}
preferIpAddress: true
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
client:
registryFetchIntervalSeconds: 5
Estas configuraciones optimizan la comunicación con Eureka y mejoran la detección de cambios en el estado de los servicios, aunque los valores por defecto son apropiados para la mayoría de escenarios.
Con esta configuración básica, el microservicio está listo para participar en el ecosistema de descubrimiento de servicios y puede ser localizado por otros componentes de la arquitectura. El siguiente paso natural será utilizar las capacidades de resolución programática de instancias para establecer comunicación entre servicios, aspecto que veremos cómo implementar en la siguiente sección donde exploraremos el uso del DiscoveryClient
.
DiscoveryClient: resolución programática de instancias
Una vez que los microservicios están registrados en Eureka, el siguiente paso fundamental es poder localizarlos programáticamente para establecer comunicación entre ellos. Spring Cloud proporciona la interfaz DiscoveryClient
que actúa como una abstracción para consultar el registro de servicios y obtener información detallada de las instancias disponibles.
Inyección y uso del DiscoveryClient
El DiscoveryClient
se puede inyectar fácilmente en cualquier componente Spring mediante inyección de dependencias. Esta interfaz proporciona métodos estándar para interactuar con el registro de servicios independientemente del proveedor específico (Eureka, Consul, etc.):
@RestController
public class ProductController {
private final DiscoveryClient discoveryClient;
public ProductController(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@GetMapping("/products")
public String getProducts() {
// Lógica para obtener instancias de servicios
return "Lista de productos";
}
}
Obtención de instancias de servicios
El método más utilizado del DiscoveryClient
es **getInstances(String serviceId)**
, que devuelve una lista de todas las instancias disponibles para un servicio específico. El serviceId
corresponde al nombre definido en la propiedad spring.application.name
del servicio registrado:
@Service
public class InventarioService {
private final DiscoveryClient discoveryClient;
public InventarioService(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
public List<ServiceInstance> obtenerInstanciasAlmacen() {
return discoveryClient.getInstances("almacen-service");
}
}
Extracción de información de instancias
Cada ServiceInstance
contiene metadatos completos sobre la instancia del servicio, incluyendo host, puerto, esquema de protocolo y URI base. Esta información es esencial para construir las URLs necesarias para realizar llamadas HTTP:
@RestController
public class PedidoController {
private final DiscoveryClient discoveryClient;
private final RestClient restClient;
public PedidoController(DiscoveryClient discoveryClient,
RestClient.Builder restClientBuilder) {
this.discoveryClient = discoveryClient;
this.restClient = restClientBuilder.build();
}
@GetMapping("/pedido/{id}/inventario")
public String consultarInventario(@PathVariable Long id) {
// Obtener la primera instancia disponible
ServiceInstance instancia = discoveryClient
.getInstances("inventario-service")
.get(0);
// Construir la URL completa del endpoint
String baseUrl = instancia.getUri().toString();
String url = baseUrl + "/api/inventario/producto/" + id;
// Realizar la llamada HTTP
return restClient.get()
.uri(url)
.retrieve()
.body(String.class);
}
}
Manejo de múltiples instancias
Cuando existen múltiples instancias de un servicio, getInstances()
devuelve una lista completa. Para esta introducción, utilizaremos una estrategia simple seleccionando la primera instancia disponible, aunque en entornos de producción se requieren estrategias de balanceo más sofisticadas:
@Service
public class NotificacionService {
private final DiscoveryClient discoveryClient;
private final RestClient restClient;
public NotificacionService(DiscoveryClient discoveryClient,
RestClient.Builder restClientBuilder) {
this.discoveryClient = discoveryClient;
this.restClient = restClientBuilder.build();
}
public String enviarEmail(String destinatario, String mensaje) {
List<ServiceInstance> instancias = discoveryClient
.getInstances("email-service");
if (instancias.isEmpty()) {
throw new RuntimeException("No hay instancias disponibles de email-service");
}
// Seleccionar la primera instancia disponible
ServiceInstance instancia = instancias.get(0);
String url = instancia.getUri() + "/api/email/enviar";
// Preparar el cuerpo de la petición
EmailRequest request = new EmailRequest(destinatario, mensaje);
return restClient.post()
.uri(url)
.body(request)
.retrieve()
.body(String.class);
}
}
Información adicional de instancias
Las instancias proporcionan metadatos enriquecidos que pueden ser útiles para decisiones de enrutamiento o monitoreo. Algunos de los datos más relevantes incluyen:
@RestController
public class DiagnosticoController {
private final DiscoveryClient discoveryClient;
public DiagnosticoController(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@GetMapping("/servicios/estado")
public Map<String, Object> obtenerEstadoServicios() {
List<String> servicios = discoveryClient.getServices();
Map<String, Object> estado = new HashMap<>();
for (String servicio : servicios) {
List<ServiceInstance> instancias = discoveryClient.getInstances(servicio);
List<Map<String, Object>> detallesInstancias = new ArrayList<>();
for (ServiceInstance instancia : instancias) {
Map<String, Object> detalle = new HashMap<>();
detalle.put("host", instancia.getHost());
detalle.put("puerto", instancia.getPort());
detalle.put("uri", instancia.getUri().toString());
detalle.put("instanceId", instancia.getInstanceId());
detalle.put("secure", instancia.isSecure());
detalle.put("metadata", instancia.getMetadata());
detallesInstancias.add(detalle);
}
estado.put(servicio, detallesInstancias);
}
return estado;
}
}
Integración con RestClient
El patrón más común combina DiscoveryClient
con RestClient
(el cliente HTTP moderno recomendado en Spring Boot 3) para realizar llamadas entre servicios. Esta combinación proporciona flexibilidad total sobre el proceso de descubrimiento y comunicación:
@Component
public class ClienteService {
private final DiscoveryClient discoveryClient;
private final RestClient restClient;
public ClienteService(DiscoveryClient discoveryClient,
RestClient.Builder restClientBuilder) {
this.discoveryClient = discoveryClient;
this.restClient = restClientBuilder.build();
}
public Cliente obtenerCliente(Long clienteId) {
ServiceInstance instancia = discoveryClient
.getInstances("cliente-service")
.stream()
.findFirst()
.orElseThrow(() -> new RuntimeException("Cliente service no disponible"));
String url = String.format("%s/api/clientes/%d",
instancia.getUri(), clienteId);
return restClient.get()
.uri(url)
.retrieve()
.body(Cliente.class);
}
public Cliente crearCliente(Cliente nuevoCliente) {
ServiceInstance instancia = discoveryClient
.getInstances("cliente-service")
.get(0);
String url = instancia.getUri() + "/api/clientes";
return restClient.post()
.uri(url)
.contentType(MediaType.APPLICATION_JSON)
.body(nuevoCliente)
.retrieve()
.body(Cliente.class);
}
}
Manejo de errores y servicios no disponibles
Es crucial implementar manejo de errores robusto cuando se trabaja con resolución programática de instancias, ya que los servicios pueden no estar disponibles temporalmente:
@Service
public class ReporteService {
private final DiscoveryClient discoveryClient;
private final RestClient restClient;
public ReporteService(DiscoveryClient discoveryClient,
RestClient.Builder restClientBuilder) {
this.discoveryClient = discoveryClient;
this.restClient = restClientBuilder.build();
}
public String generarReporte(String tipoReporte) {
List<ServiceInstance> instancias = discoveryClient
.getInstances("reporte-service");
if (instancias.isEmpty()) {
return "Servicio de reportes temporalmente no disponible";
}
ServiceInstance instancia = instancias.get(0);
String url = instancia.getUri() + "/api/reportes/generar";
try {
return restClient.post()
.uri(url)
.body(Map.of("tipo", tipoReporte))
.retrieve()
.body(String.class);
} catch (Exception e) {
return "Error al generar reporte: " + e.getMessage();
}
}
}
Esta aproximación manual con DiscoveryClient
proporciona control total sobre el proceso de descubrimiento y comunicación entre servicios. Sin embargo, en aplicaciones de producción con múltiples instancias, se hace necesario implementar estrategias de balanceo de carga más sofisticadas. En la próxima lección exploraremos Spring Cloud LoadBalancer, que automatiza el balanceo de carga y simplifica significativamente estas tareas.
Ejemplo de cliente eureka completo
Imaginemos un microservicio de “directions” para llevar lógica de negocio de direcciones de clientes.
Este podría ser el archivo de configuración del microservicio que usaríamos en el repositorio git de configuración centralizada:
El microservicio directions, al tener el starter spring-cloud-starter-config se conecta al config server y lee esa configuración, en su propio archivo bootstrap.yml solo tiene que conocer dónde está ubicado el config server:
A mayores se suele habilitar el discovery client:
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en SpringBoot
Documentación oficial de SpringBoot
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SpringBoot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de SpringBoot
Explora más contenido relacionado con SpringBoot y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender cómo convertir una aplicación Spring Boot en un cliente Eureka mediante dependencias y configuración.
- Aprender el funcionamiento del registro automático y la anotación @EnableEurekaClient.
- Configurar correctamente el cliente Eureka en el archivo application.yml.
- Utilizar DiscoveryClient para localizar programáticamente instancias de servicios registrados.
- Implementar llamadas HTTP entre microservicios usando DiscoveryClient y RestClient con manejo básico de errores.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje