传统的Web框架,struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的,在Servlet3.1之后才有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上,因此它的运行环境的可选择行要比传统web框架多的多。
- 同步servlet阻塞了什么?
它阻塞了tomcat容器的servlet线程。当网络请求发送到tomcat容器之后,tomcat容器会给每个请求启动一个线程,线程里会调用响应的servlet来处理。此时你的业务耗时多久,这个线程就要等多久,很多个请求就会阻塞。
- 为什么要使用异步servlet?
如上,使用异步servlet可以提升更大的并发量,达到程序垂直扩展在水平扩展。垂直扩展就是指在我们代码能力范围内的瓶颈,水平扩展就比如加服务器,升配置等等。
webflux开发
创建一个springboot项目,勾选Reactive Web,不要勾选以前使用的Web Starter,我使用的软件idea,jdk是11,eclipse也是勾这两个。还是用了lombok,软件需要装一下这个插件。
创建完项目,pom自动加入依赖:
@RestController
public class OldController {
/**
* 常规写法
*
* @return
*/
@GetMapping("/old")
public Map<String, Object> old() {
return new HashMap<String, Object>() {{
put("username", "赵山河");
put("age", "30");
put("sex", "男");
}};
}
/**
* Mono 返回0-1个元素
* @return
*/
@GetMapping("/mono")
public Mono<Map<String, Object>> mono() {
return Mono.just(new HashMap<String, Object>() {{
put("username", "赵山河");
put("age", "30");
put("sex", "男");
}});
}
/**
* Flux - 返回0-N个元素
* @return
*/
@GetMapping(value = "/flux" , produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> flux() {
Flux<String> result = Flux.fromStream(IntStream.range(1,5).mapToObj(s -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "flux--" + s;
}));
return result;
}
以上一个常规写法和webflux写法,区别在于阻塞和非阻塞,假设controller里有耗时操作,那么常规写法是耗时多久,这个线程就占用多久,而webflux写法会采用异步的响应式的方式来处理,则不会一直占用该线程。而Flux是返回多次数据。
- SSE - Server sent Event
严格地说,HTTP协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。
也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。
SSE就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。Flux就是这个样子,getMapping里的produces属性指定了返回的是一个数据流,MediaType.TEXT_EVENT_STREAM_VALUE是它提供的枚举。
完整实例
操作MongoDB依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
添加MongoDB相关注解
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
@EnableReactiveMongoRepositories
实体类相关注解
import org.springframework.data.mongodb.core.mapping.Document;
//指定在mongodb中的集合名
@Document(collection = "user")
@Data
public class User {
@Id
private String id;
private String name;
private int age;
}
定义仓库
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
//User实体类,String是Id主键的类型
@Repository
public interface UserRepository extends ReactiveMongoRepository<User,String> {
}
controller
@RestController
@RequestMapping("/user")
public class UserController {
//使用构造函数注入
private final UserRepository repository;
public UserController(UserRepository repository){
this.repository = repository;
}
//全部返回
@GetMapping("/all")
public Flux<User> getAll(){
return repository.findAll();
}
//流方式返回
@GetMapping(value = "/stream/all",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamGetAll(){
return repository.findAll();
}
}
开启MongoDB服务,配置application文件
spring.data.mongodb.uri=mongodb://localhost:27017/webflux
使用Restlet Client插件:
进去搜Restlet下载,进不去需要安装谷歌访问助手
http://chromecj.com
启动项目,进行测试。