2.Spring MVC与WebFlux响应式编程

打印 上一主题 下一主题

主题 1714|帖子 1714|积分 5142

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目录

  一、Spring MVC核心机制与工作原理

  • 哀求处理流程:DispatcherServlet分发机制、HandlerMapping与HandlerAdapter • 核心组件:ViewResolver、ModelAndView、拦截器(Interceptor) • 注解驱动开发:@Controller、@RequestMapping、@RequestBody/@ResponseBody • RESTful支持:@RestController、@PathVariable、@RequestParam • 异常处理:@ControllerAdvice、@ExceptionHandler、全局异常响应
  
  二、WebFlux响应式编程模型

  • 响应式核心概念:Reactive Streams规范、背压(Backpressure)机制 • WebFlux vs MVC:非壅闭IO、事件驱动模型、适用场景对比 • 核心组件:RouterFunction、HandlerFunction、WebFilter • 响应式类型:Mono与Flux操作符(map、flatMap、zip) • 响应式数据访问:R2DBC(关系型数据库)、Reactive MongoDB
  
  三、同步与异步处理对比

  • 壅闭式处理(MVC):Servlet线程模型、Tomcat线程池设置 • 非壅闭式处理(WebFlux):EventLoop线程模型、Netty性能优势 • 性能压测对比:1000并发下MVC(Tomcat) vs WebFlux(Netty)吞吐量 • 混淆应用场景:MVC同步接口与WebFlux异步接口共存计谋
  
  四、实战:构建混淆应用(MVC+WebFlux)

  • 项目架构设计:Spring Boot多模块设置(MVC模块 + WebFlux模块) • 接口兼容性:同一返回格式(JSON)、全局跨域设置 • 异步接口开发:基于WebFlux的文件上传/下载、SSE(Server-Sent Events)实时推送 • 灰度发布计谋:通过网关(Spring Cloud Gateway)动态路由流量
  
  五、性能优化与生产调优

  • MVC优化:Tomcat线程池参数(maxThreads、acceptCount)、静态资源缓存 • WebFlux优化:Netty事件循环组设置、响应式超时控制 • 内存泄漏排查:堆内存分析(MAT工具)、壅闭操作检测(BlockHound) • 监控告警:Micrometer集成Prometheus、WebFlux链路追踪(Sleuth + Zipkin)
  
  六、常见问题与面试题精选

  • 高频面试题: • Spring MVC怎样处理HTTP哀求?DispatcherServlet的作用是什么? • WebFlux的背压机制怎样解决数据斲丧速度不匹配问题? • 为什么WebFlux默认使用Netty而不是Tomcat? • 实战场景题: • 设计一个支持10万并发的实时股票报价接口(WebFlux + SSE)。 • 优化一个Spring MVC接口从200ms耽误降低到50ms以下。 • 陷阱题: • 在WebFlux中调用壅闭代码(如JDBC)会导致什么问题?怎样解决? • 为什么WebFlux的Mono返回类型不能直接序列化为XML?
  
  
  一、Spring MVC核心机制与工作原理

  1. 哀求处理全流程分析

  Spring MVC的哀求处理流程围绕DispatcherServlet睁开,其核心流程如下:
  

  • HTTP哀求接收:DispatcherServlet作为前端控制器,接收全部HTTP哀求。
  • 处理器映射(HandlerMapping): • 根据哀求URL匹配对应的控制器方法(如@RequestMapping定义的路径)。 • 支持多种匹配计谋(路径通配符、正则表达式)。
    1. @GetMapping("/users/{id}")  
    2. public User getUser(@PathVariable Long id) { ... }  
    复制代码
  • 处理器适配(HandlerAdapter): • 调用目的控制器方法,处理参数绑定(如@RequestBody解析JSON)。 • 支持多种参数类型(HttpServletRequest、Model、自定义POJO)。
  • 视图解析(ViewResolver): • 根据逻辑视图名(如return "userList";)解析为物理视图(如userList.jsp)。 • 支持模板引擎(Thymeleaf、FreeMarker)。
  
  2. 核心组件详解

  2.1 ViewResolver与模板渲染

  • 视图解析流程
  1. // 配置Thymeleaf视图解析器  
  2. @Bean  
  3. public ThymeleafViewResolver viewResolver() {  
  4.     ThymeleafViewResolver resolver = new ThymeleafViewResolver();  
  5.     resolver.setTemplateEngine(templateEngine());  
  6.     return resolver;  
  7. }  
复制代码
动态数据传递:通过Model对象传递数据到视图。
  1. @GetMapping("/profile")  
  2. public String profile(Model model) {  
  3.     model.addAttribute("user", userService.getCurrentUser());  
  4.     return "profile";  
  5. }  
复制代码
2.2 拦截器(Interceptor)实战

  • 自定义拦截器:实现HandlerInterceptor接口,完成日志、权限等操作。
  1. public class AuthInterceptor implements HandlerInterceptor {  
  2.     @Override  
  3.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {  
  4.         if (!checkToken(request.getHeader("token"))) {  
  5.             response.sendError(401, "Unauthorized");  
  6.             return false;  
  7.         }  
  8.         return true;  
  9.     }  
  10. }  
