Service Discovery
Service Discovery avec Eureka
10-15 min
Service Discovery avec Eureka
La découverte de services est un composant essentiel dans une architecture microservices. Netflix Eureka permet de gérer l’enregistrement et la découverte des services de manière efficace.
1. Configuration du Serveur Eureka
1.1 Création du Projet
curl https://start.spring.io/starter.tgz \
-d dependencies=cloud-eureka-server \
-d groupId=com.example \
-d artifactId=eureka-server \
-d name=eureka-server \
-d description=Eureka%20Server%20for%20Microservices \
-d packageName=com.example.eureka \
-d packaging=jar \
-d javaVersion=17 \
| tar -xzvf -
1.2 Configuration du Serveur
# application.yml
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.3 Configuration de la Haute Disponibilité
# application-peer1.yml
spring:
application:
name: eureka-server
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
service-url:
defaultZone: http://peer2:8762/eureka/
---
# application-peer2.yml
spring:
application:
name: eureka-server
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
service-url:
defaultZone: http://peer1:8761/eureka/
1.4 Configuration du Monitoring
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
enabled: true
prometheus:
enabled: true
eureka:
server:
wait-time-in-ms-when-sync-empty: 0
enable-self-preservation: true
eviction-interval-timer-in-ms: 4000
2. Configuration des Clients
2.1 Ajout des Dépendances
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.2 Configuration des Clients
# application.yml
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
fetch-registry: true
register-with-eureka: true
registry-fetch-interval-seconds: 5
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.hostname}:${spring.application.name}:${server.port}
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30
2.3 Configuration Multi-environnements
# application-dev.yml
spring:
profiles: dev
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
metadata-map:
environment: development
---
# application-prod.yml
spring:
profiles: prod
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka/
instance:
metadata-map:
environment: production
prefer-ip-address: true
3. Utilisation de la Découverte de Services
3.1 Injection du Client Eureka
@Service
public class ProductService {
private final EurekaClient eurekaClient;
private final RestTemplate restTemplate;
@Autowired
public ProductService(EurekaClient eurekaClient, RestTemplate restTemplate) {
this.eurekaClient = eurekaClient;
this.restTemplate = restTemplate;
}
public Product getProduct(Long id) {
InstanceInfo instance = eurekaClient.getNextServerFromEureka("order-service", false);
String baseUrl = instance.getHomePageUrl();
return restTemplate.getForObject(baseUrl + "/api/orders/" + id, Product.class);
}
}
3.2 Configuration du RestTemplate avec Load Balancing
@Configuration
public class RestTemplateConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.3 Utilisation avec Feign Client
@FeignClient(name = "order-service", configuration = FeignConfig.class)
public interface OrderClient {
@GetMapping("/api/orders/{id}")
Order getOrder(@PathVariable("id") Long id);
}
@Configuration
public class FeignConfig {
@Bean
public Retryer retryer() {
return new Retryer.Default(100, 1000, 3);
}
}
4. Gestion des Erreurs et Health Checks
4.1 Configuration des Health Checks
# application.yml
eureka:
client:
healthcheck:
enabled: true
instance:
health-check-url-path: /actuator/health
health-check-interval: 10s
health-check-timeout: 5s
4.2 Gestion des Erreurs
@ControllerAdvice
public class EurekaErrorHandler {
@ExceptionHandler(EurekaClientException.class)
public ResponseEntity<ErrorResponse> handleEurekaClientException(EurekaClientException ex) {
ErrorResponse error = new ErrorResponse(
"SERVICE_DISCOVERY_ERROR",
"Erreur lors de la découverte du service",
ex.getMessage()
);
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(error);
}
}
5. Monitoring et Métriques
5.1 Configuration des Métriques
@Component
public class EurekaMetrics {
private final MeterRegistry registry;
private final EurekaClient eurekaClient;
@Autowired
public EurekaMetrics(MeterRegistry registry, EurekaClient eurekaClient) {
this.registry = registry;
this.eurekaClient = eurekaClient;
initializeMetrics();
}
private void initializeMetrics() {
Gauge.builder("eureka.registered.services", eurekaClient, this::getRegisteredServicesCount)
.description("Nombre de services enregistrés")
.register(registry);
Gauge.builder("eureka.available.services", eurekaClient, this::getAvailableServicesCount)
.description("Nombre de services disponibles")
.register(registry);
}
private double getRegisteredServicesCount(EurekaClient client) {
return client.getApplications().getRegisteredApplications().size();
}
private double getAvailableServicesCount(EurekaClient client) {
return client.getApplications().getRegisteredApplications().stream()
.filter(app -> app.getInstances().stream().anyMatch(InstanceInfo::isUp))
.count();
}
}
5.2 Configuration des Alertes
# prometheus.yml
groups:
- name: eureka
rules:
- alert: ServiceDown
expr: eureka_available_services < 2
for: 5m
labels:
severity: critical
annotations:
summary: "Service indisponible"
description: "Le service {{ $labels.service }} est indisponible depuis 5 minutes"
6. Tests
6.1 Test d’Intégration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class EurekaIntegrationTest {
@Autowired
private EurekaClient eurekaClient;
@Test
void whenServiceStarts_thenItRegistersWithEureka() {
Application application = eurekaClient.getApplication("product-service");
assertNotNull(application);
assertTrue(application.getInstances().size() > 0);
assertTrue(application.getInstances().get(0).getStatus() == InstanceInfo.InstanceStatus.UP);
}
}
6.2 Test de Résilience
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class EurekaResilienceTest {
@Autowired
private EurekaClient eurekaClient;
@Test
void whenEurekaServerDown_thenClientHandlesGracefully() {
// Simuler une panne du serveur Eureka
eurekaClient.shutdown();
// Vérifier que le client gère gracieusement la situation
assertThrows(EurekaClientException.class, () -> {
eurekaClient.getApplication("product-service");
});
}
}
Conclusion
La découverte de services avec Eureka offre plusieurs avantages :
- Gestion dynamique des services
- Haute disponibilité avec la configuration peer-to-peer
- Monitoring et métriques intégrés
- Configuration flexible pour différents environnements
- Intégration native avec Spring Cloud
Dans le prochain tutoriel, nous verrons comment centraliser la configuration de nos microservices avec Spring Cloud Config.
Commentaires
Les commentaires sont alimentés par GitHub Discussions
Connectez-vous avec GitHub pour participer à la discussion