Spring WebFlux 响应式编程教程

部署运行你感兴趣的模型镜像

目录

  1. 响应式编程概述
  2. Reactor编程模型
  3. WebFlux架构
  4. RouterFunction与HandlerFunction
  5. 响应式Web客户端
  6. 响应式数据访问
  7. Spring Security集成
  8. 测试策略
  9. 性能优化
  10. 监控与调试
  11. 最佳实践
  12. 总结

响应式编程概述

响应式编程优势

响应式编程是一种面向数据流和变化的编程范式:

核心优势

  • 非阻塞I/O:避免线程阻塞,提高并发性能
  • 背压处理:自动处理生产者消费者速度不匹配
  • 弹性架构:支持失败恢复和自动扩展
  • 异步处理:充分利用多核CPU资源

Spring MVC vs WebFlux

两种编程模型对比

特性Spring MVCSpring WebFlux
线程模型请求/线程 1:1事件循环 + 少量线程
阻塞特性同步阻塞异步非阻塞
并发性能线程数限制更高并发
学习曲线相对简单技术栈复杂
生态系统成熟完善快速发展

Maven依赖配置

<dependencies>
    <!-- Spring WebFlux Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    
    <!-- Reactor Core -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-core</artifactId>
    </dependency>
    
    <!-- Reactor Netty -->
    <dependency>
        <groupId>io.projectreactor.netty</groupId>
        <artifactId>reactor-netty-http</artifactId>
    </dependency>
    
    <!-- Spring Data R2DBC (响应式数据库访问) -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-r2dbc</artifactId>
    </dependency>
    
    <!-- R2DBC PostgreSQL -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-r2dbc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.r2dbc</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>
    
    <!-- Spring Security Reactive -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-webflux</artifactId>
    </dependency>
    
    <!-- Jackson Reactive -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    
    <!-- Test Reactive -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <!-- WebTestClient -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Reactor编程模型

核心类型

Reactor核心操作符

@Service
public class ReactiveOrderService {
    
    private final ReactiveOrderRepository orderRepository;
    private final ReactiveUserRepository userRepository;
    private final ReactiveProductRepository productRepository;
    
    public ReactiveOrderService(ReactiveOrderRepository orderRepository,
                               ReactiveUserRepository userRepository,
                               ReactiveProductRepository productRepository) {
        this.orderRepository = orderRepository;
        this.userRepository = userRepository;
        this.productRepository = productRepository;
    }
    
    // Mono处理单个元素
    public Mono<Order> createOrder(CreateOrderRequest request) {
        return Mono.fromSupplier(() -> request)
            .flatMap(this::validateOrderRequest)
            .flatMap(this::createOrderEntity)
            .flatMap(orderRepository::save)
            .flatMap(this::updateInventory)
            .flatMap(this::sendOrderConfirmation)
            .doOnSuccess(order -> log.info("订单创建成功: {}", order.getId()))
            .doOnError(error -> log.error("订单创建失败", error));
    }
    
    // Flux处理多个元素
    public Flux<Order> getOrdersByStatus(OrderStatus status) {
        return orderRepository.findByStatus(status)
            .flatMap(this::enrichOrderWithUser)
            .flatMap(this::enrichOrderWithItems)
            .filter(this::isValidOrder)
            .sort(Comparator.comparing(Order::getCreatedDate).reversed())
            .take(100); // 限制返回数量
    }
    
    // 背压处理
    public Flux<Order> getOrderStream(Long userId) {
        return orderRepository.findByUserId(userId)
            .filter(order -> order.getStatus() != OrderStatus.CANCELLED)
            .onBackpressureBuffer(1000) // 缓冲1000个元素
            .delayElements(Duration.ofMillis(100)) // 控制流速度
            .timeout(Duration.ofSeconds(30)) // 超时控制
            .retry(3); // 重试3次
    }
    
    // 组合异步操作
    public Mono<OrderSummary> getOrderSummary(Long orderId) {
        Mono<Order> orderMono = orderRepository.findById(orderId);
        Mono<User> userMono = orderMono
            .flatMap(order -> userRepository.findById(order.getUserId()));
        Mono<List<OrderItem>> itemsMono = orderMono
            .flatMapMany(order -> orderItemRepository.findByOrderId(orderId))
            .collectList();
        
        return Mono.zip(orderMono, userMono, itemsMono)
            .map(tuple -> {
                Order order = tuple.getT1();
                User user = tuple.getT2();
                List<OrderItem> items = tuple.getT3();
                
                return OrderSummary.builder()
                    .orderId(order.getId())
                    .customerName(user.getFirstName() + " " + user.getLastName())
                    .totalAmount(order.getTotalAmount())
                    .itemCount(items.size())
                    .status(order.getStatus())
                    .build();
            })
            .switchIfEmpty(Mono.error(new OrderNotFoundException(orderId)));
    }
    
