Saltar al contenido principal

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 MVCWebFlux
BloqueanteNo bloqueante
1 hilo/solicitudEvent loop
Servlet APIReactive Streams
Tomcat, JettyNetty
Escalabilidad verticalEscalabilidad horizontal

Pregunta 2: ¿Cuál es la diferencia entre Mono y Flux?

Respuesta:

  • Mono: 0 o 1 elemento
  • Flux: 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:

OperadorDescripción
mapTransformación sincrónica
flatMapTransformación asíncrona
filterFiltrado
zipCombinar
onErrorReturnFallback de error
switchIfEmptyFallback vacío
timeoutLímite de tiempo
retryReintentar

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ónComportamiento
@CacheableLee del caché o ejecuta y almacena en caché
@CachePutSiempre ejecuta y actualiza el caché
@CacheEvictElimina 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:

TipoRouting
DirectRouting key exacta
FanoutBroadcast a todas las colas
TopicPattern matching (*.error, order.#)
HeadersBasado 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:

  1. Publisher confirms: confirmación del exchange
  2. Consumer acks: confirmación del procesamiento
  3. Persistent messages: sobrevive al reinicio
  4. 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 hora
  • 0 0 9 * * MON-FRI: 9h de lunes a viernes
  • 0 */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:

WebSocketREST
Comunicación bidireccionalRequest-Response
Tiempo realDatos estáticos
Chat, notificacionesCRUD, API
Conexión persistenteConexiones cortas
Juegos, tradingAplicaciones clásicas

Puntuación​

PuntuaciónNivel
27-30Excelente - Técnicas avanzadas dominadas
21-26Bien - Algunas revisiones
15-20Medio - Repase los capítulos
Menos de 15A revisar

Volver al Módulo 14