Quiz: Técnicas Avanzadas
Ponga a prueba sus conocimientos sobre WebFlux, Redis, RabbitMQ, Scheduling y WebSockets.
30 preguntas - Haga clic en cada pregunta para revelar la respuesta.
Preguntas 1-10: Programación Reactiva (WebFlux)
Pregunta 1: ¿Cuál es la diferencia entre Spring MVC y WebFlux?
Respuesta:
| Spring MVC | WebFlux |
|---|---|
| Bloqueante | No bloqueante |
| 1 hilo/solicitud | Event loop |
| Servlet API | Reactive Streams |
| Tomcat, Jetty | Netty |
| Escalabilidad vertical | Escalabilidad horizontal |
Pregunta 2: ¿Cuál es la diferencia entre Mono y Flux?
Respuesta:
Mono: 0 o 1 elementoFlux: 0 a N elementos
Mono<User> user = userRepository.findById(1L);
Flux<User> users = userRepository.findAll();
Pregunta 3: ¿Cuáles son los operadores comunes de Mono/Flux?
Respuesta:
| Operador | Descripción |
|---|---|
map | Transformación sincrónica |
flatMap | Transformación asíncrona |
filter | Filtrado |
zip | Combinar |
onErrorReturn | Fallback de error |
switchIfEmpty | Fallback vacío |
timeout | Límite de tiempo |
retry | Reintentar |
Pregunta 4: ¿Cómo escribir un controlador reactivo?
Respuesta:
@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);
}
}
Pregunta 5: ¿Qué es R2DBC?
Respuesta: Reactive Relational Database Connectivity - acceso no bloqueante a bases de datos relacionales.
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByEmail(String email);
}
Reemplaza JPA/JDBC bloqueante en aplicaciones reactivas.
Pregunta 6: ¿Cómo usar WebClient?
Respuesta:
@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);
}
}
Pregunta 7: ¿Por qué nunca llamar a block() en producción?
Respuesta: block() bloquea el hilo, anulando las ventajas de lo reactivo:
- Bloquea el event loop
- Reduce el throughput
- Puede causar deadlocks
Usar solo en pruebas.
Pregunta 8: ¿Cómo probar código reactivo?
Respuesta:
@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();
}
Pregunta 9: ¿Cuándo usar WebFlux?
Respuesta:
- Alta concurrencia (miles de conexiones)
- Streaming de datos
- Aplicaciones en tiempo real
- Integraciones con muchas llamadas externas
Evitar si: equipo no capacitado, bibliotecas bloqueantes, CRUD simple.
Pregunta 10: ¿Cómo manejar errores en reactivo?
Respuesta:
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();
});
Preguntas 11-17: Caché con Redis
Pregunta 11: ¿Cómo configurar Spring Cache con Redis?
Respuesta:
<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 { }
Pregunta 12: ¿Cómo usar @Cacheable?
Respuesta:
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
log.info("Fetching from DB");
return userRepository.findById(id).orElseThrow();
}
// Primera llamada: DB
// Llamadas siguientes: caché
Pregunta 13: ¿Cuál es la diferencia entre @Cacheable, @CachePut y @CacheEvict?
Respuesta:
| Anotación | Comportamiento |
|---|---|
@Cacheable | Lee del caché o ejecuta y almacena en caché |
@CachePut | Siempre ejecuta y actualiza el caché |
@CacheEvict | Elimina del caché |
@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() { }
Pregunta 14: ¿Cómo configurar el TTL del caché?
Respuesta:
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
Pregunta 15: ¿Cómo usar RedisTemplate directamente?
Respuesta:
@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);
}
}
Pregunta 16: ¿Cómo manejar la indisponibilidad de Redis?
Respuesta:
@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());
// Continuar sin caché
}
};
}
}
Pregunta 17: ¿Cuándo almacenar en caché?
Respuesta:
- Datos leídos frecuentemente, modificados raramente
- Cálculos costosos
- Llamadas a API externas
- Datos de referencia (países, categorías)
Evitar: datos en tiempo real, datos personales sensibles.
Preguntas 18-23: Mensajería con RabbitMQ
Pregunta 18: ¿Cómo configurar RabbitMQ con Spring?
Respuesta:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
Pregunta 19: ¿Cuáles son los tipos de exchange en RabbitMQ?
Respuesta:
| Tipo | Routing |
|---|---|
| Direct | Routing key exacta |
| Fanout | Broadcast a todas las colas |
| Topic | Pattern matching (*.error, order.#) |
| Headers | Basado en los headers |
Pregunta 20: ¿Cómo enviar un mensaje?
Respuesta:
@Service
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public void sendOrderEvent(OrderEvent event) {
rabbitTemplate.convertAndSend(
"order.exchange", // exchange
"order.created", // routing key
event // mensaje
);
}
}
Pregunta 21: ¿Cómo recibir un mensaje?
Respuesta:
@Component
public class OrderListener {
@RabbitListener(queues = "order.queue")
public void handleOrder(OrderEvent event) {
log.info("Received: {}", event);
processOrder(event);
}
}
Pregunta 22: ¿Qué es una Dead Letter Queue?
Respuesta: Cola que recibe los mensajes fallidos (rechazados, expirados, máximo de reintentos):
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.withArgument("x-dead-letter-exchange", "dlx.exchange")
.withArgument("x-dead-letter-routing-key", "dlq")
.build();
}
Permite analizar y reprocesar los errores.
Pregunta 23: ¿Cómo garantizar la entrega de los mensajes?
Respuesta:
- Publisher confirms: confirmación del exchange
- Consumer acks: confirmación del procesamiento
- Persistent messages: sobrevive al reinicio
- Durable queues: colas persistentes
@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);
}
}
Preguntas 24-27: Tareas Programadas
Pregunta 24: ¿Cómo habilitar el scheduling?
Respuesta:
@SpringBootApplication
@EnableScheduling
public class Application { }
Pregunta 25: ¿Cuáles son las opciones de @Scheduled?
Respuesta:
// Cada 5 segundos
@Scheduled(fixedRate = 5000)
// 5 segundos después de finalizar
@Scheduled(fixedDelay = 5000)
// Expresión Cron
@Scheduled(cron = "0 0 6 * * *") // Todos los días a las 6h
// Con retardo inicial
@Scheduled(fixedRate = 5000, initialDelay = 10000)
Pregunta 26: ¿Cómo escribir una expresión Cron?
Respuesta:
┌───────────── segundo (0-59)
│ ┌───────────── minuto (0-59)
│ │ ┌───────────── hora (0-23)
│ │ │ ┌───────────── día del mes (1-31)
│ │ │ │ ┌───────────── mes (1-12)
│ │ │ │ │ ┌───────────── día semana (0-7)
* * * * * *
Ejemplos:
0 0 * * * *: cada hora0 0 9 * * MON-FRI: 9h de lunes a viernes0 */15 * * * *: cada 15 minutos
Pregunta 27: ¿Cómo ejecutar un método de forma asíncrona?
Respuesta:
@EnableAsync
@Configuration
public class AsyncConfig { }
@Service
public class EmailService {
@Async
public CompletableFuture<Boolean> sendEmail(String to) {
// Ejecutado en otro hilo
return CompletableFuture.completedFuture(true);
}
}
Preguntas 28-30: WebSockets
Pregunta 28: ¿Cómo configurar WebSocket con STOMP?
Respuesta:
@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();
}
}
Pregunta 29: ¿Cómo enviar y recibir mensajes WebSocket?
Respuesta:
@Controller
public class ChatController {
@MessageMapping("/chat.send")
@SendTo("/topic/messages")
public ChatMessage send(ChatMessage message) {
return message;
}
}
// Envío programático
@Service
public class NotificationService {
private final SimpMessagingTemplate template;
public void notifyUser(String userId, Notification notif) {
template.convertAndSendToUser(userId, "/queue/notifications", notif);
}
}
Pregunta 30: ¿Cuándo usar WebSocket vs REST?
Respuesta:
| WebSocket | REST |
|---|---|
| Comunicación bidireccional | Request-Response |
| Tiempo real | Datos estáticos |
| Chat, notificaciones | CRUD, API |
| Conexión persistente | Conexiones cortas |
| Juegos, trading | Aplicaciones clásicas |
Puntuación
| Puntuación | Nivel |
|---|---|
| 27-30 | Excelente - Técnicas avanzadas dominadas |
| 21-26 | Bien - Algunas revisiones |
| 15-20 | Medio - Repase los capítulos |
| Menos de 15 | A revisar |