    // 错误处理和重试
    public Mono<String> processPayment(Long orderId, PaymentRequest request) {
        return Mono.fromCallable(() -> validatePaymentRequest(orderId, request))
            .flatMap(this::callPaymentService)
            .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2))
                .filter(this::isRetryableException)
                .doBeforeRetry(signal -> 
                    log.warn("支付重试第{}次: orderId={}", 
                        signal.totalRetries() + 1, orderId)))
            .doOnSuccess(response -> log.info("支付成功: orderId={}", orderId))
            .doOnError(error -> log.error("支付失败: orderId={}", orderId, error))
            .onErrorReturn("PAYMENT_FAILED");
    }
    
    // 缓存和合并
    public Flux<Product> getOrderProducts(Long orderId) {
        return orderRepository.findById(orderId)
            .flatMapMany(order -> orderItemRepository.findByOrderId(orderId))
            .map(OrderItem::getProductId)
            .flatMap(productRepository::findById)
            .distinct(Product::getId) // 去重
            .cache(Duration.ofMinutes(5)) // 缓存5分钟
            .switchIfEmpty(Flux.error(new ProductNotFoundException()));
    }
    
    private Mono<CreateOrderRequest> validateOrderRequest(CreateOrderRequest request) {
        return Mono.just(request)
            .filter(req -> req.getItems() != null && !req.getItems().isEmpty())
            .switchIfEmpty(Mono.error(new IllegalArgumentException("订单项目不能为空")))
            .filter(req -> req.getUserId() != null)
            .switchIfEmpty(Mono.error(new IllegalArgumentException("用户ID不能为空")));
    }
    
    private Mono<Order> createOrderEntity(CreateOrderRequest request) {
        return Mono.fromCallable(() -> {
            Order order = Order.builder()
                .orderNumber(generateOrderNumber())
                .userId(request.getUserId())
                .totalAmount(request.getTotalAmount())
                .status(OrderStatus.PENDING)
                .createdDate(LocalDateTime.now())
                .build();
            return order;
        }).subscribeOn(Schedulers.boundedElastic());
    }
    
    private boolean isRetryableException(Throwable error) {
        return error instanceof SocketTimeoutException ||
               error instanceof ConnectException ||
               error instanceof TimeoutException;
    }
}

WebFlux架构

注解式Controller

响应式REST控制器

@RestController
@RequestMapping("/api/v1")
public class ReactiveOrderController {
    
    private final ReactiveOrderService orderService;
    private final ServerSentEventService sseService;
    
