Spring WebFlux编程介绍和应用

一、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,并定义几个增删改查的接口。唯一的区别在于返回的对象是MonoFlux,返回单个数据就是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等消息中间件的接入

还在研究中

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值