0116 spring的webFlux

背景

場景 要求 編程方法
電商和金融行業 數據一致性要求很是高 高併發的時候須要鎖或者其它機制來保證一些重要數據的一致性;
可是性能也降低的很快;
遊戲,新聞,視頻,廣告 不須要很高的數據一致性 對併發數和響應速度要求比較高
這種場景下,出現了響應式編程。依賴的基礎技術點以下:
技術點 說明
servlet3.1 支持響應式編程
java8 語法豐富支持響應式編程,非堵塞式編程
spring5 新一代的web框架webflux,依託於servlet3.1+和java8
srpingboot2.x 使用了spring5
Rxjava 一種流行的響應式編程框架
Reactor spring5中響應式編程的默認實現方式

基本概念

響應式編程關鍵詞:
數據流:流式處理
異步: 異步處理
消息:基於消息名

Reactor模型

  1. 客戶端先向服務器端註冊感興趣的event,完成了事件訂閱;
  2. 客戶端發生已經註冊的事件,會觸發服務器的響應,服務器存在一個selector線程,【輪詢客戶端發送過來的事件】可是並不實際處理事件,而是找到對應的Request Handler,啓用另一條線程運行處理。
  3. 最終結果會轉換成data stream,發送到客戶端;

 

 WebFlux

基於servlet3.1對非阻塞機制,和java8的函數式語法,webflux出現了。
響應式編程分爲3層:
說明
router functions 路由分發層,reactor模式中的selector;
根據請求的事件,選擇對應的方法去處理客戶端發送過來的事件請求。
spring webflux 控制層,處理業務邏輯前進行的封裝和控制數據流的返回格式
http/reactive streams 轉換層:把結果轉換爲數據流的過程

容器要求:支持servlet3.1  
java異步編程領域:Netty java

開發方式react

開發方式 說明
類springMVC模式 簡單,跟普通的springMVC方式有不少共同點,容易被接受
函數功能性 使用的比較少,由於不太好理解,開發後端的技術若是出現兩種並存,效率不容易提升

數據流的封裝git

數據流封裝 說明
Flux 存放0-N個數據流序列,響應式框架會一個一個的發送到客戶端
Mono 存放0-1個數據流序列,一次僅發送一個數據流序列到客戶端

DispatcherHandler

跟springMVC對標
對比 說明
DispatcherServlet springMVC核心控制器
DispatcherHandler webFlux核心處理器

核心處理代碼github

public Mono<Void> handle(ServerWebExchange exchange) {
        if (this.handlerMappings == null) {
            return createNotFoundError();
        }
        return Flux.fromIterable(this.handlerMappings)
                .concatMap(mapping -> mapping.getHandler(exchange))
                .next()
                .switchIfEmpty(createNotFoundError())
                .flatMap(handler -> invokeHandler(exchange, handler))
                .flatMap(result -> handleResult(exchange, result));
    }

核心處理流程web

步驟 說明
1 DispatherHandler  接受請求
2 找到對應的HandlerMapping 從控制器中獲得,經過@RequestMapping獲得
fromIterable(this.handlerMappings)
 
3 獲得對應的HandlerAdapter   .concatMap(mapping -> mapping.getHandler(exchange))
4 處理完畢以後獲得Result     .flatMap(handler -> invokeHandler(exchange, handler))
   
5 返回到DispatherHandler
6 轉換爲handleResult 轉換爲對應的數據流
.flatMap(result -> handleResult(exchange, result));

注意

spring webflux只支持spring data reactive的數據源;
而數據庫的開發每每是堵塞的,因此,spring data reactive並不能對數據庫的開發提供支持。
適用於 redis,mongodb等nosql數據庫

WebFlux的服務端開發

啓動器代碼redis

package com.springbootpractice.demo.webflux;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableReactiveMongoRepositories(basePackages = "com.springbootpractice.demo.webflux.dao.repository")
public class DemoWebfluxApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoWebfluxApplication.class, args);
    }

}

持久層代碼spring

package com.springbootpractice.demo.webflux.dao.repository;