    @GetMapping("/orders/{id}")
    public Mono<ResponseEntity<OrderDto>> getOrder(@PathVariable Long id) {
        return orderService.findById(id)
            .map(orderMapper::toDto)
            .map(ResponseEntity::ok)
            .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()))
            .doOnError(error -> log.error("获取订单失败: {}", id, error));
    }
    
    @GetMapping(value = "/orders", params = "status")
    public Flux<OrderDto> getOrdersByStatus(
            @RequestParam OrderStatus status,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        
        return orderService.findByStatus(status)
            .map(orderMapper::toDto)
            .skip(page * size)
            .take(size)
            .buffer(size) // 批量处理
            .flatMap(orders -> Flux.fromIterable(orders));
    }
    
    @PostMapping("/orders")
    public Mono<ResponseEntity<OrderDto>> createOrder(
            @Valid @RequestBody CreateOrderRequest request) {
        
        return orderService.createOrder(request)
            .map(orderMapper::toDto)
            .map(orderDto -> ResponseEntity.status(HttpStatus.CREATED).body(orderDto))
            .onErrorResume(ValidationException.class, 
                error -> Mono.just(ResponseEntity.badRequest().build()))
            .doOnSubscribe(subscription -> 
                log.info("开始创建订单: {}", request));
    }
    
    @PutMapping("/orders/{id}/status")
    public Mono<ResponseEntity<Void>> updateOrderStatus(
            @PathVariable Long id,
            @RequestParam OrderStatus status) {
        
        return orderService.updateOrderStatus(id, status)
            .then(Mono.just(ResponseEntity.ok().<Void>build()))
            .onErrorReturn(ResponseEntity.notFound().build());
    }
    
    @DeleteMapping("/orders/{id}")
    public Mono<ResponseEntity<Void>> cancelOrder(@PathVariable Long id) {
        return orderService.cancelOrder(id)
            .then(Mono.just(ResponseEntity.ok().<Void>build()))
            .onErrorReturn(ResponseEntity.notFound().build());
    }
    
    // Server-Sent Events
    @GetMapping(value = "/orders/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<OrderDto>> streamOrders(@RequestParam Long userId) {
        return sseService.getOrderEvents(userId)
            .map(orderDto -> ServerSentEvent.builder(orderDto)
                .event("order-update")
                .id(orderDto.getId().toString())
                .build())
            .onErrorResume(error -> Flux.just(
                ServerSentEvent.builder()
                    .event("error")
                    .data("Stream connection error")
                    .build()
                    .skipUntil(DataBuffer.limit(-1))));
    }
    
    // 批量操作
    @PostMapping("/orders/batch-import")
    public Flux<OrderDto> batchImportOrders(@Valid @RequestBody Flux<CreateOrderRequest> requests) {
        return requests
            .flatMap(orderService::createOrder)
            .map(orderMapper::toDto)
            .doOnComplete(() -> log.info("批量订单导入完成"))
            .doOnError(error -> log.error("批量订单导入失败", error));
    }
    
    // 条件更新
    @PatchMapping("/orders/{id}")
    public Mono<ResponseEntity<OrderDto>> patchOrder(
            @PathVariable Long id,
            @RequestBody JsonPatch patch) {
        
        return orderService.findById(id)
            .flatMap(order -> orderService.applyPatchAndSave(order, patch))
            .map(orderMapper::toDto)
            .map(ResponseEntity::ok)
            .switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
    }
}

RouterFunction与HandlerFunction

函数式编程模型

路由函数配置

@Configuration
public class ReactiveWebRouter {
    
    private final ReactiveOrderHandler orderHandler;
    private final ReactiveUserHandler userHandler;
    private final ReactiveProductHandler productHandler;
    
    public ReactiveWebRouter(ReactiveOrderHandler orderHandler,
                            ReactiveUserHandler userHandler,
                            ReactiveProductHandler productHandler) {
        this.orderHandler = orderHandler;
        this.userHandler = userHandler;
        this.productHandler = productHandler;
    }
    
    @Bean
    public RouterFunction<ServerResponse> orderRoutes() {
        return RouterFunctions
            .route()
            
            // 订单相关路由
            .GET("/api/v2/orders/{id}", orderHandler::getOrder)
            .GET("/api/v2/orders", orderHandler::getOrders)
            .POST("/api/v2/orders", orderHandler::createOrder)
            .PUT("/api/v2/orders/{id}", orderHandler::updateOrder)
            .DELETE("/api/v2/orders/{id}", orderHandler::deleteOrder)
            
            // 条件查询
            .GET("/api/v2/orders/by-status/{status}", orderHandler::getOrdersByStatus)
            .GET("/api/v2/orders/by-user/{userId}", orderHandler::getOrdersByUser)
            
            // 用户相关路由
            .GET("/api/v2/users/{id}", userHandler::getUser)
            .POST("/api/v2/users", userHandler::createUser)
            .PUT("/api/v2/users/{id}", userHandler::updateUser)
            
            // 产品相关路由
            .GET("/api/v2/products", productHandler::getProducts)
            .GET("/api/v2/products/{id}", productHandler::getProduct)
            
            // 嵌套路由
            .add(orderRoutes().andRoute())
            .build();
    }
    
    @Bean
    public RouterFunction<ServerResponse> nestedOrderRoutes() {
        return RouterFunctions
            .route()
            .path("/api/v2/orders", () -> RouterFunctions
                .route()
                .GET("/{id}/items", orderHandler::getOrderItems)
                .POST("/{id}/items", orderHandler::addOrderItem)
                .PUT("/{id}/items/{itemId}", orderHandler::updateOrderItem)
                .DELETE("/{id}/items/{itemId}", orderHandler::removeOrderItem)
                .build())
            .build();
    }
    
