Eureka Discovery Client

Intermedio
SpringBoot
SpringBoot
Actualizado: 09/10/2025

@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 - Autor del tutorial

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