复制代码
设置拦截路径
  1. @Configuration  
  2. public class WebConfig implements WebMvcConfigurer {  
  3.     @Override  
  4.     public void addInterceptors(InterceptorRegistry registry) {  
  5.         registry.addInterceptor(new AuthInterceptor())  
  6.                 .addPathPatterns("/api/**");  
  7.     }  
  8. }  
复制代码

  3. 注解驱动开发

  3.1 控制器层注解

  • @Controller vs @RestController: • @Controller:用于传统MVC模式,共同视图解析器返回HTML页面。 • @RestController:用于RESTful API,全部方法默认添加@ResponseBody,返回JSON/XML数据。
  3.2 哀求映射注解

  • 多条件匹配
  1. @RequestMapping(  
  2.     value = "/search",  
  3.     method = RequestMethod.GET,  
  4.     params = "keyword",  
  5.     headers = "Content-Type=application/json"  
  6. )  
  7. public List<Product> searchProducts() { ... }  
复制代码
3.3 参数绑定注解

  • @RequestBody与HttpMessageConverter: • JSON反序列化流程: 1. 哀求头Content-Type: application/json触发MappingJackson2HttpMessageConverter。 2. 将哀求体JSON转换为Java对象。
  1. @PostMapping("/users")  
  2. public ResponseEntity<User> createUser(@RequestBody User user) {  
  3.     User savedUser = userService.save(user);  
  4.     return ResponseEntity.created(URI.create("/users/" + savedUser.getId())).body(savedUser);  
  5. }  
复制代码

  4. RESTful接口设计规范

  4.1 资源操作语义化

  • HTTP方法对应CRUD
  HTTP方法操作示例GET查询资源GET /users/123POST创建资源POST /usersPUT全量更新PUT /users/123PATCH部分更新PATCH /users/123DELETE删除资源DELETE /users/123  4.2 HATEOAS实现

  • Spring HATEOAS集成
  1. @GetMapping("/users/{id}")  
  2. public EntityModel<User> getUser(@PathVariable Long id) {  
  3.     User user = userService.findById(id);  
  4.     return EntityModel.of(user,  
  5.         linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),  
  6.         linkTo(methodOn(UserController.class).getUserOrders(id)).withRel("orders")  
  7.     );  
  8. }  
复制代码
响应示例
  1. {  
  2.   "id": 123,  
  3.   "name": "John",  
  4.   "_links": {  
  5.     "self": { "href": "http://localhost:8080/users/123" },  
  6.     "orders": { "href": "http://localhost:8080/users/123/orders" }  
  7.   }  
  8. }  
复制代码

  5. 全局异常处理

  5.1 同一异常响应

  • 自定义异常类
  1. public class ResourceNotFoundException extends RuntimeException {  
  2.     public ResourceNotFoundException(String message) {  
  3.         super(message);  
  4.     }  
  5. }  
复制代码
全局异常处理器
  1. @ControllerAdvice  
  2. public class GlobalExceptionHandler {  
  3.     @ExceptionHandler(ResourceNotFoundException.class)  
  4.     public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {  
  5.         ErrorResponse error = new ErrorResponse(404, ex.getMessage());  
  6.         return ResponseEntity.status(404).body(error);  
  7.     }  
  8. }  
复制代码
错误响应体
  1. {  
  2.   "code": 404,  
  3.   "message": "User not found with id 123"  
  4. }  
复制代码

  总结 本章深入解析了Spring MVC的核心机制,从哀求处理流程到RESTful接口设计,覆盖了控制器开发、参数绑定、异常处理等关键内容。后续章节将对比WebFlux响应式编程模型,帮助开发者根据业务场景选择最佳技术方案。
  
  二、WebFlux响应式编程模型

  1. 响应式核心概念

  1.1 Reactive Streams规范

  Reactive Streams是响应式编程的标准化规范,定义了以下核心接口: • Publisher<T>:数据生产者,发布数据流。 • Subscriber<T>:数据斲丧者,订阅并处理数据。 • Subscription:订阅关系,控制数据哀求(如request(n))。 • Processor<T, R>:同时充当生产者和斲丧者的中间处理节点。
  代码示例(简朴Publisher)
  1. Flux<Integer> flux = Flux.range(1, 10)  
  2.                          .delayElements(Duration.ofMillis(100));  
  3. flux.subscribe(  
  4.     value -> System.out.println("Received: " + value),  
  5.     error -> System.err.println("Error: " + error),  
  6.     () -> System.out.println("Completed")  
  7. );  
复制代码
1.2 背压(Backpressure)机制

  • 问题场景:生产者速度 > 斲丧者速度,导致内存溢出。 • 解决方案:通过Subscription动态控制数据流速。
  1. Flux.range(1, 1000)  
  2.     .onBackpressureBuffer(10)  // 缓冲区大小为10,超出后丢弃旧数据  
  3.     .subscribe(new BaseSubscriber<Integer>() {  
  4.         @Override  
  5.         protected void hookOnSubscribe(Subscription subscription) {  
  6.             request(5);  // 初始请求5个元素  
  7.         }  
  8.         @Override  
  9.         protected void hookOnNext(Integer value) {  
  10.             process(value);  
  11.             request(1);  // 每处理完1个元素,再请求1个  
  12.         }  
  13.     });  