    @Bean
    public RouterFunction<ServerResponse> utilityRoutes() {
        return RouterFunctions
            .route()
            .GET("/health", request -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(Map.of("status", "UP"))))
            
            .GET("/info", request -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(getApplicationInfo())))
            
            .filter(this::correlationIdFilter)
            .filter(this::loggingFilter)
            .build();
    }
    
    private HandlerFilterFunction<ServerResponse, ServerResponse> correlationIdFilter() {
        return (request, next) -> {
            String correlationId = UUID.randomUUID().toString();
            ServerRequest modifiedRequest = ServerRequest.from(request)
                .headers(headers -> headers.set("X-Correlation-ID", correlationId))
                .build();
            
            return next.handle(modifiedRequest)
                .map(response -> ServerResponse.from(response)
                    .headers(headers -> headers.set("X-Correlation-ID", correlationId))
                    .build());
        };
    }
    
    private HandlerFilterFunction<ServerResponse, ServerResponse> loggingFilter() {
        return (request, next) -> {
            long startTime = System.currentTimeMillis();
            
            return next.handle(request)
                .doOnNext(response -> {
                    long duration = System.currentTimeMillis() - startTime;
                    log.info("Request: {} {} - Response: {} - Duration: {}ms",
                        request.method(),
                        request.path(),
                        response.statusCode(),
                        duration);
                });
        };
    }
    
    private Map<String, Object> getApplicationInfo() {
        Map<String, Object> info = new HashMap<>();
        info.put("name", "Reactive Order Service");
        info.put("version", "2.0.0");
        info.put("environment", getActiveProfile());
        info.put("startupTime", getStartupTime());
        return info;
    }
}

// 订单处理器
@Component
public class ReactiveOrderHandler {
    
    private final ReactiveOrderService orderService;
    private final OrderMapper orderMapper;
    
    public ReactiveOrderHandler(ReactiveOrderService orderService,
                               OrderMapper orderMapper) {
        this.orderService = orderService;
        this.orderMapper = orderMapper;
    }
    
    public Mono<ServerResponse> getOrder(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        
        return orderService.findById(id)
            .flatMap(order -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(orderMapper.toDto(order))))
            .switchIfEmpty(ServerResponse.notFound().build())
            .onErrorResume(ex -> ServerResponse.badRequest()
                .body(BodyInserters.fromValue(Map.of("error", ex.getMessage()))));
    }
    
    public Mono<ServerResponse> getOrders(ServerRequest request) {
        Optional<String> statusParam = request.queryParam("status");
        Optional<String> userIdParam = request.queryParam("userId");
        
        Flux<Order> orders;
        
        if (statusParam.isPresent()) {
            OrderStatus status = OrderStatus.valueOf(statusParam.get());
            orders = orderService.findByStatus(status);
        } else if (userIdParam.isPresent()) {
            Long userId = Long.parseLong(userIdParam.get());
            orders = orderService.findByUserId(userId);
        } else {
            orders = orderService.findAll();
        }
        
        return ServerResponse.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromPublisher(
                orders.map(orderMapper::toDto), OrderDto.class));
    }
    
    public Mono<ServerResponse> createOrder(ServerRequest request) {
        return request.bodyToMono(CreateOrderRequest.class)
            .flatMap(orderService::createOrder)
            .flatMap(order -> ServerResponse.status(HttpStatus.CREATED)
                .contentType(MediaType.APPLICATION_JSON)
                .headers(headers -> headers.set("Location", 
                    "/api/v2/orders/" + order.getId()))
                .body(BodyInserters.fromValue(orderMapper.toDto(order))))
            .onErrorResume(ValidationException.class, ex -> 
                ServerResponse.badRequest()
                    .body(BodyInserters.fromValue(Map.of("error", ex.getMessage()))));
    }
    
    public Mono<ServerResponse> updateOrder(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        
        return request.bodyToMono(OrderUpdateRequest.class)
            .flatMap(updateRequest -> orderService.updateOrder(id, updateRequest))
            .flatMap(order -> ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(orderMapper.toDto(order))))
            .switchIfEmpty(ServerResponse.notFound().build());
    }
    
    public ServerResponse deleteOrder(ServerRequest request) {
        Long id = Long.parseLong(request.pathVariable("id"));
        
        return orderService.deleteOrder(id)
            .then(ServerResponse.noContent().build())
            .onErrorReturn(ServerResponse.notFound().build());
    }
}

