SpringWebflux 入门

Spring WebFlux

1.SpringWebFLux介绍

是Spring Framework提供的一种用户构建响应式Web应用程序的模块。它基于Reactive Streams标准,并使用了Reactor库来实现非阻塞、异步的编程模型

与传统的Spring MVC相比,Spring WebFlux采用了一种基于事件驱动的架构,可以处理更高的并发请求和负载。它不再依赖于Servlet容器,而是使用自己的服务器,例如Netty或Undertow,以实现异步、高效的请求处理。

Spring WebFlux提供了以下主要特性:

  1. 响应式编程模型:通过使用Reactive Streams和Reactor库,可以实现异步、非阻塞的编程方式,从而提高系统的吞吐量和性能。
  2. 注解驱动的编程模式:类似于Spring MVC,Spring WebFlux也支持基于注解的编程,通过注解控制器和路由函数,可以方便地定义请求映射、参数绑定和响应结果。
  3. 函数式端点定义:除了使用注解定义控制器,Spring WebFlux还支持使用函数式编程的方式定义端点。可以通过Router Functions来构建路由和处理请求,使代码更为简洁和灵活。
  4. 响应式数据流处理:Spring WebFlux中引入了Flux和Mono两个反应式类型,用于处理数据流。Flux表示一个包含0到N个元素的响应式序列,而Mono表示一个包含0或1个元素的响应式序列。这些类型可以用于处理异步数据流和流式操作。
1.1 什么是异步非阻塞
  • 异步和同步:

    异步和同步针对调用者,调用者发送请求,如果等待对方回应之后才继续执行去做其他事情就是同步,发送请求成功之后不等待对方回应直接去做其他事情就是非阻塞

  • 阻塞和非阻塞

    阻塞和非阻塞针对被调用者,调用者收到请求后,做完任务请求才给出反馈就是阻塞,收到请求之后马上给出反馈就是非阻塞

2.响应式编程

2.1 什么是响应式编程

响应式变成是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面或类似“B1+C1”的公司,而包含公式的值会依据其他单元格的值变化而变化;

响应式编程(Reactive Programming)是一种编程范式,旨在处理异步数据流和事件,并支持高效、可靠、可扩展的程序开发。它强调数据流的变化和传播,以及对这些变化作出相应的能力。

在响应式编程中,程序被看作是一组数据流和数据流之间的关系。数据流可以是来自用户输入、传感器、网络请求等的事件流或数据序列。响应式编程通过使用观察者模式(Observer Pattern)和函数式编程的概念,使得开发者可以定义对这些数据流进行监听和处理的逻辑。

响应式编程具有以下特点:

  1. 异步和非阻塞:响应式编程鼓励使用非阻塞的方式处理数据流,以避免线程阻塞和资源浪费。它通过使用回调函数、Promise、Future等机制来处理异步操作。
  2. 数据流的变换和组合:响应式编程提供了丰富的操作符和转换函数,可以对数据流进行过滤、映射、聚合等各种操作。这些操作可以组合在一起,形成复杂的数据流处理逻辑。
  3. 响应式和自动更新:响应式编程使得程序能够自动地对数据流的变化作出相应。当输入数据流发生变化时,相关的处理逻辑会自动更新和重新执行。
  4. 错误处理和容错性:响应式编程提供了错误处理机制,可以方便地捕获和处理异常情况。它也支持容错性,通过使用重试、超时等策略来处理潜在的故障。

响应式编程有许多应用场景,特别是在需要处理大量实时数据、事件驱动的系统和用户界面开发中。它能够简化异步编程和事件处理的复杂性,并提供了一种可靠和高效的方式来处理数据流和事件流。

public class ObserverDemo extends Observable {


