欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
欢迎评论交流,感谢您的阅读😄。
目录
- 引言
- HTTP/HTTPS协议
- Spring Web与Spring Web MVC
- Spring WebFlux
- 自定义的TPC/IP协议
- FTP、SMTP协议
引言
总所周知,服务间通信模式可分为同步、异步和流式通信。
同步通信以HTTP和RPC为主,这两者都是应用层的,属于TCP/IP协议族。
TCP/IP 是一组网络通信协议(Protocol Suite)。 它定义了数据如何在网络中被打包、寻址、传输、路由和接收的规则。
TCP/IP核心分层如下:
层次 | 协议示例 |
---|---|
应用层 | HTTP, HTTPS, FTP, SMTP, DNS, SSH, Telnet, gRPC, MQTT, Kafka |
传输层 | TCP, UDP, SCTP |
网际层(网络层) | IP (IPv4/IPv6), ICMP, IGMP, ARP, RARP, RIP, OSPF, BGP |
链路层 | 以太网 (Ethernet), Wi-Fi (802.11), PPP (Point-to-Point Protocol) |
而我的Spring框架专注于应用层的开发,下面,让我们一起看看Spring是如何处理应用层不同类型的协议请求的。
HTTP/HTTPS协议
Spring Web与Spring Web MVC
Spring框架提供Spring Web与SpringMVC模块处理应用层的HTTP/HTTPS协议。
Spring Web是Spring框架中处理Web功能的基础模块,提供HTTP请求处理、响应管理、远程服务集成等底层功能。
SpringWeb处理请求是基于Servlet API的。
Java Servlet规范定义了HttpServlet
类,里面包含一系列的doXXX()方法,对应Http的各类型请求方法:Get\Post\Put\Delete\Head\Patch等。
另外还有会话管理、RESTful支持等。
Spring MVC模块全程Spring Web MVC,是基于Spring Web的处理HTTP/HTTPS协议的上层模块。
Spring MVC提供有常用的@RestController注解,这个注解就依赖于Spring Web的@RequestMapping和@ResponseBody。
Spring MVC执行流程如下:
其中核心组件为前端控制器DispatcherServlet
,将 URL 映射到具体的控制器方法的HandlerMapping
,将逻辑视图名称解析为实际视图(如 JSP 页面)的ViewResolver
,适配不同类型的控制器(如注解式控制器)的HandlerAdapter
。
PS:在Spring Boot项目中,添加 spring-boot-starter-web
会同时引入 Spring Web 和 Spring MVC 的能力。
Spring WebFlux
Spring框架提供Spring WebFlux模块来支持响应式编程。Spring WebFlux是Spring 5.0引入的,是Spring框架中与Spring MVC并行的一个方案,专门用于构建完全非阻塞、事件驱动的应用程序,能够更好地利用服务器资源,尤其是在高并发、I/O密集型场景下。
要理解 WebFlux,首先要理解响应式编程。它的核心思想是:
- 数据流 (Data Streams): 数据被看作是异步发出的事件流。
- 非阻塞 (Non-blocking): 操作不会阻塞当前线程。当一个操作(如数据库查询、网络调用)需要等待时,线程会被释放去处理其他任务,而不是空等。当操作完成时,会通过回调或事件通知来继续处理。
- 背压 (Backpressure): 消费者可以控制生产者发送数据的速率,防止消费者被过快的数据流淹没。
WebFlux 的关键组件和特性:
-
Project Reactor: WebFlux 底层依赖于 Project Reactor,这是一个实现了 Reactive Streams 规范的响应式库。Reactor 提供了两种核心的发布者(Publisher)类型:
Mono<T>
: 代表 0 或 1 个元素的异步序列(例如,获取单个用户信息)。Flux<T>
: 代表 0 到 N 个元素的异步序列(例如,获取用户列表,或者一个无限的事件流)。
-
两种编程模型:
- 注解驱动 (Annotation-based)
非常类似于 Spring MVC。可以使用 @Controller、@RestController 以及 @GetMapping、@PostMapping 等注解。不同的是,控制器方法的参数和返回值通常是 Mono 或 Flux。
@RestController
public class UserController {
@GetMapping("/users/{id}")
public Mono<User> getUserById(@PathVariable String id) {
// 异步获取用户
return userService.findById(id);
}
@GetMapping("/users")
public Flux<User> getAllUsers() {
// 异步获取所有用户流
return userService.findAll();
}
}
- 函数式路由 (Functional Endpoints / RouterFunctions)
这是一种更轻量级、更函数式的编程模型,请求被路由到处理器函数(HandlerFunction)。
@Configuration
public class UserRoutes {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
return RouterFunctions
.route(GET("/functional/users/{id}"), handler::getUserById)
.andRoute(GET("/functional/users"), handler::getAllUsers);
}
}
// UserHandler.java
@Component
public class UserHandler {
public Mono<ServerResponse> getUserById(ServerRequest request) {
String id = request.pathVariable("id");
Mono<User> userMono = userService.findById(id);
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(userMono, User.class);
}
// ... getAllUsers ...
}
-
非阻塞服务器: WebFlux 默认运行在像 Netty 这样的非阻塞 I/O 服务器上。它也可以运行在支持 Servlet 3.1+ 非阻塞 I/O 的传统 Servlet 容器(如 Tomcat, Jetty, Undertow)上,但 Netty 是更自然的选择,因为它本身就是完全异步事件驱动的。
-
客户端: Spring WebFlux 还包含一个响应式的 HTTP 客户端 WebClient,用于非阻塞地调用外部服务。
什么时候考虑使用 WebFlux?
- 需要处理高并发连接的应用,如微服务网关、聊天应用、实时数据仪表盘。
- I/O 密集型应用,其中大部分时间花在等待网络或磁盘操作。
- 需要流式处理数据的场景(例如,视频流、Server-Sent Events)。
- 希望构建一个完全响应式的系统。
需要注意的点:
- 学习曲线: 响应式编程范式与传统的命令式编程有较大差异,需要一定的学习成本。调试响应式代码也可能更复杂。
- “传染性”: 为了充分发挥响应式编程的优势,最好整个调用链都是响应式的。如果在一个响应式流中调用了一个阻塞的库,那么整个流的非阻塞优势就会被破坏。
- 并非银弹: 对于CPU密集型任务或者并发量不高的应用,WebFlux 可能不会带来显著的性能提升,甚至可能因为额外的复杂性而得不偿失。
自定义的TPC/IP协议
一些RPC框架会有自定义的TCP/IP协议,这时请求就不是通过Servlet处理的。
例如传统的gRPC、Apache Dubbo默认使用自定义的Dubbo协议、Thrift等。
它们通常直接在TCP套接字上监听特定端口。它们有自己的网络通信层,负责接收原始的TCP数据包,根据RPC框架定义的协议格式进行解码、反序列化、找到对应的服务和方法、执行调用、序列化结果、编码并通过TCP连接返回给客户端。
这种情况下,通常不需要Servlet容器(如Tomcat),RPC服务可以作为独立的Java应用运行(可能内嵌一个Netty等NIO服务器)。
这种RPC自定义协议的情况下,RPC框架自己实现了完整的网络通信、协议解析、请求分发、序列化等功能,不依赖于Servlet容器和HTTP协议。
也不需要Spring做处理。
FTP、SMTP协议
Spring框架提供Spring Integration配合Apache Commons VFS可以处理FTP与SMTP,从而传输文件与发送邮件。
当然,使用Spring MVC的MultipartFile也可以处理HTTP请求中的文件上传。但是MultipartFile不支持复杂的文件传输流程(如定时任务、错误重试)。