复制代码

  2. WebFlux vs MVC:模型对比与适用场景

  2.1 非壅闭IO与事件驱动

  • MVC(壅闭式): • 每个哀求占用一个线程(Tomcat线程池默认200线程)。 • 高并发时线程资源耗尽,导致性能瓶颈。 • WebFlux(非壅闭): • 基于事件循环(EventLoop),单线程处理数千连接。 • 适用I/O密集型场景(如微服务网关、实时推送)。
  2.2 性能对比

  指标Spring MVC(Tomcat)WebFlux(Netty)吞吐量(req/s)5k15k内存占用高(每个线程1MB)低(共享线程池)适用场景CRUD、同步逻辑高并发、实时流  2.3 混淆应用场景

  • 用例:电商体系同时提供管理后台(MVC)和实时订单推送(WebFlux)。 • 设置示例
  1. # application.yml  
  2. server:  
  3.   port: 8080  
  4.   servlet:  
  5.     context-path: /mvc  
  6. spring:  
  7.   webflux:  
  8.     base-path: /reactive  
复制代码

  3. 核心组件详解

  3.1 函数式端点(RouterFunction & HandlerFunction)

  • 路由定义:使用RouterFunctions.route()组合哀求谓词和处理器。
  1. @Bean  
  2. public RouterFunction<ServerResponse> userRoutes() {  
  3.     return route()  
  4.         .GET("/users/{id}", this::getUser)  
  5.         .POST("/users", this::createUser)  
  6.         .build();  
  7. }  
  8. private Mono<ServerResponse> getUser(ServerRequest request) {  
  9.     Long id = Long.parseLong(request.pathVariable("id"));  
  10.     return ServerResponse.ok().body(userService.findById(id), User.class);  
  11. }  
复制代码
3.2 响应式过滤器(WebFilter)

  • 日志纪录示例
  1. @Component  
  2. public class LoggingWebFilter implements WebFilter {  
  3.     @Override  
  4.     public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {  
  5.         long startTime = System.currentTimeMillis();  
  6.         return chain.filter(exchange)  
  7.             .doOnTerminate(() -> {  
  8.                 long duration = System.currentTimeMillis() - startTime;  
  9.                 System.out.println(  
  10.                     exchange.getRequest().getURI() + " - " + duration + "ms"  
  11.                 );  
  12.             });  
  13.     }  
  14. }  
复制代码

  4. 响应式类型与操作符

  4.1 Mono与Flux核心操作

  操作符用途示例map同步转换元素Flux.just(1,2).map(i -> i*2)flatMap异步转换(返回Publisher)Flux.just(1,2).flatMap(i -> Mono.just(i*2))zip归并多个流(按元素顺序配对)Mono.zip(monoA, monoB, (a,b) -> a + b)merge归并多个流(按元素到达顺序)Flux.merge(fluxA, fluxB)  代码示例(实时数据流处理)
  1. Flux<StockPrice> prices = WebClient.create()  
  2.     .get()  
  3.     .uri("http://stock-service/prices")  
  4.     .retrieve()  
  5.     .bodyToFlux(StockPrice.class)  
  6.     .filter(price -> price.getValue() > 100)  
  7.     .bufferTimeout(10, Duration.ofSeconds(1))  
  8.     .flatMap(batch -> saveBatchToDB(batch));  
复制代码

  5. 响应式数据访问

  5.1 R2DBC(关系型数据库)

  • 核心依赖
  1. <dependency>  
  2.     <groupId>io.r2dbc</groupId>  
  3.     <artifactId>r2dbc-postgresql</artifactId>  
  4. </dependency>  
复制代码
CRUD示例
  1. public Flux<User> findAll() {  
  2.     return databaseClient.sql("SELECT * FROM users")  
  3.                         .map(row -> new User(row.get("id", Long.class), row.get("name", String.class)))  
  4.                         .all();  
  5. }  
复制代码
5.2 Reactive MongoDB

  • 查询与订阅
  1. public Flux<User> streamUsers() {  
  2.     return mongoTemplate.changeStream(User.class)  
  3.         .watchCollection("users")  
  4.         .listen()  
  5.         .map(ChangeStreamEvent::getBody);  
  6. }  