	public static void main(String[] args) {
		ObserverDemo observer =new ObserverDemo();
		observer.addObserver((o,arg)->{
			System.out.println("发送变化");
		});
		observer.addObserver((o,arg)->{
			System.out.println("手动通知观察者通知,准备改变");
		});
		observer.setChanged();//数据变化
		observer.notifyObservers();//通知
	}
}
2.2 Reactor实现
  1. Reactor 是一个基于 Java 8+ 的响应式编程框架,它提供了一种基于事件驱动、非阻塞的编程模型,旨在简化异步编程和处理流式数据。Reactor 的核心是 Reactor 类库,它实现了 Reactive Streams 规范,并提供了一系列用于构建响应式应用的工具和组件。

  2. Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素;

  3. Flux 和 Mono:Flux 表示包含零个或多个元素的异步序列,而 Mono 表示包含零个或一个元素的异步序列。Flux 和 Mono 是 Reactor 中最基本的数据流表示,您可以对它们进行各种操作,比如映射、过滤、合并等。

  4. Flux和Mono都可以发送三种信号:

    元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,错误信号终止数据流同时把错误信息传递给订阅者

代码演示Flux和Mono:

/**
 * @Description: 创建Flux Mono方法
 * @author: scott
 * @date: 2023年10月30日 18:54
 */
public class TestReactor {

	public static void main(String[] args) {
		//just 直接声明
		Flux.just(1, 2, 3, 4).subscribe(System.out::println);
		Mono.just(1);
		//其他方法
		Integer[] array = {1, 2, 3, 4};
		Flux.fromArray(array);

		List<Integer> list = Arrays.asList(1, 2, 3, 5);
		Flux.fromIterable(list);
	}
}

  • 错误信号和完成信号都是终止信号,不能共存
  • 如果没有发送任何元素中,而是直接发送错误或者完成信号,则表示空数据流
  • 如果没有错误和完成信号,则表示无限数据流
  • 调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅subscribe什么都不会发生的
  1. 常用操作符

    map: 元素映射为新元素

    Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5);
    Flux<String> mappedNumbers = numbers.map(number -> "Number: " + number);
    
    

    **Filter 操作符:**根据指定的条件筛选元素

    Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5);
    Flux<Integer> evenNumbers = numbers.filter(number -> number % 2 == 0);
    
    

    **FlatMap 操作符:**将流的每个元素转换为新流,然后将这些流平铺合并成单一流。

    Flux<String> letters = Flux.just("A", "B", "C");
    Flux<String> combinedLetters = letters.flatMap(letter -> Flux.just(letter.toLowerCase(), letter.toUpperCase()));
    
    

    **Merge 操作符:**将多个流合并为单一流。

    Flux<Integer> numbers1 = Flux.just(1, 2, 3);
    Flux<Integer> numbers2 = Flux.just(4, 5, 6);
    Flux<Integer> mergedNumbers = Flux.merge(numbers1, numbers2);
    

    其他操作符等

    reduce:对流中的元素进行累积计算,返回一个单个的结果。
    take:从流中取出指定数量的元素。
    zip:将多个流的对应位置的元素进行配对。
    onErrorResume:在遇到错误时,返回一个备用的流。
    retry:在遇到错误时,重新尝试执行操作。

    timeout:设置一个超时时间,在指定时间内未完成操作,则抛出超时异常。

3. SpringWebFulx执行流程和核心API

SpringWebflux 处理请求流程图:ece3a110c4aecdbc81afaceb4e346d4d

SpringMvc处理请求流程图:

​ Spring Webflux 执行过程和Spring mvc相似,SpringWebflux核心控制器DispatchHandler,实现接口WebHanlder

​ 接口WebHanlder 有一个方法

public interface WebHandler {

	/**
	 * Handle the web server exchange.
	 * @param exchange the current server exchange
	 * @return {@code Mono<Void>} to indicate when request handling is complete
	 */
	Mono<Void> handle(ServerWebExchange exchange);

}
@Override
	public Mono<Void> handle(ServerWebExchange exchange) {// 放http请求响应信息
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		return Flux.fromIterable(this.handlerMappings)
				.concatMap(mapping -> mapping.getHandler(exchange)) //根据请求地址获取对应mapping
				.next()
				.switchIfEmpty(createNotFoundError())
				.flatMap(handler -> invokeHandler(exchange, handler))// 调用具体的业务方法
				.flatMap(result -> handleResult(exchange, result)); //处理结果返回
	}

4.SpringWebflux (基于注解编程模型实现)

4.1导入包
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-webflux</artifactId>
     <version>2.2.10.RELEASE</version>
</dependency>
4.2实体类
package com.xq.webflux_test.entity;

/**
 * @Description: TODO
 * @author: scott
 * @date: 2023年11月14日 16:54
 */
public class User {

	private String name;