import com.springbootpractice.demo.webflux.dao.entity.User;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2020年01月15日 6:18 下午
 **/
@Repository
public interface UserRepository extends ReactiveMongoRepository<User,Long> {

   Flux<User> findByUserNameLikeAndNoteLike(String userName,String note);

}

控制器代碼
**sql

package com.springbootpractice.demo.webflux.controller;

import com.springbootpractice.demo.webflux.core.UserValidator;
import com.springbootpractice.demo.webflux.dao.entity.User;
import com.springbootpractice.demo.webflux.service.UserService;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.validation.Valid;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2020年01月15日 6:43 下午
 **/
@RestController
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping(path = "/user/{id}")
    public Mono<User> getUser(@PathVariable("id") Long id){
        return userService.getUserById(id);
    }

    @GetMapping(path = "/user/{userName}/{note}")
    public Flux<User> getUser(@PathVariable("userName") String userName, @PathVariable("note") String note){
        return userService.findByUserNameAndNote(userName,note);
    }

    @GetMapping(path = "/user/insert/{user}")
    public Mono<User> insertUser(@Valid  @PathVariable("user")  User user){
        return userService.insertUser(user);
    }

//    @InitBinder
//    public void initBinder(DataBinder binder){
//        binder.setValidator(new UserValidator());
//    }
}

WebFlux的核心功能

類比springMVC,提供了 WebFluxConfigurer進行配置,根據須要實現對應的方法;

轉換器

對標 springMVC,也須要實現Converter接口:

代碼mongodb

package com.springbootpractice.demo.webflux.core;

import com.springbootpractice.demo.webflux.dao.entity.User;
import com.springbootpractice.demo.webflux.dao.entity.enums.SexEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;

import java.util.Objects;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2020年01月16日 9:28 上午
 **/

public class String2UserConverter implements Converter<String, User> {
    @Override
    public User convert(String s) {
        final String[] split = Objects.requireNonNull(s,"轉換爲User的string不能爲空").split("-");

        Assert.isTrue(split.length==4,"轉換爲User的string必須含有4個字段");
        return User.builder()
                .id(Long.parseLong(split[0]))
                .userName(split[1])
                .note(split[2])
                .sex(SexEnum.getSexEnum(Integer.parseInt(split[3])))
                .build();
    }
}

校驗器

對標springMVC的校驗器, 實現Validator接口;

代碼數據庫

package com.springbootpractice.demo.webflux.core;


import com.springbootpractice.demo.webflux.dao.entity.User;
import org.apache.logging.log4j.util.Strings;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2020年01月16日 9:48 上午
 **/

public class UserValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return clazz.equals(User.class);
    }

    @Override
    public void validate(Object target, Errors errors) {

        User user = (User) target;
        if (Strings.isBlank(user.getUserName())){
            errors.rejectValue("userName","","用戶名不能爲空");
        }

    }
}

局部校驗器

對標springMVC, 能夠在控制器中增長  @InitBinder ,裏面配置好本控制器的校驗器

代碼

@InitBinder
    public void initBinder(DataBinder binder){
        binder.setValidator(new UserValidator());
    }

設置靜態資源

一些文件,圖片,配置內容的配置,能夠在WebConfigurer中進行配置;

代碼

package com.springbootpractice.demo.webflux.core;

import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.CacheControl;
import org.springframework.validation.Validator;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;

import java.util.concurrent.TimeUnit;

/**
 * 說明:TODO
 * @author carter
 * 建立時間: 2020年01月16日 9:33 上午
 **/
@Configuration
public class WebFluxConfig implements WebFluxConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new String2UserConverter());

    }

    @Override
    public Validator getValidator() {
        return new UserValidator();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));

    }
}

訪問靜態資源例子:
image.png

小結

經過本篇文章,你學會了:

  1. springWebFlux的基礎概念和reactor模型;
  2. 一個利用webFlux操做mongodb的增刪改查的例子;
  3. 類比springMVC,開發webflux的轉換器,校驗器,靜態資源;

代碼點我獲取!

原創不易,轉載請註明出處,歡迎溝通交流。
相關文章
相關標籤/搜索