复制代码

  总结 WebFlux通过非壅闭IO和响应式编程模型,为高并发场景提供了高效的解决方案。团结R2DBC和Reactive MongoDB,开发者可以构建端到端的响应式应用。下一章将深入探讨同步与异步处理的性能差异及混淆应用架构设计。
  
  三、同步与异步处理对比

  
  1. 壅闭式处理(Spring MVC)

  1.1 Servlet线程模型与Tomcat设置

  • 线程模型: • 每个HTTP哀求占用一个独立线程(Tomcat默认线程池大小为200)。 • 线程从接收哀求到返回响应的全流程中,若碰到壅闭操作(如数据库查询、远程调用),线程会被挂起,直到操作完成。 • 设置优化
  1. # application.yml  
  2. server:  
  3.   tomcat:  
  4.     max-threads: 200      # 最大工作线程数  
  5.     accept-count: 100     # 等待队列容量  
  6.     min-spare-threads: 10 # 最小空闲线程数  
复制代码
性能瓶颈: • 当并发哀求超过max-threads + accept-count时(如300并发),Tomcat直接拒绝哀求(返回503)。 • 长耗时操作(如文件导出)大概导致线程池耗尽,影响其他哀求。
  1.2 典型同步接口示例

  1. @RestController  
  2. public class BlockingController {  
  3.     @GetMapping("/blocking/users/{id}")  
  4.     public User getUser(@PathVariable Long id) {  
  5.         // 模拟耗时1秒的数据库查询(阻塞线程)  
  6.         try { Thread.sleep(1000); } catch (InterruptedException e) {}  
  7.         return new User(id, "User" + id);  
  8.     }  
  9. }  
复制代码

  2. 非壅闭式处理(WebFlux)

  2.1 EventLoop线程模型与Netty优势

  • EventLoop机制: • 单线程(EventLoop)通过事件驱动处理多个连接(如1000并发)。 • 全部I/O操作(如网络哀求、数据库调用)均为非壅闭,通过回调通知结果。 • Netty核心参数
  1. # application.yml  
  2. spring:  
  3.   webflux:  
  4.     server:  
  5.       max-in-memory-size: 10MB    # 请求体内存缓冲大小  
  6.       thread-pool:  
  7.         max-threads: 4            # 事件循环线程数(通常设为CPU核数)  
复制代码
性能优势: • 高并发下内存占用稳定(无线程上下文切换开销)。 • 适用于实时流处理(如股票行情推送、物联网设备数据采集)。
  2.2 典型异步接口示例

  1. @RestController  
  2. public class ReactiveController {  
  3.     @GetMapping("/reactive/users/{id}")  
  4.     public Mono<User> getUser(@PathVariable Long id) {  
  5.         // 模拟异步数据库查询(不阻塞线程)  
  6.         return Mono.delay(Duration.ofSeconds(1))  
  7.                    .map(delay -> new User(id, "User" + id));  
  8.     }  
  9. }  
复制代码

  3. 性能压测对比(MVC vs WebFlux)

  3.1 压测环境与参数

  • 工具:Apache JMeter(1000并发,一连10秒)。 • 接口逻辑:模仿1秒耽误的查询操作。 • 服务器设置:4核CPU,8GB内存,Spring Boot 3.0。
  3.2 压测结果

  指标Spring MVC(Tomcat)WebFlux(Netty)吞吐量(req/s)200950平均响应时间1000ms1050ms最大内存占用1.2GB300MB线程占用数2004  结论: • WebFlux在高并发场景下吞吐量提拔近5倍,内存占用降低75%。 • MVC的响应时间更稳定,但受限于线程池容量。
  
  4. 混淆应用场景与共存计谋

  4.1 技术选型原则

  • 使用MVC的场景: • 依赖壅闭式组件(如JDBC、JPA)。 • 简朴CRUD接口,无需高并发(如管理后台)。 • 使用WebFlux的场景: • 高并发、低耽误需求(如API网关、实时推送)。 • 依赖非壅闭数据源(如R2DBC、Reactive MongoDB)。
  4.2 混淆架构实现

  • 项目设置
  1. // build.gradle  
  2. dependencies {  
  3.     implementation 'org.springframework.boot:spring-boot-starter-web'      // MVC  
  4.     implementation 'org.springframework.boot:spring-boot-starter-webflux'  // WebFlux  
  5. }  
复制代码
路由计谋:通过@Order控制处理器优先级。
  1. @Configuration  
  2. public class WebConfig implements WebFluxConfigurer {  
  3.     @Bean  
  4.     @Order(-1)  // WebFlux优先处理  
  5.     public RouterFunction<ServerResponse> reactiveRoutes() {  
  6.         return route()  
  7.             .GET("/reactive/**", request -> ServerResponse.ok().build())  
  8.             .build();  
  9.     }  
  10. }  
复制代码
4.3 线程池隔离

  • MVC线程池:Tomcat默认线程池处理同步哀求。 • WebFlux线程池:Netty EventLoop处理异步哀求。 • 关键设置
  1. spring:  
  2.   task:  
  3.     execution:  
  4.       pool:  
  5.         core-size: 10  # 异步任务线程池(避免阻塞EventLoop)  
