目录
- 响应式编程概述
- Reactor编程模型
- WebFlux架构
- RouterFunction与HandlerFunction
- 响应式Web客户端
- 响应式数据访问
- Spring Security集成
- 测试策略
- 性能优化
- 监控与调试
- 最佳实践
- 总结
响应式编程概述
响应式编程优势
响应式编程是一种面向数据流和变化的编程范式:
核心优势:
- 非阻塞I/O:避免线程阻塞,提高并发性能
- 背压处理:自动处理生产者消费者速度不匹配
- 弹性架构:支持失败恢复和自动扩展
- 异步处理:充分利用多核CPU资源
Spring MVC vs WebFlux
两种编程模型对比:
| 特性 | Spring MVC | Spring 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响应式编程为企业级应用提供了高性能的非阻塞异步处理方案:
核心技术要点
- 响应式编程模型 - Reactor Mono/Flux异步数据流处理
- 非阻塞I/O - 基于事件循环的高并发处理能力
- 函数式路由 - RouterFunction和HandlerFunction声明式路由
- 响应式客户端 - WebClient异步HTTP客户端
- 响应式数据访问 - R2DBC非阻塞数据库访问
- 安全集成 - Spring Security Reactive安全保障
- 背压处理 - 自动处理流量控制和背压
性能优势对比
性能表现差异:
- 并发能力:WebFlux > 10000并发连接 vs MVC < 1000连接
- 内存使用:WebFlux更低线程开销,更高内存效率
- CPU利用率:WebFlux充分利用多核CPU资源
- 延迟性能:WebFlux更低延迟,更好用户体验
适用场景分析
WebFlux适用场景:
- 高并发I/O密集型:微服务、API网关、代理服务
- 实时数据处理:消息流、事件处理、实时推送
- 网络延迟敏感:分布式系统、远程调用密集型
- 资源受限环境:容器化部署、云原生应用
MVC仍适用场景:
- CPU密集型任务:复杂的业务逻辑计算
- 简单CRUD应用:传统三层架构应用
- 团队技术栈限制:团队对响应式编程不熟悉
最佳实践建议
- 渐进式迁移:从新模块开始使用WebFlux,逐步迁移现有模块
- 性能测试:充分测试响应式代码的网络I/O性能
- 错误处理:建立完善的错误处理和重试机制
- 监控调试:响应式流的可视化监控和调试工具
- 团队培训:投入时间学习响应式编程范式
技术选型决策
推荐使用WebFlux的情况:
- 应用需要处理大量并发连接
- 对外部服务的调用较多
- 需要处理流式数据(如SSE、WebSocket)
- 追求极致的性能和资源利用率
- 团队有深入学习响应式编程的意愿
继续使用MVC的情况:
- 传统CRUD应用,并发量不高
- 团队对响应式编程接受度低
- 复杂的业务逻辑和事务处理
- 第三方框架对响应式支持不完善
通过Spring WebFlux,小伙伴们可以在Spring生态系统中体验到顶级的响应式编程能力,构建高性能、高并发、资源效率的现代Web应用!
7208

被折叠的 条评论
为什么被折叠?