响应式Web客户端

WebClient使用

响应式HTTP客户端

@Service
public class ReactiveExternalServiceClient {
    
    private final WebClient webClient;
    private final CircuitBreaker circuitBreaker;
    
    public ReactiveExternalServiceClient(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder
            .baseUrl("https://api.external.com")
            .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
            .defaultHeader(HttpHeaders.ACCEPT, "application/json")
            .filter(logRequest())
            .filter(logResponse())
            .filter(circuitBreakerFilter())
            .build();
    }
    
    // GET请求
    public Mono<UserDto> getUserById(Long userId) {
        return webClient.get()
            .uri("/users/{id}", userId)
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, 
                response -> Mono.error(new ClientErrorException(response.statusCode())))
            .onStatus(HttpStatus::is5xxServerError,
                response -> Mono.error(new ServerErrorException(response.statusCode())))
            .bodyToMono(UserDto.class)
            .timeout(Duration.ofSeconds(10))
            .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)));
    }
    
    // POST请求
    public Mono<ApiResponse> createOrder(CreateOrderRequest request) {
        return webClient.post()
            .uri("/orders")
            .body(BodyInserters.fromValue(request))
            .exchange()
            .flatMap(response -> {
                if (response.statusCode().is2xxSuccessful()) {
                    return response.bodyToMono(ApiResponse.class);
                } else {
                    return response.createError()
                        .flatMap(error -> Mono.error(new ApiException(error)));
                }
            })
            .subscribeOn(Schedulers.boundedElastic());
    }
    
    // PUT请求
    public Mono<Void> updateCart(Long userId, CartUpdateRequest request) {
        return webClient.put()
            .uri("/users/{userId}/cart", userId)
            .body(BodyInserters.fromValue(request))
            .exchange()
            .flatMap(response -> response.releaseBody().then(Mono.empty()));
    }
    
    // DELETE请求
    public Mono<Void> deleteItem(Long itemId) {
        return webClient.delete()
            .uri("/items/{id}", itemId)
            .exchange()
            .flatMap(response -> response.releaseBody().then(Mono.empty()));
    }
    
    // 流式响应
    public Flux<ProductDto> streamProducts(String category) {
        return webClient.get()
            .uri("/products?category={category}&stream=true", category)
            .accept(MediaType.TEXT_EVENT_STREAM)
            .retrieve()
            .bodyToFlux(ServerSentEvent.class)
            .mapNotNull(ServerSentEvent::data)
            .map(this::parseProductDto)
            .filter(Optional::isPresent)
            .map(Optional::get);
    }
    
    // 批量请求
    public Flux<ProductDto> getProductsBatch(List<Long> productIds) {
        return Flux.fromIterable(productIds)
            .flatMap(id -> webClient.get()
                .uri("/products/{id}", id)
                .retrieve()
                .bodyToMono(ProductDto.class),
                10) // 并发度为10
            .filter(Objects::nonNull);
    }
    
    // 条件请求
    public Mono<ProductDto> getProductWithConditional(Long productId, String etag) {
        return webClient.get()
            .uri("/products/{id}", productId)
            .ifNoneMatch(etag)
            .exchange()
            .flatMap(response -> {
                if (response.statusCode() == HttpStatus.NOT_MODIFIED) {
                    return Mono.empty(); // 内容未修改
                } else {
                    return response.bodyToMono(ProductDto.class);
                }
            });
    }
    
    // 超时和重试配置
    public Mono<ApiResponse> robustApiCall(String path) {
        return webClient.get()
            .uri(path)
            .exchange()
            .timeout(Duration.ofMillis(500))
            .retryWhen(Retry.backoff(5, Duration.ofMillis(100))
                .filter(throwable -> {
                    if (throwable instanceof WebClientResponseException) {
                        WebClientResponseException ex = (WebClientResponseException) throwable;
                        return ex.getStatusCode().is5xxServerError();
                    }
                    return throwable instanceof TimeoutException;
                }))
            .flatMap(response -> response.bodyToMono(ApiResponse.class));
    }
    
    private ExchangeFilterFunction logRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
            log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
            clientRequest.headers()
                .forEach((name, values) -> log.debug("  Header: {}={}", name, values));
            return Mono.just(clientRequest);
        });
    }
    
    private ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(response -> {
            log.info("Response: {}", response.statusCode());
            return Mono.just(response);
        });
    }
    
    private ExchangeFilterFunction circuitBreakerFilter() {
        return ExchangeFilterFunction.ofRequestProcessor(request ->
            Mono.fromCallable(() -> request)
                .transformDeferred(CircuitBreakerOperator.of(circuitBreaker))
                .flatMap(requestBody -> {
                    HttpHeaders headers = request.headers();
                    String correlationId = headers.getFirst("X-Correlation-ID");
                
                    return webClient.execute(clientRequest -> {
                        clientRequest.headers().set("X-Correlation-ID", correlationId);
                        return Mono.just(clientRequest);
                    });
                })
        );
    }
    
    private Optional<ProductDto> parseProductDto(Object data) {
        try {
            return Optional.ofNullable(objectMapper.convertValue(data, ProductDto.class));
        } catch (Exception e) {
            log.warn("Failed to parse product data", e);
            return Optional.empty();
        }
    }
}

