一、Spring Webflux的介绍
1.WebFlux介绍
WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架。通过Reactor项目实现Reactive Streams规范,完全异步和非阻塞框架。本身不会加快程序执行速度,但在高并发情况下借助异步IO能够以少量而稳定的线程处理更高的吞吐,规避文件IO/网络IO阻塞带来的线程堆积。
1.1特性
- 异步非阻塞
- 响应式函数编程
- 不拘束于Servlet
1.2适用场景
- 高并发
- 高吞吐量
- 可伸缩性
2.Spring MVC和Spring WebFlux对比
在Spring框架中,Spring MVC和Spring WebFlux是两个常用的Web框架,代表了两个IO模型,阻塞式和非阻塞式。SpringWebflux是SpringFramework5.0添加的新功能。
WebMvc是基于Web Servlet的阻塞式模型,即OIO或BIO,一个请求到达服务器后会单独分配一个线程去处理请求,如果请求包含IO操作,线程在IO操作结束之前一直处于阻塞等待状态。适合处理传统的同步请求响应场景,对于大量 I/O 密集型操作,性能可能不如 WebFlux。
WebFlux是基于web reactor的非阻塞模型(一般称为nio),webflx是反应式编程,反应式编程是关于非阻塞应用程序,它们是异步和事件驱动的,一个请求到达服务器后也会分配一个线程去处理请求,如果请求包含IO操作,线程在IO操作结束之前不再是处于阻塞等待状态,而是去处理其他事情,等到IO操作结束之后,再通知(得益于系统的机制)线程继续处理请求。WebFlux 可以处理高并发、高负载的场景,适用于需要处理大量非阻塞 I/O 操作的应用。
相对于SpringMVC来说,Spring WebFlux并不会提高程序的运行速度,而是在有限的资源下提高系统的伸缩性,相对于提高程序的高并发。
2.Reactor介绍
2.1工作原理
Reactor 是一个支持响应式流(Reactive Streams)的轻量级JVM基础库,帮助应用高效,异步地传递消息,WebFlux默认的响应式库(Reactive Libraries)就是Reactor库。Reactor的工作原理如下:
2.2 Mono和Flux
Reactor提供了Mono和 Flux2种API类型,并通过丰富运算符集来处理0…1(Mono)和0…N(Flux)数据序列
Mono:0…1个数据
Flux :0…N个数据
2.3编程模式
WebFlux和MVC非常的相似,很多注解都是可以共用的,在一定程度上减少了从mvc迁移到webflux的成本。如下图是官方文档的对比
WebFlux可以使用两种编程模式,一种就是大家都非常熟悉的注解的方式,即使用@Controller
、@RestController
,另一种是函数式编程。
(1)使用注解方式
注解方式一个其实使用起来和使用mvc没什么区别,下面依然和使用mvc一样,定义一个Controller
,并定义几个增删改查的接口。唯一的区别在于返回的对象是Mono
和Flux
,返回单个数据就是Mono
,多个就使用Flux。
@Slf4j @RequestMapping("/test") @RestController public class TestController { @Autowired private IRegionService regionService; @RequestMapping("/mono") public Mono<String> index() { JSONObject object = new JSONObject(); object.put("key", "123456"); log.info("开始获取数据!!!"); Mono<String> result = Mono.fromSupplier(this::execute); log.info("获取数据结束!!!"); return result; } @RequestMapping("/all") public Flux<DpRegion> getAllRegion() { return regionService.getAllRegionList(); } private String execute() { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } return "hello"; } }
(2)使用函数式
定义RouterFunction:
@Component
public class UserFunctionRouter {@Autowired
private UserHandler userHandler;@Bean("userRouter")
public RouterFunction router() {
RouterFunction<ServerResponse> routerFunction = route()
.GET("/user/find/all", accept(MediaType.APPLICATION_JSON_UTF8), userHandler::findAll)
.GET("/user/query/{uuid}", accept(MediaType.APPLICATION_JSON), userHandler::queryByName)
.build();
return routerFunction;
}
}
定义HandlerFunction
@Component
public class UserHandler {
@Autowired
private UserRepository userRepository;public Mono findAll(ServerRequest serverRequest) {
Flux<User> flux = userRepository.findAll();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(flux, User.class);
}
// 查询单个
public Mono queryByUUID(ServerRequest serverRequest) {
String uuid = serverRequest.pathVariable("uuid");
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).body(userRepository.queryByUUID(uuid),User.class);
}
}
二、从零开始搭建Web工程
1.基础工程搭建
1.1创建maven工程,引入webflux包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.1简单的测试Controller
@Slf4j @RequestMapping("/mock") @RestController public class TestController { @RequestMapping("/mono") public Mono<JSONObject> index() { JSONObject object = new JSONObject(); object.put("key", "123456"); Mono<JSONObject> result = Mono.just(object); return result; } }
2.MySql的接入
2.1R2DBC引入
目前支持反应式关系型数据库连接的只有R2DBC,引入maven工程
<!-- spring data r2dbc --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-r2dbc</artifactId> </dependency> <!-- r2dbc 连接池 --> <dependency> <groupId>io.r2dbc</groupId> <artifactId>r2dbc-pool</artifactId> </dependency> <!--r2dbc mysql 库 --> <dependency> <groupId>io.asyncer</groupId> <artifactId>r2dbc-mysql</artifactId> <version>1.0.5</version> </dependency>
2.2数据库表设计
CREATE TABLE `mock_date` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '数据库自增列',
`URL` varchar(128) NOT NULL COMMENT '请求地址',
`RES_DATA` varchar(1024) NOT NULL COMMENT '返回值 ',
`QUREY_PARAM` varchar(255) DEFAULT NULL,
`QUREY_BODY` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4;
2.3DAO代码
@Repository @Component public interface IMockDataDao extends R2dbcRepository<MockData, Long> { }
2.4Service层
public interface IMockServerService { public Mono<ResponseObject> addMockInfo(RequestObject<ReqMockInfoDto> requestObject); }
@Override public Mono<ResponseObject> addMockInfo(RequestObject<ReqMockInfoDto> requestObject) { ReqMockInfoDto reqMockInfoDto = requestObject.getData(); if (null == reqMockInfoDto) { return Mono.just(ResponseObject.paramError()); } MockData mockData = new MockData(); BeanUtils.copyProperties(reqMockInfoDto, mockData); return mockDataDao.save(mockData) .log() .flatMap(a -> { log.info("执行成功!!!"); return Mono.just(ResponseObject.successful()); }) .doOnSuccess(s -> { log.info("执行成功success!!!"); }) .onErrorResume( b -> { log.error("执行失败!!!"); return true; }, c -> Mono.just(ResponseObject.error()) ); }
2.5Controller层
@RequestMapping("/add") public Mono<ResponseObject> addMockInfo(@RequestBody @Valid RequestObject<ReqMockInfoDto> requestObject) { log.info("添加mock信息入参,param:{}", JSONObject.toJSONString(requestObject)); return mockServerService.addMockInfo(requestObject); }
3.Redis接入
3.1将反应式Redis包引入maven
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>
3.2模板工具ReactiveRedisTemplat
使用Redis模板工具ReactiveRedisTemplate可以实现缓存数据的处理
@Slf4j @Service public class RedisServiceImpl implements IRedisService { @Autowired private ReactiveRedisTemplate reactiveRedisTemplate; @Override public Mono<Void> setString() { reactiveRedisTemplate .opsForValue() .setIfAbsent("human", "yinyu", Duration.of(50, ChronoUnit.SECONDS)); return null; } @Override public Mono<String> getString() { Mono<String> result = reactiveRedisTemplate .opsForValue() .get("human"); result.subscribe(a -> log.info("get result:{}", a), e -> log.error("get data error:{}", e)); return result; } }
4.Mq等消息中间件的接入
还在研究中