Quiz: Techniques Avancées
Testez vos connaissances sur WebFlux, Redis, RabbitMQ, Scheduling et WebSockets.
30 questions - Cliquez sur chaque question pour révéler la réponse.
Questions 1-10: Programmation Réactive (WebFlux)
Question 1: Quelle est la différence entre Spring MVC et WebFlux?
Réponse:
| Spring MVC | WebFlux |
|---|---|
| Bloquant | Non-bloquant |
| 1 thread/requête | Event loop |
| Servlet API | Reactive Streams |
| Tomcat, Jetty | Netty |
| Scalabilité verticale | Scalabilité horizontale |
Question 2: Quelle est la différence entre Mono et Flux?
Réponse:
Mono: 0 ou 1 élémentFlux: 0 à N éléments
Mono<User> user = userRepository.findById(1L);
Flux<User> users = userRepository.findAll();
Question 3: Quels sont les opérateurs Mono/Flux courants?
Réponse:
| Opérateur | Description |
|---|---|
map | Transformation synchrone |
flatMap | Transformation async |
filter | Filtrage |
zip | Combiner |
onErrorReturn | Fallback erreur |
switchIfEmpty | Fallback vide |
timeout | Limite de temps |
retry | Réessayer |
Question 4: Comment écrire un contrôleur réactif?
Réponse:
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserRepository repository;
@GetMapping
public Flux<User> getAll() {
return repository.findAll();
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> getById(@PathVariable Long id) {
return repository.findById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> create(@RequestBody User user) {
return repository.save(user);
}
}
Question 5: Qu'est-ce que R2DBC?
Réponse: Reactive Relational Database Connectivity - accès non-bloquant aux bases relationnelles.
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByEmail(String email);
}
Remplace JPA/JDBC bloquant dans les applications réactives.
Question 6: Comment utiliser WebClient?
Réponse:
@Service
public class ExternalApiService {
private final WebClient webClient;
public Mono<Data> fetchData(String id) {
return webClient.get()
.uri("/data/{id}", id)
.retrieve()
.bodyToMono(Data.class)
.timeout(Duration.ofSeconds(5))
.retry(3);
}
}
Question 7: Pourquoi ne jamais appeler block() en production?
Réponse: block() bloque le thread, annulant les avantages du réactif:
- Bloque l'event loop
- Réduit le throughput
- Peut causer des deadlocks
Utiliser uniquement dans les tests.
Question 8: Comment tester du code réactif?
Réponse:
@Test
void shouldFindUser() {
Mono<User> userMono = userService.findById(1L);
StepVerifier.create(userMono)
.assertNext(user -> {
assertThat(user.getName()).isEqualTo("John");
})
.verifyComplete();
}
@Test
void shouldHandleError() {
Mono<User> userMono = userService.findById(999L);
StepVerifier.create(userMono)
.expectError(NotFoundException.class)
.verify();
}
Question 9: Quand utiliser WebFlux?
Réponse:
- Haute concurrence (milliers de connexions)
- Streaming de données
- Applications temps réel
- Intégrations avec beaucoup d'appels externes
Éviter si: équipe non formée, bibliothèques bloquantes, CRUD simple.
Question 10: Comment gérer les erreurs en réactif?
Réponse:
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new NotFoundException("User not found")))
.onErrorReturn(new User("Fallback"))
.onErrorResume(e -> {
log.error("Error: {}", e.getMessage());
return Mono.empty();
});
Questions 11-17: Cache avec Redis
Question 11: Comment configurer Spring Cache avec Redis?
Réponse:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
@EnableCaching
public class CacheConfig { }
Question 12: Comment utiliser @Cacheable?
Réponse:
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
log.info("Fetching from DB");
return userRepository.findById(id).orElseThrow();
}
// Premier appel: DB
// Appels suivants: cache
Question 13: Quelle est la différence entre @Cacheable, @CachePut et @CacheEvict?
Réponse:
| Annotation | Comportement |
|---|---|
@Cacheable | Lit du cache ou exécute et cache |
@CachePut | Toujours exécute et met à jour le cache |
@CacheEvict | Supprime du cache |
@CachePut(value = "users", key = "#user.id")
public User update(User user) { }
@CacheEvict(value = "users", key = "#id")
public void delete(Long id) { }
@CacheEvict(value = "users", allEntries = true)
public void clearCache() { }
Question 14: Comment configurer le TTL du cache?
Réponse:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
Question 15: Comment utiliser RedisTemplate directement?
Réponse:
@Service
public class SessionService {
private final RedisTemplate<String, Object> redisTemplate;
public void saveSession(String token, UserSession session) {
redisTemplate.opsForValue().set(
"session:" + token,
session,
Duration.ofHours(1)
);
}
public UserSession getSession(String token) {
return (UserSession) redisTemplate.opsForValue()
.get("session:" + token);
}
}
Question 16: Comment gérer l'indisponibilité de Redis?
Réponse:
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
@Override
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException ex, Cache cache, Object key) {
log.warn("Cache error: {}", ex.getMessage());
// Continuer sans cache
}
};
}
}
Question 17: Quand mettre en cache?
Réponse:
- Données lues fréquemment, modifiées rarement
- Calculs coûteux
- Appels API externes
- Données de référence (pays, catégories)
Éviter: données temps réel, données personnelles sensibles.
Questions 18-23: Messaging avec RabbitMQ
Question 18: Comment configurer RabbitMQ avec Spring?
Réponse:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
Question 19: Quels sont les types d'exchange RabbitMQ?
Réponse:
| Type | Routing |
|---|---|
| Direct | Routing key exact |
| Fanout | Broadcast à toutes les queues |
| Topic | Pattern matching (*.error, order.#) |
| Headers | Basé sur les headers |
Question 20: Comment envoyer un message?
Réponse:
@Service
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public void sendOrderEvent(OrderEvent event) {
rabbitTemplate.convertAndSend(
"order.exchange", // exchange
"order.created", // routing key
event // message
);
}
}
Question 21: Comment recevoir un message?
Réponse:
@Component
public class OrderListener {
@RabbitListener(queues = "order.queue")
public void handleOrder(OrderEvent event) {
log.info("Received: {}", event);
processOrder(event);
}
}
Question 22: Qu'est-ce qu'une Dead Letter Queue?
Réponse: Queue qui reçoit les messages en échec (rejetés, expirés, max retries):
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.withArgument("x-dead-letter-exchange", "dlx.exchange")
.withArgument("x-dead-letter-routing-key", "dlq")
.build();
}
Permet d'analyser et retraiter les erreurs.
Question 23: Comment garantir la livraison des messages?
Réponse:
- Publisher confirms: confirmation de l'échange
- Consumer acks: confirmation du traitement
- Persistent messages: survit au redémarrage
- Durable queues: queues persistantes
@RabbitListener(queues = "order.queue", ackMode = "MANUAL")
public void handle(OrderEvent event, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
try {
process(event);
channel.basicAck(tag, false);
} catch (Exception e) {
channel.basicNack(tag, false, false);
}
}
Questions 24-27: Tâches Planifiées
Question 24: Comment activer le scheduling?
Réponse:
@SpringBootApplication
@EnableScheduling
public class Application { }
Question 25: Quelles sont les options de @Scheduled?
Réponse:
// Toutes les 5 secondes
@Scheduled(fixedRate = 5000)
// 5 secondes après la fin
@Scheduled(fixedDelay = 5000)
// Expression Cron
@Scheduled(cron = "0 0 6 * * *") // Tous les jours à 6h
// Avec délai initial
@Scheduled(fixedRate = 5000, initialDelay = 10000)
Question 26: Comment écrire une expression Cron?
Réponse:
┌───────────── seconde (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── heure (0-23)
│ │ │ ┌───────────── jour du mois (1-31)
│ │ │ │ ┌───────────── mois (1-12)
│ │ │ │ │ ┌───────────── jour semaine (0-7)
* * * * * *
Exemples:
0 0 * * * *: toutes les heures0 0 9 * * MON-FRI: 9h en semaine0 */15 * * * *: toutes les 15 minutes
Question 27: Comment exécuter une méthode de façon asynchrone?
Réponse:
@EnableAsync
@Configuration
public class AsyncConfig { }
@Service
public class EmailService {
@Async
public CompletableFuture<Boolean> sendEmail(String to) {
// Exécuté dans un autre thread
return CompletableFuture.completedFuture(true);
}
}
Questions 28-30: WebSockets
Question 28: Comment configurer WebSocket avec STOMP?
Réponse:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
Question 29: Comment envoyer et recevoir des messages WebSocket?
Réponse:
@Controller
public class ChatController {
@MessageMapping("/chat.send")
@SendTo("/topic/messages")
public ChatMessage send(ChatMessage message) {
return message;
}
}
// Envoi programmatique
@Service
public class NotificationService {
private final SimpMessagingTemplate template;
public void notifyUser(String userId, Notification notif) {
template.convertAndSendToUser(userId, "/queue/notifications", notif);
}
}
Question 30: Quand utiliser WebSocket vs REST?
Réponse:
| WebSocket | REST |
|---|---|
| Communication bidirectionnelle | Request-Response |
| Temps réel | Données statiques |
| Chat, notifications | CRUD, API |
| Connexion persistante | Connexions courtes |
| Jeux, trading | Applications classiques |
Score
| Score | Niveau |
|---|---|
| 27-30 | Excellent - Techniques avancées maîtrisées |
| 21-26 | Bien - Quelques révisions |
| 15-20 | Moyen - Relisez les chapitres |
| Moins de 15 | À revoir |