响应式数据访问

R2DBC配置

响应式数据库配置

@Configuration
@EnableR2dbcRepositories
public class ReactiveDatabaseConfig {
    
    @Bean
    @ConfigurationProperties("app.datasource")
    public ConnectionFactoryOptions connectionFactoryOptions() {
        return ConnectionFactoryOptions.builder()
            .option(ConnectionFactoryOptions.DRIVER, "postgres")
            .option(ConnectionFactoryOptions.HOST, "localhost")
            .option(ConnectionFactoryOptions.PORT, 5432)
            .option(ConnectionFactoryOptions.DATABASE, "reactive_db")
            .option(ConnectionFactoryOptions.USER, "yourUsername")
            .option(ConnectionFactoryOptions.PASSWORD, "yourPassword")
            .option(ConnectionFactoryOptions.CONNECT_TIMEOUT, Duration.ofSeconds(10))
            .option(ConnectionFactoryOptions.STATEMENT_TIMEOUT, Duration.ofSeconds(30))
            .build();
    }
    
    @Bean
    public ConnectionFactory connectionFactory(@Autowired ConnectionFactoryOptions options) {
        return ConnectionFactories.get(options);
    }
    
    @Bean
    public R2dbcEntityTemplate r2dbcEntityTemplate(
            ConnectionFactories connectionFactory) {
        
        return new R2dbcEntityTemplate(connectionFactory, 
            new R2dbcDialect());
    }
    
    @Bean
    public ReactiveTransactionManager transactionManager(
            ConnectionFactories connectionFactory) {
        
        return new R2dbcTransactionManager(connectionFactory);
    }
}

// 响应式Repository
@Repository
public interface ReactiveOrderRepository extends ReactiveCrudRepository<Order, Long> {
    
    Flux<Order> findByUserId(Long userId);
    
    Flux<Order> findByStatus(OrderStatus status);
    
    Flux<Order> findByStatusAndCreatedDateBetween(OrderStatus status, 
                                                  LocalDateTime startDate, 
                                                  LocalDateTime endDate);
    
    @Query("SELECT * FROM orders WHERE total_amount > :amount")
    Flux<Order> findByTotalAmountGreaterThan(@Param("amount") BigDecimal amount);
    
    @Modifying
    @Query("UPDATE orders SET status = :status WHERE id = :id")
    Mono<Integer> updateOrderStatus(@Param("id") Long id, 
                                   @Param("status") OrderStatus status);
    
    @Query("SELECT COUNT(*) FROM orders WHERE user_id = :userId AND status = :status")
    Mono<Long> countByUserIdAndStatus(@Param("userId") Long userId, 
                                     @Param("status") OrderStatus status);
    
    Flux<Order> findByUserIdOrderByCreatedDateDesc(Long userId, Pageable pageable);
}

// 自定义Repository实现
@Repository
public class CustomReactiveOrderRepository {
    
    private final R2dbcEntityTemplate template;
    
    public CustomReactiveOrderRepository(R2dbcEntityTemplate template) {
        this.template = template;
    }
    
    public Flux<Order> findOrdersWithItems(Long userId) {
        return template.select(Order.class)
            .matching(Query.query(where("userId").is(userId)))
            .all()
            .flatMap(this::enrichWithItems);
    }
    
    public Mono<OrderDetails> findOrderDetails(Long orderId) {
        return template.select(Order.class)
            .matching(Query.query(where("id").is(orderId)))
            .one()
            .flatMap(this::buildOrderDetails);
    }
    