复制代码

  总结

  • 同步模型(MVC):得当简朴业务、壅闭式数据源,开发门槛低。 • 异步模型(WebFlux):得当高并发、实时流,但需重构为全响应式链路。 • 混淆架构:通过路由和线程池隔离,平衡开发效率与性能需求。
  生产建议: • 核心服务(如支付回调)使用WebFlux包管高可用。 • 复杂事务操作(如库存扣减)优先选择MVC+JDBC。 • 使用BlockHound检测壅闭调用,确保响应式代码纯净性。
  1. // BlockHound配置(检测阻塞操作)  
  2. public class BlockHoundConfig {  
  3.     @PostConstruct  
  4.     public void init() {  
  5.         BlockHound.builder()  
  6.             .allowBlockingCallsInside("com.example.MyService", "safeBlockingMethod")  
  7.             .install();  
  8.     }  
  9. }  
复制代码

  四、实战:构建混淆应用(MVC+WebFlux)

  
  1. 项目架构设计

  1.1 多模块工程设置

  • 模块分别: • mvc-module:处理同步哀求(依赖spring-boot-starter-web)。 • webflux-module:处理异步哀求(依赖spring-boot-starter-webflux)。 • common-module:共享DTO、工具类、异常处理。
  • Maven/Gradle设置
  1. // build.gradle(根项目)  
  2. subprojects {  
  3.     apply plugin: 'org.springframework.boot'  
  4.     dependencies {  
  5.         implementation project(':common-module')  
  6.     }  
  7. }  
  8. // mvc-module/build.gradle  
  9. dependencies {  
  10.     implementation 'org.springframework.boot:spring-boot-starter-web'  
  11. }  
  12. // webflux-module/build.gradle  
  13. dependencies {  
  14.     implementation 'org.springframework.boot:spring-boot-starter-webflux'  
  15. }  
复制代码
启动类隔离
  1. // MVC模块启动类  
  2. @SpringBootApplication  
  3. @EnableAutoConfiguration(exclude = {ReactiveWebServerFactoryAutoConfiguration.class})  
  4. public class MvcApplication { ... }  
  5. // WebFlux模块启动类  
  6. @SpringBootApplication  
  7. @EnableAutoConfiguration(exclude = {ServletWebServerFactoryAutoConfiguration.class})  
  8. public class WebfluxApplication { ... }  
复制代码

  2. 接口兼容性与同一规范

  2.1 同一JSON响应格式

  • 全局响应封装(common-module中定义):
  1. public class ApiResponse<T> {  
  2.     private int code;  
  3.     private String message;  
  4.     private T data;  
  5.     // Getter/Setter  
  6. }  
复制代码
MVC同一返回(@ControllerAdvice):
  1. @ControllerAdvice  
  2. public class MvcResponseWrapper implements ResponseBodyAdvice<Object> {  
  3.     @Override  
  4.     public boolean supports(MethodParameter returnType, Class converterType) {  
  5.         return !returnType.getGenericParameterType().equals(ApiResponse.class);  
  6.     }  
  7.     @Override  
  8.     public Object beforeBodyWrite(Object body, MethodParameter returnType,  
  9.             MediaType selectedContentType, Class selectedConverterType,  
  10.             ServerHttpRequest request, ServerHttpResponse response) {  
  11.         return new ApiResponse<>(200, "Success", body);  
  12.     }  
  13. }  
复制代码
WebFlux同一返回(全局过滤器):
  1. @Component  
  2. public class WebfluxResponseFilter implements WebFilter {  
  3.     @Override  
  4.     public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {  
  5.         return chain.filter(exchange)  
  6.             .then(Mono.defer(() -> {  
  7.                 ServerHttpResponse response = exchange.getResponse();  
  8.                 if (response.getStatusCode() == HttpStatus.OK) {  
  9.                     Object body = response.getBody();  
  10.                     return response.writeWith(Mono.just(  
  11.                         response.bufferFactory().wrap(  
  12.                             new ApiResponse<>(200, "Success", body).toString().getBytes()  
  13.                         )  
  14.                     ));  
  15.                 }  
  16.                 return Mono.empty();  
  17.             }));  
  18.     }  
  19. }  
复制代码
2.2 全局跨域设置

  • MVC跨域设置
  1. @Configuration  
  2. public class MvcCorsConfig implements WebMvcConfigurer {  
  3.     @Override  
  4.     public void addCorsMappings(CorsRegistry registry) {  
  5.         registry.addMapping("/**")  
  6.                 .allowedOrigins("*")  
  7.                 .allowedMethods("GET", "POST");  
  8.     }  
  9. }  
复制代码
WebFlux跨域设置
  1. @Configuration  
  2. public class WebfluxCorsConfig {  
  3.     @Bean  
  4.     public CorsWebFilter corsFilter() {  
  5.         CorsConfiguration config = new CorsConfiguration();  
  6.         config.addAllowedOrigin("*");  
  7.         config.addAllowedMethod("*");  
  8.         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
  9.         source.registerCorsConfiguration("/**", config);  
  10.         return new CorsWebFilter(source);  
  11.     }  
  12. }  
复制代码

  3. 异步接口开发实战

  3.1 文件上传与下载

  • WebFlux文件上传
  1. @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)  
  2. public Mono<String> uploadFile(@RequestPart("file") FilePart filePart) {  
  3.     Path path = Paths.get("/tmp/" + filePart.filename());  
  4.     return filePart.transferTo(path).thenReturn("Upload success");  
  5. }  