	private String gendre;

	private Integer age;


	public User(String name, String gendre, Integer age) {
		this.name = name;
		this.gendre = gendre;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getGendre() {
		return gendre;
	}

	public void setGendre(String gendre) {
		this.gendre = gendre;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}
}

4.3 Service类
package com.xq.webflux_test.service;

import com.xq.webflux_test.entity.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

public interface UserService {

	//根据id查询用户
	Mono<User> getUserById(int id);

	//查询所有的用户
	Flux<User> getAllUser();

	//添加用户
	Mono<Void> addUser(Mono<User> user);
}

package com.xq.webflux_test.service.impl;

import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description: user接口实现
 * @author: xy
 * @date: 2023年11月14日 17:01
 */
@Service
public class UserServiceImpl implements UserService {

	private Map<Integer, User> users = new HashMap<>();

	public UserServiceImpl() {
		users.put(1, new User("xy", "男", 18));
		users.put(2, new User("Jones", "男", 28));
		users.put(3, new User("jack", "女", 8));
	}

	@Override
	public Mono<User> getUserById(int id) {
		return Mono.justOrEmpty(this.users.get(id));
	}

	@Override
	public Flux<User> getAllUser() {
		return Flux.fromIterable(this.users.values());
	}

	@Override
	public Mono<Void> addUser(Mono<User> userMono) {

		return userMono.doOnNext(user -> {
			//向map集合中放值
			int id = this.users.size() + 1;
			this.users.put(id, user);
		}).thenEmpty(Mono.empty());
	}
}

4.4 Controller
package com.xq.webflux_test.controller;

import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @Description:
 * @author: xy
 * @date: 2023年11月14日 17:10
 */
@RestController
public class UserController {

	@Autowired
	private UserService userService;

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


	@GetMapping("/user/list")
	public Flux<User> getUserById() {
		return userService.getAllUser();
	}

	@PostMapping("user")
	public Mono<Void> addUser(@RequestBody User user) {
		return userService.addUser(Mono.just(user));
	}
}

4.5 测试结果

image-20240112165119247

image-20240112165152435

5.基于函数式编程实现

5.1 创建UserHandler
package com.xq.webflux_test2.handler;

import com.xq.webflux_test.entity.User;
import com.xq.webflux_test.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @Description:
 * @author: xq0136
 * @date: 2023年11月15日 17:17
 */
public class UserHandler {

	private final UserService userService;

	public UserHandler(UserService userService) {

		this.userService = userService;
	}

	public Mono<ServerResponse> getUserById(ServerRequest request){

		int userId  = Integer.valueOf(request.pathVariable("id"));
		Mono<User> userMono = this.userService.getUserById(userId);
		Mono<ServerResponse> notFount = ServerResponse.notFound().build();
		//把userMono进行转换返回
		//使用reactor 操作符flatMap
		return userMono.flatMap(user -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(user))
				.switchIfEmpty(notFount);
	}

	//查询所有
	public Mono<ServerResponse> getAllUsers(ServerRequest request){
		Flux<User> allUser = this.userService.getAllUser();

		return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser,User.class);
	}


	public Mono<ServerResponse> saveUser(ServerRequest request){
		//得到user对象
		Mono<User> userMono = request.bodyToMono(User.class);
		return ServerResponse.ok().build(this.userService.addUser(userMono));
	}
}

5.2 测试
/**
 * @Description:
 * @author: xy
 * @date: 2024年01月12日 17:16
 */
public class Server {

   public static void main(String[] args) throws Exception {
      Server server = new Server();
      server.createReactorServer();
      System.out.println("enter to exit");
      System.in.read();
   }


     //1 创建Router路由
    public RouterFunction<ServerResponse> routingFunction() {
        //创建hanler对象
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHandler(userService);
        //设置路由
        return RouterFunctions.route(
                GET("/users/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
                .andRoute(GET("/users").and(accept(APPLICATION_JSON)),handler::getAllUsers);
    }

   //2 创建服务器完成适配
   public void createReactorServer() {
      //路由和handler适配
      RouterFunction<ServerResponse> route = routingFunction();
      //将路由转为Handler
      HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
      ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
      //创建服务器
      HttpServer httpServer = HttpServer.create();
      httpServer.handle(adapter).bindNow();
   }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值