    public Flux<Order> findOrdersByCriteria(OrderSearchCriteria criteria) {
        Query query = Query.query(where("1").is(1));
        
        if (criteria.getUserId() != null) {
            query = query.with(where("userId").is(criteria.getUserId()));
        }
        
        if (criteria.getStatus() != null) {
            query = query.with(where("status").is(criteria.getStatus()));
        }
        
        if (criteria.getStartDate() != null && criteria.getEndDate() != null) {
            query = query.with(where("createdDate").between(criteria.getStartDate(), criteria.getEndDate()));
        }
        
        if (criteria.getMinAmount() != null) {
            query = query.with(where("totalAmount").greaterThan(criteria.getMinAmount()));
        }
        
        return template.select(Order.class)
            .matching(query)
            .all()
            .sort(Sort.by("createdDate").descending());
    }
    
    private Mono<Order> enrichWithItems(Order order) {
        return template.select(OrderItem.class)
            .matching(Query.query(where("orderId").is(order.getId())))
            .all()
            .collectList()
            .map(items -> {
                order.setItems(new ArrayList<>(items));
                return order;
            });
    }
    
    private Mono<OrderDetails> buildOrderDetails(Order order) {
        return Mono.zip(
            enrichWithUser(order),
            template.select(OrderItem.class)
                .matching(Query.query(where("orderId").is(order.getId())))
                .all()
                .collectList()
        ).map(tuple -> OrderDetails.builder()
            .order(order)
            .user(tuple.getT1())
            .items(tuple.getT2())
            .build());
    }
    
    private Mono<User> enrichWithUser(Order order) {
        return template.select(User.class)
            .matching(Query.query(where("id").is(order.getUserId())))
            .one();
    }
}

Spring Security集成

响应式安全配置

WebFlux安全配置

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ReactiveSecurityConfig {
    
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(
            ServerHttpSecurity http) {
        
        return http
            .authorizeExchange(exchanges -> exchanges
                .pathmatchers("/api/public/**").permitAll()
                .pathMatchers("/api/auth/**").permitAll()
                .pathMatchers("/actuator/**").hasRole("ADMIN")
                .pathMatchers("/api/admin/**").hasRole("ADMIN")
                .pathMatchers("/api/orders/**").hasAnyRole("USER", "ADMIN")
                .anyExchange().authenticated())
            
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwtSpec -> jwtSpec.jwtDecoder(jwtDecoder())))
            
            .csrf(csrf -> csrf.disable())
            .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
            .formLogin(ServerHttpSecurity.FormLoginSpec::disable)
            
            .exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint(authEntryPoint())
                .accessDeniedHandler(accessDeniedHandler()))
            
            .anonymous(anonymous -> anonymous.disable())
            
            .build();
    }
    
    @Bean
    public ReactiveJwtDecoder jwtDecoder() {
        return NimbusReactiveJwtDecoder.withJwkSetUri(jwtProperties.getJwkSetUri())
            .cache(Duration.ofMinutes(10))
            .build();
    }
    
    @Bean
    public ServerAuthenticationEntryPoint authEntryPoint() {
        return (exchange, ex) -> {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            
            String body = "{\"error\":\"Unauthorized\",\"message\":\"" + ex.getMessage() + "\"}";
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
            
            response.getHeaders().add("Content-Type", "application/json");
            return response.writeWith(Mono.just(buffer));
        };
    }
    
    @Bean
    public ServerAccessDeniedHandler accessDeniedHandler() {
        return (exchange, ex) -> {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.FORBIDDEN);
            
            String body = "{\"error\":\"Forbidden\",\"message\":\"Access denied\"}";
            DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
            
            response.getHeaders().add("Content-Type", "application/json");
            return response.writeWith(Mono.just(buffer));
        };
    }
}

// 响应式用户服务
@Service
public class ReactiveUserService {
    
    @Autowired
    private ReactiveUserRepository userRepository;
    
    @Autowired
    private ReactivePasswordEncoder passwordEncoder;
    
    public Mono<User> findByUsername(String username) {
        return userRepository.findByUsername(username)
            .switchIfEmpty(Mono.error(new UsernameNotFoundException("用户不存在: " + username)));
    }
    