复制代码
WebFlux文件下载
  1. @GetMapping("/download/{filename}")  
  2. public Mono<Resource> downloadFile(@PathVariable String filename) {  
  3.     return Mono.fromSupplier(() -> new FileSystemResource("/tmp/" + filename));  
  4. }  
复制代码
3.2 SSE实时推送

  • 服务端实现
  1. @GetMapping(value = "/stream/prices", produces = MediaType.TEXT_EVENT_STREAM_VALUE)  
  2. public Flux<StockPrice> streamPrices() {  
  3.     return Flux.interval(Duration.ofSeconds(1))  
  4.                .map(sequence -> new StockPrice("AAPL", 150 + sequence * 0.5));  
  5. }  
复制代码
客户端订阅
  1. const eventSource = new EventSource('/stream/prices');  
  2. eventSource.onmessage = (e) => console.log(JSON.parse(e.data));  
复制代码

  4. 灰度发布与动态路由

  4.1 Spring Cloud Gateway设置

  • 路由规则(按Header分流):
  1. spring:  
  2.   cloud:  
  3.     gateway:  
  4.       routes:  
  5.         - id: mvc-route  
  6.           uri: http://localhost:8081  
  7.           predicates:  
  8.             - Header=X-Gray, v1  
  9.         - id: webflux-route  
  10.           uri: http://localhost:8082  
  11.           predicates:  
  12.             - Header=X-Gray, v2  
