AgentSkillsCN

spring-reactive

使用Spring WebFlux、Project Reactor(Mono、Flux)、WebClient以及响应式运算符,构建响应式API与服务。功能涵盖MDC上下文传播(ContextRegistry、Hooks.enableAutomaticContextPropagation)、WebClient配置(超时、错误处理、重试)、响应式运算符链(flatMap、switchIfEmpty、zip),以及阻塞检测。在实现WebFlux端点、在响应式边界间传播MDC/追踪上下文、配置WebClient、调试上下文丢失、处理背压,或使用StepVerifier测试响应式流时使用此功能。

SKILL.md
--- frontmatter
name: spring-reactive
description: Build reactive APIs and services with Spring WebFlux, Project Reactor (Mono, Flux), WebClient, and reactive operators. Capabilities include MDC context propagation (ContextRegistry, Hooks.enableAutomaticContextPropagation), WebClient configuration (timeouts, error handling, retry), reactive operator chains (flatMap, switchIfEmpty, zip), and blocking detection. Use when implementing WebFlux endpoints, propagating MDC/trace context across reactive boundaries, configuring WebClient, debugging context loss, handling backpressure, or testing reactive streams with StepVerifier.
license: MIT
metadata:
  version: 1.0.0
  audience: developers
  workflow: backend-development

Spring Reactive

Build non-blocking, reactive web applications with Spring WebFlux and Project Reactor. This skill focuses on production patterns, especially MDC context propagation which is the #1 pain point in reactive applications.

When to Use Me

  • Implement reactive REST endpoints with WebFlux
  • Propagate MDC context (traceId, requestId) across async boundaries
  • Configure WebClient with proper timeouts, retries, and error handling
  • Debug "context loss" issues where traceId disappears in logs
  • Chain reactive operators (Mono/Flux) correctly
  • Detect and handle blocking calls in reactive code
  • Test reactive streams with StepVerifier

What I Do

  • Guide MDC context propagation setup with ContextRegistry and Hooks
  • Provide WebClient configuration patterns (connection pools, timeouts)
  • Explain Mono/Flux operator selection and chaining
  • Help debug context loss and blocking call issues
  • Show StepVerifier testing patterns
  • Cover error handling strategies in reactive streams

MDC Context Propagation

This is critical. Without proper setup, MDC context (traceId, spanId, requestId) is lost when crossing reactive boundaries.

Required Configuration (Spring Boot 3.x / Reactor 3.5+)

java
@Configuration
public class ReactorContextConfig {
    @PostConstruct
    public void setupContextPropagation() {
        // Enable automatic context propagation (Reactor 3.5+)
        Hooks.enableAutomaticContextPropagation();

        // Register MDC accessor with null-safe handling
        ContextRegistry.getInstance().registerThreadLocalAccessor(
            "mdc",
            () -> Optional.ofNullable(MDC.getCopyOfContextMap()).orElse(Map.of()),
            map -> { if (map != null) MDC.setContextMap(map); },
            MDC::clear
        );
    }
}

Key Points

  • Use Micrometer context-propagation integration for production (recommended over DIY)
  • Context flows per-subscription within the same reactive chain
  • Use deferContextual to read Reactor Context; don't assume operators "drop" context
  • WebFilter captures context: .contextWrite(ctx -> ctx.put("mdc", contextMap))

Reactor Patterns

Mono/Flux Selection

Use CaseTypeWhy
Single value or emptyMono<T>0..1 elements
Collection/streamFlux<T>0..N elements
Void operationMono<Void>Side effects only
Optional resultMono<T> with switchIfEmptyHandle empty case

Common Operators

java
// Transform
mono.map(x -> transform(x))           // Sync
mono.flatMap(x -> asyncCall(x))       // Async

// Handle empty
mono.switchIfEmpty(Mono.defer(() -> fallback()))
mono.defaultIfEmpty(defaultValue)

// Error handling
mono.onErrorResume(ex -> fallbackMono())
mono.onErrorMap(ex -> new CustomException(ex))

// Combine
Mono.zip(mono1, mono2, (a, b) -> combine(a, b))
Flux.merge(flux1, flux2)  // Interleaved
Flux.concat(flux1, flux2) // Sequential

Anti-Patterns

java
// BAD: Blocking in reactive chain
mono.map(x -> blockingCall(x))  // Blocks event loop!

// GOOD: Offload to bounded scheduler
mono.flatMap(x -> Mono.fromCallable(() -> blockingCall(x))
    .subscribeOn(Schedulers.boundedElastic()))

WebClient Configuration

Configure HttpClient with timeouts, add MDC propagation filter via Mono.deferContextual(), handle errors with .onStatus(), retry with Retry.backoff().

Blocking Detection

ViolationSolution
Thread.sleep()Mono.delay(Duration)
JDBC callsUse R2DBC or subscribeOn(Schedulers.boundedElastic())
InputStream.read()Use DataBufferUtils
Synchronized blocksUse Mono.fromCallable().subscribeOn()

Enable BlockHound in dev/test to detect blocking violations automatically.

Common Errors

ErrorCauseSolution
MDC values null in logsContext not propagatedEnable Hooks.enableAutomaticContextPropagation()
block()/blockFirst() errorBlocking on event loopUse subscribeOn(Schedulers.boundedElastic())
Timeout on blocking readWebClient timeoutConfigure timeouts in HttpClient
Context is emptyNew Mono without contextUse Mono.deferContextual() or contextWrite()

Testing with StepVerifier

java
@Test
void shouldProcessReactively() {
    StepVerifier.create(service.process("input"))
        .expectNext("expected")
        .verifyComplete();
}

@Test
void shouldPropagateContext() {
    Mono<String> result = service.getTraceId()
        .contextWrite(ctx -> ctx.put("mdc", Map.of("traceId", "test-123")));

    StepVerifier.create(result)
        .expectNext("test-123")
        .verifyComplete();
}

WebTestClient Testing

java
@WebFluxTest(OrderController.class)
class OrderControllerTest {
    @Autowired
    private WebTestClient webClient;

    @MockBean
    private OrderService orderService;

    @Test
    void shouldReturnOrder() {
        when(orderService.findById(1L)).thenReturn(Mono.just(order));

        webClient.get().uri("/orders/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody(Order.class).isEqualTo(order);
    }
}

Backpressure Basics

StrategyUse When
onBackpressureBuffer()Slow consumer, bounded buffer OK
onBackpressureDrop()Latest value matters, can drop old
limitRate(n)Control request batch size

Context7 Integration

For up-to-date Spring WebFlux documentation:

code
libraryName: "Spring WebFlux" or "Project Reactor"
Queries: "WebClient configuration", "Mono Flux operators", "Context propagation"

Reference Navigation

TopicReferenceLoad When
Full MDC WebFilterresearch.mdComplete implementation
WebClient advanced configresearch.mdConnection pool tuning, DNS resolver
Operator deep diveresearch.mdComplete operator catalog
BlockHound setupresearch.mdBlocking detection configuration

Related Skills

SkillUse For
spring-boot-coreGeneral Spring Boot patterns
spring-testingTesting strategies including WebTestClient
opentelemetry-tracingDistributed tracing setup

Resources