    public Mono<User> createUser(CreateUserRequest request) {
        return Mono.just(request)
            .flatMap(this::validateUserRequest)
            .map(this::createUserEntity)
            .flatMap(user -> passwordEncoder.encode(request.getPassword())
                .doOnNext(user::setPassword))
            .flatMap(userRepository::save)
            .doOnSuccess(user -> log.info("用户创建成功: {}", user.getUsername()))
            .doOnError(error -> log.error("用户创建失败", error));
    }
    
    public Mono<Void> updateLastLogin(String username) {
        return userRepository.findByUsername(username)
            .flatMap(user -> {
                user.setLastLogin(LocalDateTime.now());
                return userRepository.save(user);
            })
            .then();
    }
    
    private Mono\<CreateUserRequest> validateUserRequest(CreateUserRequest request) {
        return Mono.just(request)
            .filter(req -> !StringUtils.hasText(req.getUsername()))
            .switchIfEmpty(Mono.error(new ValidationException("用户名不能为空")))
            .filter(req -> !StringUtils.hasText(req.getEmail()))
            .switchIfEmpty(Mono.error(new ValidationException("邮箱不能为空")))
            .filter(req -> !StringUtils.hasText(req.getPassword()))
            .switchIfEmpty(Mono.error(new ValidationException("密码不能为空")));
    }
    
    private User createUserEntity(CreateUserRequest request) {
        return User.builder()
            .username(request.getUsername())
            .email(request.getEmail())
            .firstName(request.getFirstName())
            .lastName(request.getLastName())
            .status(UserStatus.ACTIVE)
            .role(UserRole.USER)
            .createdDate(LocalDateTime.now())
            .build();
    }
}

总结

Spring WebFlux响应式编程为企业级应用提供了高性能的非阻塞异步处理方案:

核心技术要点

  1. 响应式编程模型 - Reactor Mono/Flux异步数据流处理
  2. 非阻塞I/O - 基于事件循环的高并发处理能力
  3. 函数式路由 - RouterFunction和HandlerFunction声明式路由
  4. 响应式客户端 - WebClient异步HTTP客户端
  5. 响应式数据访问 - R2DBC非阻塞数据库访问
  6. 安全集成 - Spring Security Reactive安全保障
  7. 背压处理 - 自动处理流量控制和背压

性能优势对比

性能表现差异

  • 并发能力:WebFlux > 10000并发连接 vs MVC < 1000连接
  • 内存使用:WebFlux更低线程开销,更高内存效率
  • CPU利用率:WebFlux充分利用多核CPU资源
  • 延迟性能:WebFlux更低延迟,更好用户体验

适用场景分析

WebFlux适用场景

  • 高并发I/O密集型:微服务、API网关、代理服务
  • 实时数据处理:消息流、事件处理、实时推送
  • 网络延迟敏感:分布式系统、远程调用密集型
  • 资源受限环境:容器化部署、云原生应用

MVC仍适用场景

  • CPU密集型任务:复杂的业务逻辑计算
  • 简单CRUD应用:传统三层架构应用
  • 团队技术栈限制:团队对响应式编程不熟悉

最佳实践建议

  1. 渐进式迁移:从新模块开始使用WebFlux,逐步迁移现有模块
  2. 性能测试:充分测试响应式代码的网络I/O性能
  3. 错误处理:建立完善的错误处理和重试机制
  4. 监控调试:响应式流的可视化监控和调试工具
  5. 团队培训:投入时间学习响应式编程范式

技术选型决策

推荐使用WebFlux的情况

  • 应用需要处理大量并发连接
  • 对外部服务的调用较多
  • 需要处理流式数据(如SSE、WebSocket)
  • 追求极致的性能和资源利用率
  • 团队有深入学习响应式编程的意愿

继续使用MVC的情况

  • 传统CRUD应用,并发量不高
  • 团队对响应式编程接受度低
  • 复杂的业务逻辑和事务处理
  • 第三方框架对响应式支持不完善

通过Spring WebFlux,小伙伴们可以在Spring生态系统中体验到顶级的响应式编程能力,构建高性能、高并发、资源效率的现代Web应用!

您可能感兴趣的与本文相关的镜像

FLUX.1-dev

FLUX.1-dev

图片生成
FLUX

FLUX.1-dev 是一个由 Black Forest Labs 创立的开源 AI 图像生成模型版本,它以其高质量和类似照片的真实感而闻名,并且比其他模型更有效率

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小凯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值