spring-webflux是spring在5.0版本后提供的一套响应式编程风格的web开发框架。
这个框架包含了spring-framework和spring mvc,它可以运行在Netty、Undertow以及3.1版本以上的Serlvet容器上。
你可以在项目中同时使用spring-webmvc和spring-webflux,或者只用其中一个来开发web应用。
如果你同时添加了spring-boot-starter-web和spring-boot-starter-webflux依赖,那么Spring Boot会自动配置Spring MVC,而不是WebFlux。你当然可以强制指定应用类型,通过SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
CREATE TABLE `ts_person` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`age` tinyint(4) NOT NULL COMMENT '年龄',
`name` varchar(20) NOT NULL COMMENT '名称',
`ctime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`mtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表-webflux测试';
@Data
public class Person {
/**
* id
*/
private Integer id;
/**
* 名称
*/
private String name;
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime ctime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime mtime;
}
dao接口,这里仅测试一个接口
@Mapper
public interface PersonDao {
Person findOne(@Param("id") Integer id);
int savePerson(@Param("person") Person person);
List<Person> findAll();
int deletePerson(@Param("id") Integer id);
int updatePerson(@Param("person") Person person);
}
mapper文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zby.dao.PersonDao">
<insert id="savePerson">
INSERT INTO mybatis.ts_person(`name`, age)
VALUES (#{person.name}, #{person.age})
insert>
<update id="updatePerson">
update mybatis.ts_person
set `name` = #{person.name},
age = #{person.age}
where id = #{person.id}
update>
<delete id="deletePerson">
delete
from mybatis.ts_person
where id = #{id}
delete>
<select id="findOne" resultType="com.zby.model.Person">
SELECT tp.*
FROM mybatis.ts_person tp
WHERE tp.id = #{id}
select>
<select id="findAll" resultType="com.zby.model.Person">
SELECT tp.*
FROM mybatis.ts_person tp
select>
mapper>
在webflux里面,Handler相当于mvc的service层
@Component
@RequiredArgsConstructor
public class PersonHandler {
private final PersonDao personDao;
public Mono<Person> getPerson(Integer id) {
return Mono.justOrEmpty(personDao.findOne(id));
}
public Mono<Integer> deletePerson(Integer id) {
return Mono.create(monoSink -> {
monoSink.success(personDao.deletePerson(id));
});
}
public Mono<Integer> savePerson(SavePersonRequest request) {
Person person = BeanUtil.toBean(request, Person.class);
return Mono.create(monoSink -> {
//Mono.create方法里面是异步的,差不多就是一个Future
//可以加以下代码验证异步的特点,虽然下面设置了30s睡眠,但是接口立马就返回了
/*try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
monoSink.success(personDao.savePerson(person));
});
}
public Flux<Person> findAll() {
return Flux.fromIterable(personDao.findAll());
}
public Mono<Integer> updatePerson(ModifyPersonRequest request) {
Person person = BeanUtil.toBean(request, Person.class);
return Mono.create(monoSink -> {
getPersonIfExt(request);
monoSink.success(personDao.updatePerson(person));
});
}
private void getPersonIfExt(ModifyPersonRequest request) {
Person one = personDao.findOne(request.getId());
if (Objects.isNull(one)) {
throw new BizException("对象不存在");
}
}
}
在Webflux,编写Controller有两种方式,一种是Router,一种就是沿用MVC的Controller的写法,个人建议使用Controller的写法,因为Router的写法很麻烦,不是使用注解。
下面是Controller的写法
@RestController
@RequestMapping(value = "/person")
@RequiredArgsConstructor
public class PersonController {
private final PersonHandler personHandler;
@GetMapping(value = "/{id}")
public Mono<Person> getPerson(@PathVariable("id") Integer id) {
Mono<Person> person = personHandler.getPerson(id);
return personHandler.getPerson(id);
}
@PostMapping()
public Mono<Integer> savePerson(@RequestBody SavePersonRequest request) {
return personHandler.savePerson(request);
}
@GetMapping()
public Flux<Person> findAll() {
return personHandler.findAll();
}
@DeleteMapping(value = "/{id}")
public Mono<Integer> deletePerson(@PathVariable("id") Integer id) {
return personHandler.deletePerson(id);
}
@PutMapping()
public Mono<Integer> updatePerson(@RequestBody ModifyPersonRequest request) {
return personHandler.updatePerson(request);
}
}
其他入参:
修改对象的入参
// 修改对象的入参
@Data
public class ModifyPersonRequest {
private Integer id;
/**
* 名称
*/
private String name;
private Integer age;
}
新增对象的入参
@Data
public class SavePersonRequest {
/**
* 名称
*/
private String name;
private Integer age;
}
application.yml
server:
#servlet:
# context-path: /spring-webflux-demo # 对webflux无用,不知道是不是spring版本的问题
port: 8084
spring:
application: # 应用名称(也是 Nacos 中的服务名)
name: spring-webflux-demo
profiles:
active: dev
webflux:
base-path: /spring-webflux-demo #没用,调用接口依然是没有项目路由
format:
date: yyyy-MM-dd #没用,返回的时间是个数组,我只能用@JsonFormat去解决
date-time: yyyy-MM-dd HH:mm:ss #没用,返回的时间是个数组,我只能用@JsonFormat去解决
datasource:
url: jdbc:mysql://localhost:3306?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
往ts_person插入一条记录
INSERT INTO mybatis.ts_person
(id, age, name, ctime, mtime)
VALUES(1, 15, '破损', '2023-03-06 08:22:12', '2023-03-06 08:22:12');
apache-jmeter-5.3工具
以单个person查询的测试接口作为目标
新建测试计划testWebfluxPerson.jmx,这里jmeter希望我们用命令去执行:
jmeter -n -t testplan/webflux/testWebfluxPerson.jmx -l testplan/webflux/result/result.txt -e -o testplan/webflux/webreport
testplan/webflux/testWebfluxPerson.jmx 测试计划路径
testplan/webflux/result/result.txt 为测试结果文件路径
testplan/webflux/webreport 为web报告保存路径
在jmeter的bin目录下打开cmd,运行上面的命令,生成结果:
我用spring mvc搭建类似上面的webflux对应的CRUD接口,以单个person查询的测试接口作为目标
新建测试计划testConsumerPerson.jmx,这里jmeter希望我们用命令去执行:
jmeter -n -t testplan/consumer/testConsumerPerson.jmx -l testplan/consumer/result/result.txt -e -o testplan/consumer/webreport
testplan/consumer/testConsumerPerson.jmx 测试计划路径
testplan/consumer/result/result.txt 为测试结果文件路径
testplan/consumer/webreport 为web报告保存路径
在jmeter的bin目录下打开cmd,运行上面的命令,生成结果:
Throughput:就是吞吐量的意思,指定时间内能够处理的请求数
从上面的压测来看,webmvc的吞吐量是167.88/s ,webflux的吞吐量是388.15/s ,可以看出,webflux的吞吐量明显比webmvc优秀。
但是WebFlux 并不能使接口的响应时间缩短,它只是提高了程序的吞吐量而已,使用接口工具访问Webflux的接口跟访问WebMVC接口所消耗的时间是相差无几的。
https://mp.weixin.qq.com/s/I4bqSpruoFxwk6pINNtVDQ
https://blog.csdn.net/zhangyingchengqi/article/details/110285664
https://www.zhihu.com/question/19732473
https://blog.csdn.net/qq_38515961/article/details/124989725
https://juejin.cn/post/7026166895866806308
https://blog.csdn.net/crazymakercircle/article/details/112977951