复制代码
4.2 金丝雀发布计谋

  • 按权重分流
  1. spring:  
  2.   cloud:  
  3.     gateway:  
  4.       routes:  
  5.         - id: canary-route  
  6.           uri: lb://my-service  
  7.           predicates:  
  8.             - Path=/api/**  
  9.           filters:  
  10.             - name: Weight  
  11.               args:  
  12.                 group: canary  
  13.                 weight: v1, 90  
  14.                 weight: v2, 10  
复制代码

  5. 混淆应用监控与调优

  • 线程池隔离设置
  1. # WebFlux线程池(避免阻塞EventLoop)  
  2. spring:  
  3.   task:  
  4.     execution:  
  5.       pool:  
  6.         core-size: 10  
  7.         max-size: 20  
复制代码
监控集成
  1. management:  
  2.   endpoints:  
  3.     web:  
  4.       exposure:  
  5.         include: health,metrics,threaddump  
  6.   metrics:  
  7.     tags:  
  8.       application: ${spring.application.name}  
复制代码

  总结

  通过混淆架构设计,开发者既能保留MVC的简朴易用性,又能利用WebFlux处理高并发场景。关键点包括:
  

  • 模块隔离:通过多模块工程克制依赖冲突。
  • 同一规范:全局处理响应格式与跨域设置。
  • 动态路由:团结Spring Cloud Gateway实现灰度发布。
  • 监控保障:线程池隔离与全链路监控确保稳定性。
  适用场景: • 核心交易体系(MVC包管事务一致性)。 • 实时通知服务(WebFlux支持高并发推送)。 • 逐步迁徙旧体系(通过灰度计谋平滑过渡)。
  
  五、性能优化与生产调优

  
  1. Spring MVC性能优化

  1.1 Tomcat线程池参数调优

  • 核心参数
  1. server:  
  2.   tomcat:  
  3.     max-threads: 200       # 最大工作线程数(建议值:CPU核数 * 200)  
  4.     accept-count: 100      # 等待队列容量(超出后拒绝请求)  
  5.     min-spare-threads: 20  # 最小空闲线程数(快速响应突发流量)  
复制代码
调优原则: • CPU密集型场景:max-threads设为CPU核数 * 1.5。 • I/O密集型场景:max-threads可得当增大(如200~400)。
  1.2 静态资源缓存

  • HTTP缓存头设置
  1. @Configuration  
  2. public class MvcCacheConfig implements WebMvcConfigurer {  
  3.     @Override  
  4.     public void addResourceHandlers(ResourceHandlerRegistry registry) {  
  5.         registry.addResourceHandler("/static/**")  
  6.                 .addResourceLocations("classpath:/static/")  
  7.                 .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS));  
  8.     }  
  9. }  
复制代码
Nginx缓存加速
  1. location /static/ {  
  2.     expires 30d;  
  3.     add_header Cache-Control "public";  
  4. }  
复制代码

  2. WebFlux性能优化

  2.1 Netty事件循环组设置

  • 线程模型优化
  1. # application.yml  
  2. spring:  
  3.   webflux:  
  4.     server:  
  5.       # EventLoop线程数(默认:CPU核数 * 2)  
  6.       thread-pool:  
  7.         max-threads: 8  
复制代码
参数调优: • Linux体系:启用Epoll传输(淘汰线程切换开销)。 java @Bean public NettyReactiveWebServerFactory nettyFactory() { return new NettyReactiveWebServerFactory( builder -> builder.option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.TCP_NODELAY, true) ); }
  2.2 响应式超时控制

  • 全局超时设置
  1. @Bean  
  2. public WebClient webClient() {  
  3.     return WebClient.builder()  
  4.         .clientConnector(new ReactorClientHttpConnector(  
  5.             HttpClient.create()  
  6.                 .responseTimeout(Duration.ofSeconds(5))  
  7.         ))  
  8.         .build();  
  9. }  
复制代码
接口级超时
  1. @GetMapping("/reactive/data")  
  2. public Mono<Data> getData() {  
  3.     return externalService.fetchData()  
  4.             .timeout(Duration.ofSeconds(3))  
  5.             .onErrorResume(e -> Mono.just(new Data("fallback")));  
  6. }  
复制代码

  3. 内存泄漏排查与壅闭检测

  3.1 堆内存分析(MAT工具)

  • 天生堆转储文件
  1. # 通过jmap生成  
  2. jmap -dump:format=b,file=heapdump.hprof <pid>  
  3. # 或JVM参数触发OOM时自动生成  
  4. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof  
复制代码
MAT分析步调
  

  • 打开heapdump.hprof,查察“Dominator Tree”找到占用内存最大的对象。
  • 检查可疑对象的GC Root路径(如未关闭的数据库连接、静态聚集缓存)。
  3.2 壅闭操作检测(BlockHound)

  • 集成BlockHound
  1. public class BlockHoundConfig {  
  2.     @PostConstruct  
  3.     public void init() {  
  4.         BlockHound.builder()  
  5.             .allowBlockingCallsInside("com.example.Class", "methodName")  
  6.             .install();  
  7.     }  
  8. }  
复制代码
检测结果示例
  1. Blocking call! java.lang.Thread.sleep  
  2. at com.example.Service.blockingMethod(Service.java:20)  
复制代码

  4. 监控告警与链路追踪

  4.1 Micrometer集成Prometheus

  • 设置依赖
  1. <dependency>  
  2.     <groupId>io.micrometer</groupId>  
  3.     <artifactId>micrometer-registry-prometheus</artifactId>  
  4. </dependency>  
复制代码
暴露指标端点
  1. management:  
  2.   endpoints:  
  3.     web:  
  4.       exposure:  
  5.         include: health, prometheus  
  6.   metrics:  
  7.     tags:  
  8.       application: ${spring.application.name}  
复制代码
自定义指标
  1. @Bean  
  2. public MeterRegistryCustomizer<PrometheusMeterRegistry> metrics() {  
  3.     return registry -> registry.config().commonTags("region", "us-east");  
  4. }  
复制代码
4.2 WebFlux链路追踪(Sleuth + Zipkin)

  • 设置依赖
  1. <dependency>  
  2.     <groupId>org.springframework.cloud</groupId>  
  3.     <artifactId>spring-cloud-starter-sleuth</artifactId>  
  4. </dependency>  
  5. <dependency>  
  6.     <groupId>org.springframework.cloud</groupId>  
  7.     <artifactId>spring-cloud-sleuth-zipkin</artifactId>  
  8. </dependency>  
复制代码
Zipkin服务端设置
  1. spring:  
  2.   zipkin:  
  3.     base-url: http://zipkin-server:9411  
  4.   sleuth:  
  5.     sampler:  
  6.       probability: 1.0  # 采样率(生产环境设为0.1)  
复制代码
追踪结果
  1. {  
  2.   "traceId": "3e6f3b7e6d6d6d6d",  
  3.   "spanId": "6d6d6d6d6d6d6d6d",  
  4.   "parentSpanId": null,  
  5.   "name": "GET /reactive/data",  
  6.   "tags": {  
  7.     "http.method": "GET",  
  8.     "http.status_code": "200"  
  9.   }  
  10. }  
复制代码

  总结

  • MVC调优重点:线程池容量、静态资源缓存、克制内存泄漏。 • WebFlux调优重点:非壅闭线程模型、响应式超时、全链路监控。 • 生产保障:通过BlockHound确保响应式代码纯净性,团结Prometheus+Zipkin实现可观测性。
  保举工具链: • 监控:Prometheus(指标) + Grafana(看板) + ELK(日志)。 • 压测:JMeter(全链路压测) + Gatling(高并发模仿)。 • 调优:Arthas(动态诊断) + VisualVM(JVM分析)。
  1. # Arthas快速诊断命令示例  
  2. $ ./arthas-boot.jar  
  3. arthas> dashboard  # 实时监控线程/内存  
  4. arthas> thread -n 5  # 查看最忙线程  
复制代码

  六、常见问题与面试题精选

  
  高频面试题

  1. Spring MVC怎样处理HTTP哀求?DispatcherServlet的作用是什么?

  答案
  

  • 哀求处理流程: • DispatcherServlet作为前端控制器,接收全部HTTP哀求。 • 通过HandlerMapping解析哀求URL,找到对应的控制器方法(如@GetMapping)。 • HandlerAdapter调用控制器方法,处理参数绑定(如@RequestBody)。 • 结果通过ViewResolver解析视图(如返回JSON或HTML页面)。
  • DispatcherServlet的核心作用: • 同一入口:会合管理哀求分发,解耦哀求处理与业务逻辑。 • 组件协调:集成HandlerMapping、ViewResolver等组件,实现松耦合架构。 • 扩展支持:支持拦截器(Interceptor)、异常处理(@ControllerAdvice)。
  代码示例
  1. @RestController  
  2. public class UserController {  
  3.     @GetMapping("/users/{id}")  
  4.     public User getUser(@PathVariable Long id) {  
  5.         return userService.findById(id);  
  6.     }  
  7. }  
复制代码

  2. WebFlux的背压机制怎样解决数据斲丧速度不匹配问题?

  答案
  

  • 背压核心原理: • 斲丧者通过Subscription动态控制数据流速(如request(n)哀求n个元素)。 • 生产者根据斲丧者的处理本领调整数据发送速率。
  • WebFlux实现方式: • Flux缓冲计谋:使用onBackpressureBuffer缓存溢出数据。 • 流量控制:limitRate()限定生产者速率。
  代码示例
  1. Flux.range(1, 1000)  
  2.     .onBackpressureBuffer(10)  // 缓冲区容量为10  
  3.     .subscribe(  
  4.         value -> process(value),  
  5.         error -> log.error("Error", error),  
  6.         () -> log.info("Completed"),  
  7.         subscription -> subscription.request(5)  // 初始请求5个元素  
  8.     );  
复制代码

  3. 为什么WebFlux默认使用Netty而不是Tomcat?

  答案
  

  • 架构差异: • Tomcat:基于Servlet API,每个哀求占用一个线程(壅闭式)。 • Netty:基于事件循环(EventLoop),单线程处理多个连接(非壅闭)。
  • 性能优势: • 高并发:Netty的EventLoop模型支持数万并发连接。 • 低耽误:非壅闭I/O淘汰线程切换开销。
  适用场景: • Tomcat:传统同步逻辑(如JDBC事务)。 • Netty:实时推送、高并发接口(如API网关)。
  
  实战场景题

  1. 设计一个支持10万并发的实时股票报价接口(WebFlux + SSE)

  实现方案
  

  • 技术选型: • WebFlux:非壅闭处理高并发连接。 • SSE(Server-Sent Events):通过长连接推送实时数据。
  • 核心代码
    1. @GetMapping(value = "/stocks", produces = MediaType.TEXT_EVENT_STREAM_VALUE)  
    2. public Flux<StockPrice> streamStockPrices() {  
    3.     return Flux.interval(Duration.ofMillis(100))  
    4.                .map(seq -> new StockPrice("AAPL", 150 + seq * 0.1));  
    5. }  
    复制代码
  • 优化计谋: • 背压控制:onBackpressureDrop()丢弃无法处理的实时数据。 • 集群摆设:通过Kafka分发数据到多个实例。
  
  2. 优化一个Spring MVC接口从200ms耽误降低到50ms以下

  优化步调
  

  • 性能分析: • 使用jstack或Arthas定位线程壅闭点(如慢SQL、外部API调用)。
  • 优化手段: • 缓存:Redis缓存查询结果(淘汰数据库压力)。 • 异步化:将非关键逻辑(如日志纪录)改为异步处理。
    1. @Async  
    2. public void logAccess(Long userId) {  
    3.     // 异步记录访问日志  
    4. }  
    复制代码
    SQL优化:添加索引、克制全表扫描。
  • 结果验证: • 通过JMeter压测确认耽误降低至50ms以下。
  
  陷阱题

  1. 在WebFlux中调用壅闭代码(如JDBC)会导致什么问题?怎样解决?

  答案: • 问题:壅闭操作(如JDBC)会占用EventLoop线程,导致整个应用吞吐量骤降。 • 解决方案
  

  • 异步驱动:使用R2DBC(响应式关系数据库驱动)。
  • 线程池隔离:将壅闭操作调理到独立线程池。
  1. Mono.fromCallable(() -> blockingJdbcCall())  
  2.     .subscribeOn(Schedulers.boundedElastic())  // 切换到弹性线程池  
  3.     .subscribe(result -> ...);  
复制代码

  2. 为什么WebFlux的Mono返回类型不能直接序列化为XML?

  答案: • 原因:默认的HttpMessageConverter大概未注册XML的响应式序列化器。 • 解决方案
  

  • 添加Jackson XML依赖:
  1. <dependency>  
  2.     <groupId>com.fasterxml.jackson.dataformat</groupId>  
  3.     <artifactId>jackson-dataformat-xml</artifactId>  
  4. </dependency>  
复制代码

  • 设置内容协商:
  1. spring:  
  2.   mvc:  
  3.     contentnegotiation:  
  4.       media-types:  
  5.         xml: application/xml  
复制代码

  总结

  • Spring MVC:得当传统同步场景,注意开发效率。 • WebFlux:得当高并发、实时流处理,需严格克制壅闭操作。 • 核心考点:背压机制、线程模型、性能优化、生产问题排查。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! 更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

种地

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表