spring webflux响应式编程学习

函数式编程的优点:

例子:当我们有大量的数据要进行比较的时候,并且我们要求出里面的最小值,这个时候我们就需要使用线程池的技术,将数据拆分成几份,然后放到线程池中,再进行比较,当然过程是比较繁琐的,这个时候我们可以使用jdk8的stream流结合并行处理(底层使用到了线程池技术)来进行操作。说白了函数式编程是一种思想。同时语法也相较于命令式编程简单了不少。

其中:@FunctionalInterface是jdk8新引入的一个函数式接口。

public class MinTest {
    public static void main(String[] args) {
        int[] nums = {10, 23, 34, 78, 67, 24};
        int min = Integer.MAX_VALUE;
        for (int i : nums) {
            if (i < min) {
                min = i;
            }
        }
        System.out.println(min);
        // jdk8
        int min2 = IntStream.of(nums).parallel().min().getAsInt();
        System.out.println(min2);
    }
}
​



public class TreadTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("命令式编程");
            }
        }).start();
        
        new Thread(() -> System.out.println("函数式编程")).start();
    }
}

stream流:

stream流的主要关注点就是,怎样将数据进行高效的处理。

package org.lambda;
​
import java.util.stream.IntStream;
​
/**
 * 演示惰性求值
 * 也就是说在你没有调用终止操作符的情况下,你写的代码是不会进行执行的
 */
public class LazyTest {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3, 4, 5};
        int sum = IntStream.of(nums).map(LazyTest::doubleNum).sum();
        System.out.println("结果:" + sum);
​
        System.out.println("惰性求值:就是没有终止操作的情况下,中间操作不会执行");
        IntStream.of(nums).map(LazyTest::doubleNum);
    }
​
    public static int doubleNum(int i) {
        System.out.println("执行了数据乘以2");
        return i * 2;
    }
    // 运行结果:
    // 执行了数据乘以2
    //执行了数据乘以2
    //执行了数据乘以2
    //执行了数据乘以2
    //执行了数据乘以2
    //结果:30
    //惰性求值:就是没有终止操作的情况下,中间操作不会执行
}

流的创建:

package org.lambda;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;
​
public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
​
        // 从集合中创建流
        list.stream();
        list.parallelStream();
​
        // 从数组中创建流
        Arrays.stream(new int[]{1, 2, 3, 4, 5});
​
        // 创建数据流
        IntStream.of(1, 2, 3, 4, 5);
        IntStream.rangeClosed(1, 10);
​
        // 使用random创建一个无限流
        new Random().ints().limit(10);
​
        // 自己创建一个流
        Random random = new Random();
        Stream.generate(() -> random.nextInt()).limit(20);
    }
}

下面这个代码在执行的时候没有看到想要的效果:对应视频:https://www.mashibing.com/study?courseNo=1956&sectionNo=85761&courseVersionId=2640

package org.lambda;
​
import java.util.stream.IntStream;
​
public class StreamDemo5 {
    public static void main(String[] args) {
        System.out.println("开始执行...");
        IntStream.range(0, 1).peek(StreamDemo5::debug).count();
        System.out.println("执行完毕");
    }
​
    public static void debug(int i) {
        System.err.println("debug: " + i);
//        System.out.flush(); // 强制刷新
//        try {
//            TimeUnit.SECONDS.sleep(5);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }
}

在使用并行流的时候,jdk底层使用的都是同一个线程池,就会产生一个问题,多个任务的时候就可能会出现排队等待的情况,这个时候我们可以使用自定义的线程池。

ForkJoinPool pool = new ForkJoinPool(20);
pool.submit(() -> IntStream.range(0, 100).parallel().peek(StreamDemo5::debug).count());
// 处理完毕关闭线程池
pool.shutdown();
// 让主线程等待该线程执行完毕
// 我们的线程池和主线程是守护关系,主线程退出,我们创建的这个线程池也跟着退出了
synchronized (pool) {
    try {
        pool.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Spring webflux响应式编程

响应式编程的基本概念和规范介绍:

是基于发布者订阅者模式来实现的。

响应式编程框架

-- RxJava、Vertx、Reactor等等,我们重点学习Reactor框架。

Project Reactor框架简介

  • 对响应式流规范的一种实现
  • Spring WebFlux 默认的响应式框架
  • 完全异步非阻塞,对背压的支持
  • 提供两个异步序列API:Flux[N] 和 Mono [0|1]
  • 提供对响应式流的操作

demo演示:

需要引入两个jar包

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.4.2</version>
</dependency>

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <version>3.4.2</version>
    <scope>test</scope>
</dependency>

Flux演示:

package com.atguigu.reactor;

import reactor.core.publisher.Flux;

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        // 1.响应式流直接列举出来
        Flux flux = Flux.just("hello", "world");
        flux.subscribe(System.out::println);

        // 2.从数组中创建
        Flux<String> fluxArr = Flux.fromArray(new String[]{"hello", "world"});
        fluxArr.subscribe(System.out::println);

        // 3.实现iterable接口的集合
        Flux<String> stringFlux = Flux.fromIterable(Arrays.asList("hello", "world"));
        stringFlux.subscribe(System.out::println);

        // 4.范围创建
        Flux<Integer> range = Flux.range(10, 5);
        range.subscribe(System.out::println);
    }
}

Mono演示:

import reactor.core.publisher.Mono;

import java.util.Optional;

public class TestMono {
    public static void main(String[] args) {
        // 1.指定元素(只能指定一个元素)
        Mono<String> a = Mono.just("a");
        a.subscribe(System.out::println);

        //2.指定空元素
        // 两者底层源码的实现是一样的
        Mono<String> nulle= Mono.justOrEmpty(null);
        nulle.subscribe(System.out::println);
        Mono<String> nullb= Mono.justOrEmpty(Optional.empty());
        nullb.subscribe(System.out::println);
    }
}

Flux 和 Mono 使用 from(Publisher p) 工厂方法

Flux.from(new Publisher<String>() {
    // 发布者生产元素
    @Override
    public void subscribe(Subscriber<? super String> subscriber) {
        for (int i = 0; i < 10; i++) {
            System.out.println("hello---" + i);
        }
    }
    // 消费者消费元素
}).subscribe(
        System.out::println,
        System.err::println,
        () -> System.out.println("处理结束")
);

defer工厂创建序列(在订阅时决定其行为)

package com.atguigu.reactor;

import reactor.core.publisher.Mono;

public class ReactorDemo01 {
    static boolean isValidValue(String value) {
        System.out.println("调用了 isValidValue的方法");
        return true;
    }

    static String getData(String value) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "echo:" + value;
    }

    static Mono<String> requestData(String value){
        return isValidValue(value) ? Mono.fromCallable(() -> getData(value))
                : Mono.error(new RuntimeException("is not valid value"));
    }

    static Mono<String> requestDeferData(String value) {
        return Mono.defer(() ->
                isValidValue(value) ?
                        Mono.fromCallable(() -> getData(value)) :
                        Mono.error(new RuntimeException())
        );
    }

    public static void main(String[] args) {
//        requestData("张三");
// 下面的代码如果不进行订阅就不会执行
//        requestDeferData("张三");
        requestDeferData("张三").subscribe();
    }

}

订阅响应式流

// 1. 最基本的订阅,不处理任何事件
subscribe();

// 2. 订阅并处理数据(onNext)
subscribe(Consumer<T> dataConsumer);

// 3. 订阅并处理数据和错误(onNext, onError)
subscribe(Consumer<T> dataConsumer, Consumer<Throwable> errorConsumer);

// 4. 订阅并处理数据、错误和完成信号(onNext, onError, onComplete)
subscribe(Consumer<T> dataConsumer, 
          Consumer<Throwable> errorConsumer, 
          Runnable completeConsumer);

// 5. 订阅并处理所有事件 + 可取消订阅(onNext, onError, onComplete, onSubscribe)
subscribe(Consumer<T> dataConsumer,
          Consumer<Throwable> errorConsumer,
          Runnable completeConsumer,
          Consumer<Subscription> subscriptionConsumer);

// 6. 使用自定义 Subscriber 订阅
subscribe(Subscriber<T> subscriber);

订阅响应式流

package com.atguigu.reactor;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class ReactorDemo02 {
    public static void main(String[] args) {
        // 1.subscribe
//        Flux.range(100, 5)
//                .filter(e -> e % 3 == 0)
//                .map(e -> "hello " + e)
//                // 在每个数据项(onNext 信号)被下游消费之前,执行一个指定的操作(比如打印日志、记录指标、调试等),但不会改变数据流本身。
//                .doOnNext(System.out::println)
//                .subscribe();

        // 2.增加订阅者
//        Flux.range(100, 5)
//                .filter(e -> e % 3 == 0)
//                .subscribe(System.out::println);

        // 3.增加对异常的处理
//        Flux.from(publisher -> {
//            for(int i=0;i<5;i++){
//                publisher.onNext(i);
//            }
//            publisher.onError(new Exception("测试数据异常"));
//        }).subscribe(
//                num -> System.out.println(num),
//                ex -> System.err.println("异常情况: " + ex)
//        );

        // /4、完成事件的处理
//        Flux.from(new Publisher<Integer>() {
//            @Override
//            public void subscribe(Subscriber s) {
//                for(int i = 0; i < 10; i++) {
//                    s.onNext(i);
//                }
//                s.onComplete();
//            }
//        }).subscribe(
//                item -> System.out.println("onNext :" + item),
//                ex -> System.out.println("异常情况:" + ex),
//                () -> System.out.println("处理完毕")
//        );

        // 5.手动控制订阅的数据量
//        Flux.range(1,  100)
//                .subscribe(
//                        data -> System.out.println("onNext:" + data), // 接收数据
//                        ex -> System.err.println("异常信息: " + ex), // 处理异常
//                        ()-> System.out.println("onComplete"), // 流完成回调
//                        subscription -> { // 订阅成功后获取 Subscription 对象
//                            subscription.request( 5); // 请求5条数据
//                            subscription.cancel(); // 取消订阅
//                        }
//                );

        //6、实现自定义订阅者
        Subscriber<String> subscriber = new Subscriber<String>() {
            volatile Subscription subscription;
            @Override
            public void onSubscribe(Subscription s) {
                subscription = s;
                System.out.println("initial request for 1 element");
                subscription.request(1);
            }

            @Override
            public void onNext(String s) {
                System.out.println("onNext:" + s);
                System.out.println("requesting 1 more element" );
                subscription.request(1);

            }

            @Override
            public void onError(Throwable t) {
                System.err.println("出现异常:" +t);
            }

            @Override
            public void onComplete() {
                System.out.println("onComplete");
            }
        };
        Flux<String> stream = Flux.just("hello", "everyone", "!");
        stream.subscribe(subscriber);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 最终的输出结果:
        // initial request for 1 element
        //onNext:hello
        //requesting 1 more element
        //onNext:everyone
        //requesting 1 more element
        //onNext:!
        //requesting 1 more element
        //onComplete
    }

}

最后一段代码的解释

// 定义自定义订阅者,实现 Subscriber 接口(泛型指定接收 String 类型数据)
Subscriber<String> subscriber = new Subscriber<String>() {
    // 定义 volatile 修饰的 Subscription 对象:
    // volatile 保证多线程下可见性,Subscription 是「发布者-订阅者」的控制通道(用于请求数据/取消订阅)
    volatile Subscription subscription;

    // 【第一步执行】订阅成功后触发的方法(发布者调用)
    @Override
    public void onSubscribe(Subscription s) {
        // 保存发布者传入的 Subscription 对象,后续通过它控制数据请求
        subscription = s;
        // 打印初始化请求日志
        System.out.println("initial request for 1 element");
        // 向发布者「请求1条数据」(背压核心:订阅者主动要数据,而非发布者推送)
        subscription.request(1);
    }

    // 【接收数据时触发】发布者有数据推送时调用(每次接收1条数据执行1次)
    @Override
    public void onNext(String s) {
        // 打印接收到的具体数据
        System.out.println("onNext:" + s);
        // 打印“请求下1条数据”的日志
        System.out.println("requesting 1 more element" );
        // 再次向发布者请求1条数据(形成“要1条→处理1条→再要1条”的循环)
        subscription.request(1);
    }

    // 【异常时触发】数据流出现异常时调用(触发后数据流终止)
    @Override
    public void onError(Throwable t) {
        // 打印异常信息(本案例无异常,此方法不会执行)
        System.err.println("出现异常:" +t);
    }

    // 【完成时触发】发布者无更多数据可发送时调用(所有数据处理完后执行)
    @Override
    public void onComplete() {
        // 打印流完成日志
        System.out.println("onComplete");
    }
};

用操作符转换响应式流

package com.atguigu.reactor;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux;

public class ReactorDemo03 {
    public static void main(String[] args) {
        // map映射
//        Flux.range(1,10)
//                .map(item -> "hello msb" + item)
//                .subscribe(System.out::println);

        // index索引
//        Flux.range(0,10)
//                .map(item -> "hello msb " + item)
//                .index()
//                .subscribe(item ->{
//                    // 二元组第一个元素 ,编号 0开始
//                    Long t1 = item.getT1();
//                    // 二元组第二个元素,也就是具体值
//                    String t2 = item.getT2();
//                    System.out.println(t1 + ":" + t2);
//
//                },
//                        System.err::println,
//                        () -> System.out.println("流已经处理完毕"));

        // 时间戳
        Flux.range(0,10)
                .map(item -> "hello msb " + item)
                .timestamp()
                .subscribe(item ->{
                            // 二元组第一个元素 ,编号 0开始
                            Long t1 = item.getT1();
                            // 二元组第二个元素,也就是具体值
                            String t2 = item.getT2();
                            System.out.println(t1 + ":" + t2);

                        },
                        System.err::println,
                        () -> System.out.println("流已经处理完毕"));
    }
}

过滤响应式流

package com.atguigu.reactor;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;

public class ReactorDemo04 {
    public static void main(String[] args) throws InterruptedException {
        Flux.interval(Duration.ofMillis(500)) // Flux<Long>
                .map(item -> "msg" + item) // Flux<String>
                .skipUntilOther(Mono.just("start").delayElement(Duration.ofSeconds(3)))
                .takeUntilOther(Mono.just("end").delayElement(Duration.ofSeconds(6)))
                .subscribe(
                        item -> System.out.println("onNext: " + item),
                        ex -> System.err.println("onError: " + ex),
                        () -> System.out.println("onCompleted")
                );

        Thread.sleep(10000);
    }

}

运行结果:

onNext: msg5 → (注:实际应为 msg6,可能是打印延迟/计时精度问题,核心逻辑是3秒后开始)
onNext: msg6
onNext: msg7
onNext: msg8
onNext: msg9
onNext: msg10
onCompleted

对上面这段代码的分析:

这段代码是基于 Reactor 响应式框架 实现的一个流式处理示例,核心功能是:生成一个周期性的数据流,通过 skipUntilOther 和 takeUntilOther 两个操作符过滤数据流的输出时段,最终只输出指定时间窗口内的元素。

数据流生成:Flux.interval(Duration.ofMillis(500))

  • 作用:创建一个无限的、周期性的数据流(Flux),每隔 500 毫秒(0.5 秒)发射一个递增的 Long 类型数字(0,1,2,3,4...)。
  • 时间线(简化):0 秒 → 发射 0;0.5 秒 → 发射 1;1 秒 → 发射 2;1.5 秒 → 发射 3;2 秒 → 发射 4;2.5 秒 → 发射 5;3 秒 → 发射 6;... 以此类推。

map:对生成的元素进行加工。

跳过前序数据:skipUntilOther(Mono.just("start").delayElement(Duration.ofSeconds(3)))

  • 核心逻辑:skipUntilOther 表示阻塞数据流输出,直到另一个信号(Mono)触发,触发后才开始放行后续元素。

  • 这里的触发信号是:一个延迟 3 秒后发射 "start" 的 Mono。

  • 效果:前 3 秒内发射的所有元素(msg0~msg5)都会被跳过,3 秒后才开始输出后续元素

    • 为什么是 msg0~msg5 被跳过?3 秒内一共发射了 3000ms / 500ms = 6 个元素(0→5),对应 msg0~msg5,这些都被跳过。

终止数据流:takeUntilOther(Mono.just("end").delayElement(Duration.ofSeconds(6)))

  • 核心逻辑:takeUntilOther 表示放行数据流输出,直到另一个信号(Mono)触发,触发后立即终止数据流。

  • 这里的触发信号是:一个延迟 6 秒后发射 "end" 的 Mono。

  • 效果:6 秒后停止输出,仅放行 3 秒~6 秒 之间发射的元素。

    • 3 秒~6 秒 时长为 3 秒,共发射 3000ms / 500ms = 6 个元素:3 秒 → msg6;3.5 秒 → msg7;4 秒 → msg8;4.5 秒 → msg9;5 秒 → msg10;5.5 秒 → msg11?✨ 关键:6 秒时触发 "end" 信号,数据流立即终止,因此 msg11 不会被发射(6 秒整的元素还没到发射时间),最终输出 msg5?不,看运行结果是 msg5~msg10,这里修正:重新计算时间线(更精准):

      • 0.0s → 0(msg0)、0.5s→1(msg1)、1.0s→2(msg2)、1.5s→3(msg3)、2.0s→4(msg4)、2.5s→5(msg5)、3.0s→6(msg6)、3.5s→7(msg7)、4.0s→8(msg8)、4.5s→9(msg9)、5.0s→10(msg10)、5.5s→11(msg11)、6.0s→触发 end 信号。
      • 3 秒后放行,因此 msg6(3.0s)、msg7(3.5s)、msg8(4.0s)、msg9(4.5s)、msg10(5.0s)会被输出;msg11(5.5s)是否输出?
      • 运行结果显示到 msg10,原因是:takeUntilOther 触发后立即终止,5.5s 发射的 msg11 还没来得及输出,6 秒的 end 信号已触发,因此终止在 msg10。

订阅消费:subscribe(...)

  • 三个回调函数分别对应:

    • onNext:每收到一个元素就打印(如 "onNext: msg5");
    • onError:数据流出错时打印异常(本例无错误,不会执行);
    • onCompleted:数据流正常终止时打印(触发 end 信号后执行)。

收集响应式流:

  • 收集到list
    public static void main(String[] args) {
        Flux.just(1,2,36,4,25,6,7)
                ////CollectionSortedList 默认是升序 输出:[1, 2, 4, 6, 7, 25, 36]
//                .collectSortedList()
                // 倒序:[36, 25, 7, 6, 4, 2, 1]
                .collectSortedList(Comparator.reverseOrder())
                .subscribe(System.out::println);
    }
请注意,收集集合中的序列元素可能耗费资源,当序列具有许多元素时这种现象尤为突出。此外,尝试在无限流上收集数据可能消耗所有可用的内存
  • 收集到map
       Flux.just(1,2,3,4,5,6)
                // 下面括号中的是key
                .collectMap(item -> "key:num" + item)
                .subscribe(System.out::println);
// output: {key:num1=1, key:num2=2, key:num5=5, key:num6=6, key:num3=3, key:num4=4}

Flux.just(1,2,3,4,5,6)
                .collectMap(
                        item -> "key:" + item,
                        item -> "value:" + item
                ).subscribe(System.out::println);
// output: {key:2=value:2, key:1=value:1, key:6=value:6, key:5=value:5, key:4=value:4, key:3=value:3}

   // 追加元素
        Flux.just(1,2,3,4,5,6)
                .collectMap(
                        integer -> "key:" + integer,
                        integer -> "value:" + integer,
                        ()->{
                            Map<String,String> map = new HashMap<>();
                            for(int i =7 ; i <10;i++){
                                map.put("key:" + i , "value:" + i);
                            }
                            return map;
                        }
                ).subscribe(System.out::println);
// output: {key:2=value:2, key:1=value:1, key:6=value:6, key:5=value:5, key:4=value:4, key:3=value:3, key:9=value:9, key:8=value:8, key:7=value:7}
  • CollectionMultimap 使用
Flux.just(1,2,3,4,5)
                .collectMultimap(
                        item -> "key:" + item,
                        item -> {
                            List<String> values = new ArrayList<>();
                            for(int i=0; i < item ; i ++){
                                values.add("value:" + i);
                            }
                            return values;
                        }
                ).subscribe(System.out::println);
// output: {key:2=[[value:0, value:1]], key:1=[[value:0]], key:5=[[value:0, value:1, value:2, value:3, value:4]], key:4=[[value:0, value:1, value:2, value:3]], key:3=[[value:0, value:1, value:2]]}

Flux.just(1,2,3,4,5)
                .collectMultimap(
                        item -> "key:" + item,
                        item -> {
                            List<String> values = new ArrayList<>();
                            for(int i =0; i < item ; i ++){
                                values.add("value:" + i);
                            }
                            return values;
                        },
                        //扩充
                        ()->{
                            Map map = new HashMap<String,List>();
                            List<String> list = new ArrayList<>();
                            for(int i=0; i<3; i++){
                                list.clear();
                                for(int j = 0 ;j <i;j++){
                                    list.add("ele:" +j);
                                }
                                map.put(i + ":key",list);
                            }
                            return map;
                        }
                ).subscribe(System.out::println);
// output: {key:2=[[value:0, value:1]], key:1=[[value:0]], 0:key=[ele:0, ele:1], 1:key=[ele:0, ele:1], key:5=[[value:0, value:1, value:2, value:3, value:4]], key:4=[[value:0, value:1, value:2, value:3]], key:3=[[value:0, value:1, value:2]], 2:key=[ele:0, ele:1]}
   

3.4 repeat 操作符的使用:

Flux.just(1,2,3)
                .repeat(3) // 表示:重复三次, 实际会打印四次
                .subscribe(System.out::println);
        // output: 1
        //2
        //3
        //1
        //2
        //3
        //1
        //2
        //3
        //1
        //2
        //3

     

3.5 defaultIfEmpty 操作符的使用:

   Flux.empty().defaultIfEmpty("hello zyq").subscribe(System.out::println);
        // output: hello zyq

3.6 distinct 操作符的使用(是一个全局性质的去重):

  Flux.just(1,1,1,2,2,2,3,3,1,1,1,2,2,2)
                .distinctUntilChanged()
                .subscribe(System.out::print);
        System.out.println();
        Flux.just(1,1,1,2,2,2,3,3,1,1,1,2,2,2)
                .distinct()
                .subscribe(System.out::print);
        
        // output: 12312
        //123

3.7 distinctUntilChanged 操作符的使用(是一个局部性质的去重):

见上

裁剪流中的元素

        // any是一个短路操作
        Flux.just(1,2,3,4,5,6)
                .doOnNext(item -> System.out.println(item))
                .any(item -> item % 2 == 0)
                .subscribe(System.out::println);
        // output: 1
        //2
        //true

  Flux.range(1, 5)
                .reduce(0, (item1, item2) -> {
                    System.out.println("item1:" + item1);
                    System.out.println("item2:" + item2);
                    return item1 + item2;
                }).subscribe(System.out::println);
        // output:1
        //2
        //true
        //item1:0
        //item2:1
        //item1:1
        //item2:2
        //item1:3
        //item2:3
        //item1:6
        //item2:4
        //item1:10
        //item2:5
        //15

// scan: 相较于上面,会将中间的计算结果也给打印出来
        Flux.range(1, 5)
                .scan(0, (num1, num2) -> {
                    System.out.println("num1:" + num1);
                    System.out.println("num2:" + num2);
                    return num1 + num2;
                })
                .subscribe(System.out::println);
        // output: 1
        //2
        //true
        //0
        //num1:0
        //num2:1
        //1
        //num1:1
        //num2:2
        //3
        //num1:3
        //num2:3
        //6
        //num1:6
        //num2:4
        //10
        //num1:10
        //num2:5
        //15

  // 模拟滑动窗口求平均值 1,2,3,4,5 的平局值是3.0 | 2,3,4,5,6的平均值是4.0
  // TODO 现在这段代码看着有点难理解,以后再看,先放过
        int arrLength = 5;
        Flux.just(1, 2,3,4,5,6)
                .index()
                .scan(new int[arrLength], (arr, entry) -> {
                    arr[(int) (entry.getT1() % arrLength)] = entry.getT2();
                    return arr;
                })
                .skip(arrLength) // 当窗口数组占满之后,开始计算平均值,因此要跳过没有占满的情况
                .map(array -> Arrays.stream(array).sum() * 1.0 / arrLength)
                .subscribe(System.out::println);
        // output: 3.0
        //4.0

   Flux.just(1,2,3,4)
                .thenMany(Flux.just(5,6,7))
                .subscribe(System.out::println);
        // output: 5
        //6
        //7

组合响应式流

  • concat操作符的使用
 // concat 流是按照顺序订阅,并且按照顺序处理的
//        Flux.concat(
//                Flux.range(10, 5).delayElements(Duration.ofMillis(100))
//                        .doOnSubscribe(subscription -> System.out.println("订阅第一个流")),
//                Flux.range(100, 5).delayElements(Duration.ofMillis(100))
//                        .doOnSubscribe(subscription -> System.out.println("订阅第二个流"))
//        ).subscribe(System.out::println);
//        Thread.sleep(10 * 1000);
        // output: 订阅第一个流
        //10
        //11
        //12
        //13
        //14
        //订阅第二个流
        //100
        //101
        //102
        //103
        //104
  • merge操作符的使用
  // merge 流是同时订阅同时处理
        Flux.merge(
                Flux.range(10, 5).delayElements(Duration.ofMillis(100))
                        .doOnSubscribe(subscription -> System.out.println("订阅第一个流")),
                Flux.range(100, 5).delayElements(Duration.ofMillis(100))
                        .doOnSubscribe(subscription -> System.out.println("订阅第二个流"))
        ).subscribe(System.out::println);
        Thread.sleep(10 * 1000);
        // output: 订阅第一个流
        //订阅第二个流
        //100
        //10
        //101
        //11
        //102
        //12
        //103
        //13
        //104
        //14
  • zip操作符的使用
        // zip 相当于是拉链的意思,表示一一对应的处理,并且以元素最少的那个流为准,以时间最长的那个流为准
        Flux.zip(
                Flux.range(1, 10)
                        .delayElements(Duration.ofMillis(10)),
                Flux.range(100, 10)
                        .delayElements(Duration.ofMillis(10))
        ).subscribe(System.out::println);
        Thread.sleep(10 * 1000);
        // output: [1,100]
        //[2,101]
        //[3,102]
        //[4,103]
        //[5,104]
        //[6,105]
        //[7,106]
        //[8,107]
        //[9,108]
        //[10,109]
  • combineLatest操作符的使用
      // combineLatest 匹配最近一个元素
        Flux.combineLatest(
                Flux.range(1, 10)
                        .delayElements(Duration.ofMillis(1000)),
                Flux.range(100, 10)
                        .delayElements(Duration.ofMillis(2000)),
                (integer1, integer2) -> integer1 + "===" + integer2
        ).subscribe(System.out::println);
        Thread.sleep(10*1000);
        // output: 1===100
        //2===100
        //3===100
        //3===101
        //4===101
        //5===101
        //5===102
        //6===102
        //7===102
        //7===103
        //8===103
        //9===103

流元素的批处理

package com.atguigu.reactor;

import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.ArrayList;

public class ReactorDemo09 {
    public static void main(String[] args) throws Exception {

//        Flux.range(1, 100)
//                .buffer(10)
//                .subscribe(System.out::println);
        // output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        //[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
        //[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
        //[31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
        //[41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
        //[51, 52, 53, 54, 55, 56, 57, 58, 59, 60]
        //[61, 62, 63, 64, 65, 66, 67, 68, 69, 70]
        //[71, 72, 73, 74, 75, 76, 77, 78, 79, 80]
        //[81, 82, 83, 84, 85, 86, 87, 88, 89, 90]
        //[91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

//        Flux.range(101, 20)
//                // false:在素数之前进行切割 true: 在素数之后进行切割
//                .windowUntil(ReactorDemo09::isPrime,  true)
//        .subscribe(
//                window ->
//                        window.collectList()
//                                .subscribe(
//                                        item -> System.out.println("window : " + item)
//                                )
//        );
        // output:当是false的时候: window : [101]
        //window : [102, 103]
        //window : [104, 105, 106, 107]
        //window : [108, 109]
        //window : [110, 111, 112, 113]
        //window : [114, 115, 116, 117, 118, 119, 120]

        // output:当是true的时候: window : []
        //window : [101, 102]
        //window : [103, 104, 105, 106]
        //window : [107, 108]
        //window : [109, 110, 111, 112]
        //window : [113, 114, 115, 116, 117, 118, 119, 120]

        // 你这段代码是 Reactor 响应式框架的组合操作,核心功能是:先将整数数据流按 “奇数 / 偶数” 分组,再对每个分组内的元素进行 “滑动窗口大小为 2” 的累积处理,最终打印分组标识与对应累积结果。下面逐部分拆解说明,清晰易懂:
        Flux.range(1, 7)
                .groupBy(item -> item % 2 == 0 ? "偶数" : "奇数")
                .subscribe(groupFlux -> groupFlux.scan(
                                        new ArrayList<>(),
                                        (list, element) -> {
                                            list.add(element);
                                            if (list.size() > 2) {
                                                list.remove(0);
                                            }
                                            return list;
                                        }
                                )
                                .filter(list -> !list.isEmpty())
                                .subscribe(item -> System.out.println(groupFlux.key() + "<======>" + item))
                );
        // output: 奇数<======>[1]
        //偶数<======>[2]
        //奇数<======>[1, 3]
        //偶数<======>[2, 4]
        //奇数<======>[3, 5]
        //偶数<======>[4, 6]
        //奇数<======>[5, 7]
        // 代码分析:以 “奇数” 分组为例,看 scan 的累积过程(元素顺序:1→3→5→7):
        //初始值:[](空列表,未经过滤时会生成,后续被 filter 过滤)
        //处理元素 1:列表变为 [1](长度≤2,不删除元素),输出 [1]
        //处理元素 3:列表变为 [1, 3](长度≤2,不删除元素),输出 [1, 3]
        //处理元素 5:先添加变为 [1, 3, 5],长度 > 2,移除第一个元素 1,最终列表 [3, 5],输出 [3, 5]
        //处理元素 7:先添加变为 [3, 5, 7],长度 > 2,移除第一个元素 3,最终列表 [5, 7],输出 [5, 7]

    }

    /**
     * 核心方法:判断一个int类型整数是否为素数
     * @param num 待判断的整数
     * @return true=是素数,false=不是素数
     */
    public static boolean isPrime(int num) {
        // 1. 边界条件判断:小于2的数不是素数
        if (num < 2) {
            return false;
        }

        // 2. 优化:只需判断到num的平方根(减少循环次数)
        // 原理:若num有大于其平方根的约数,则必然有一个对应的小于其平方根的约数
        int sqrt = (int) Math.sqrt(num);
        for (int i = 2; i <= sqrt; i++) {
            // 3. 若能被2到sqrt之间的任意数整除,说明不是素数
            if (num % i == 0) {
                return false;
            }
        }

        // 4. 所有条件都满足,是素数
        return true;
    }
}

flatMap:允许交错处理

  Random random = new Random();
        Flux.just(Arrays.asList(1,2,3),Arrays.asList("a","b","c","d"),Arrays.asList(7,8,9))
                .doOnNext(System.out::println)
                .flatMap(item -> Flux.fromIterable(item))
                .doOnSubscribe(subscription -> {
                    System.out.println("已经订阅");
                })// 我们增加一个延时,订阅后延时一段时间再发送
                .delayElements(Duration.ofMillis(random.nextInt(100) + 100))
                .subscribe(System.out::println);

        Thread.sleep(10*1000);
        // output: 已经订阅
        //[1, 2, 3]
        //[a, b, c, d]
        //[7, 8, 9]
        //1
        //2
        //3
        //a
        //b
        //c
        //d
        //7
        //8
        //9

concatMap:不允许交错处理

    Random random = new Random();
        Flux.just(Arrays.asList(1,2,3),Arrays.asList("a","b","c","d"),Arrays.asList(7,8,9))
                .doOnNext(System.out::println)
                .concatMap(item -> Flux.fromIterable(item))
                .doOnSubscribe(subscription -> {
                    System.out.println("已经订阅");
                })// 我们增加一个延时,订阅后延时一段时间再发送
                .delayElements(Duration.ofMillis(random.nextInt(100) + 100))
                .subscribe(System.out::println);

        Thread.sleep(10*1000);
        // output: 已经订阅
        //[1, 2, 3]
        //[a, b, c, d]
        //[7, 8, 9]
        //1
        //2
        //3
        //a
        //b
        //c
        //d
        //7
        //8
        //9

latMapSequential:不允许交错处理

Random random = new Random();
        Flux.just(Arrays.asList(1,2,3),Arrays.asList("a","b","c","d"),Arrays.asList(7,8,9))
                .doOnNext(System.out::println)
                .flatMapSequential(item -> Flux.fromIterable(item))
                .doOnSubscribe(subscription -> {
                    System.out.println("已经订阅");
                })// 我们增加一个延时,订阅后延时一段时间再发送
                .delayElements(Duration.ofMillis(random.nextInt(100) + 100))
                .subscribe(System.out::println);

        Thread.sleep(10*1000);
        // output: 已经订阅
        //[1, 2, 3]
        //[a, b, c, d]
        //[7, 8, 9]
        //1
        //2
        //3
        //a
        //b
        //c
        //d
        //7
        //8
        //9

备注:concatMap:会等待前一个子流完全处理完成后,再订阅并处理下一个源元素对应的子流。相当于 “串行处理子流”,无并发

flatMapSequential:会立即订阅所有源元素对应的子流(支持并发处理子流),但会缓存后续子流的结果,直到前一个子流的结果完全输出后,再按顺序释放后续子流的结果。

元素采样

  // 固定时间采样 每个100ms从流中获取对应的元素
//        Flux.range(1, 100)
//                .delayElements(Duration.ofMillis(10))
//                .sample(Duration.ofMillis(100))
//                .subscribe(System.out::println);
//
//        Thread.sleep(10 * 1000);

        // output: 2
        //12
        //20
        //29
        //38
        //47
        //56
        //64
        //73
        //82
        //91
        //100

        // 随机时间采样
        Random random = new Random();
        Flux.range(0, 20)
                .delayElements(Duration.ofMillis(100))
                .sampleTimeout(item -> Mono.delay(Duration.ofMillis(random.nextInt(100) + 50)), 20)
                .subscribe(System.out::println);

        Thread.sleep(10 * 1000);
        // output: 0
        //1
        //2
        //8
        //10
        //11
        //13
        //15
        //16
        //17
        //19

将响应式流转换为阻塞结构

package com.atguigu.reactor;

import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.stream.Stream;

public class ReactorDemo12 {
    public static void main(String[] args) throws InterruptedException {
        //public static void main(String[] args) {
// Flux.just(1,2,3).toIterable();
// Stream<Integer> stream = Flux.just(1,2,3,4).toStream();
//1、验证toIterable为阻塞
//        Iterable<Integer> integers = Flux.just(1, 2, 3, 4)
//                .delayElements(Duration.ofSeconds(1))
//                .toIterable();
//
//        System.out.println("----------------");
//        for(Integer num : integers){
//            System.out.println(num);
//        }
//        System.out.println("================");
//    }
        // output: ----------------
        //1
        //2
        //3
        //4
        //================

        // 2、我么可以做一下改进 改造成不阻塞的方式
//        Flux.just(1, 2, 3)
//                .delayElements(Duration.ofSeconds(1))
//                .subscribe(System.out::println);
//
//        System.out.println("============");
//        System.out.println("============");
        //output: ============
        //============

        //3、toStream进行阻塞
//        Stream<Integer> integerStream = Flux.just(1, 2, 3).delayElements(Duration.ofSeconds(1))
//                .toStream();
//
//        System.out.println("================");
//        integerStream.forEach(System.out::println);
//        System.out.println("================");
        // output: ================
        //1
        //2
        //3
        //================

        //4、BlockFirst 只拿第一个,其他不处理
//        Integer integer = Flux.just(1, 2, 3)
//                .delayElements(Duration.ofSeconds(1))
//                .doOnNext(item -> System.out.println("onNext: " + item))
//                .blockFirst();
//
//        System.out.println("===========");
//        System.out.println(integer);
//        System.out.println("===========");
//        Thread.sleep( 10*1000);
        // output: onNext: 1
        //===========
        //1
        //===========

        // blockLast 直到流的最后一个元素
//        Integer integer2 = Flux.just(1, 2, 3)
//                .delayElements(Duration.ofSeconds(1))
//                .doOnNext(item -> System.out.println("onNext:" + item))
//                .blockLast();
//
//        System.out.println("==========");
//        System.out.println(integer2);
//        System.out.println("==========");
        // output: onNext:1
        //onNext:2
        //onNext:3
        //==========
        //3
        //==========

        Flux<Integer> integerFlux = Flux.just(1, 2, 3).delayElements(Duration.ofSeconds(1));
        integerFlux.subscribe(item -> System.out.println("第一个订阅" + item));
        integerFlux.subscribe(item -> System.out.println("第二个订阅" + item));
        integerFlux.subscribe(item -> System.out.println("第三个订阅" + item));
        Integer integer2 = integerFlux.blockLast();
        System.out.println("阻塞最后一个元素:" + integer2);
        System.out.println("================");
        Thread.sleep( 10*1000);
        // output: 第一个订阅1
        //第二个订阅1
        //第三个订阅1
        //第一个订阅2
        //第二个订阅2
        //第三个订阅2
        //第一个订阅3
        //第三个订阅3
        //第二个订阅3
        //阻塞最后一个元素:3
        //================
    }
}

在处理元素时查看元素

-- 这个也就是我们常说的副作用

    Flux.just(1, 2, 3)
                .concatWith(Flux.error(new RuntimeException("手动异常")))
                // .doOnEach(item -> System.out.println(item))
                .subscribe(
                        item -> System.out.println("onNext:" + item),
                        ex -> System.err.println("onError:" + ex),
                        () -> System.out.println("处理完毕")
                );
        // output: onNext:1
        //onNext:2
        //onNext:3
        //onError:java.lang.RuntimeException: 手动异常

物化和非物化信号

 Flux.just(1,2,3)
                .delayElements(Duration.ofMillis(1000))
                .publishOn(Schedulers.parallel())
                .concatWith(Flux.error(new Exception("手动异常")))
                // materialize 这玩意叫物化,也就是说将异常信息也封装成为了一个信号了,如果不进行物化的话,在程序运行的过程中直接抛出异常
                // 不物化 dematerialize
                .materialize()
                .doOnEach(item -> System.out.println(item.isOnComplete()))
                .subscribe(System.out::println);

        Thread.sleep( 10*1000);
        // output: false
        //onNext(1)
        //false
        //onNext(2)
        //false
        //onNext(3)
        //false
        //onError(java.lang.Exception: 手动异常)
        //true

以编程方式创建流

push和create方式创建流

// TODO 下面的代码现在看不懂的了,以后再看
package com.atguigu.reactor.programe;

import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.stream.IntStream;

public class ProgramingDemo01 {
    public static void main(String[] args) throws Exception{
        // push  工厂方法能通过适配一个单线程生产者来便捷创建 Flux 实例。此方法对于适配异步、单线程、多值 API 非常有用,而无须关注背压和取消,push 方法本身包含背压和取消。
//        Flux<Integer> push = Flux.push(new Consumer<FluxSink<Integer>>() {
//            @Override
//            public void accept(FluxSink<Integer> fluxSink) {
//                // 从数据库中获取数据
//                // FluxSink 追加到响应式流中,这样将命令处理方式,转化为响应式处理方式
//                IntStream.range(1, 10)
//                        .forEach(item -> fluxSink.next(item));
//            }
//        });
//        push.subscribe(System.out::println);
//        Thread.sleep( 5*1000);
        // output:1
        //2
        //3
        //4
        //5
        //6
        //7
        //8
        //9

        // create 方法能从不同的线程中发出事件
        MyEventProcessor myEventProcessor = new MyEventProcessor();
        Flux<String> bridage = Flux.create(sink -> {
            myEventProcessor.register(new MyEventListener<String>() {
                @Override
                public void onDataChunk(List<String> chunk) {
                    for (String s : chunk) {
                        sink.next(s);
                    }
                }

                @Override
                public void processComplete() {
                    sink.complete();
                }
            });
        });
        bridage.subscribe(
                System.out::println,
                ex -> System.err.println(ex),
                () ->System.out.println("处理完毕")
        );
        myEventProcessor.process();
        //Thread.sleep(5*1000);
    }
    static class MyEventProcessor{
        private MyEventListener listener;
        private Random random = new Random();
        void register(MyEventListener listener){
            this.listener = listener;
        }

        public void process(){
            while(random.nextInt(10) % 3 != 0){
                List<String> dataChunk = new ArrayList<>();
                for(int i = 0 ;i < 10;i++){
                    dataChunk.add("data - " + i );
                }
                listener.onDataChunk(dataChunk);
            }
            listener.processComplete();;
        }
    }
    interface  MyEventListener<T>{
        void onDataChunk(List<T> chunk);

        void processComplete();
    }
}

generate方法

package com.atguigu.reactor.programe;

import reactor.core.publisher.Flux;
import reactor.core.publisher.SynchronousSink;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;

public class ProgramingDemo02 {
    public static void main(String[] args) throws Exception{
        // 斐波那契额数列打印
        Flux.generate(
                        // 通过Callable提供初始状态实例
                        new Callable<ArrayList<Long>>() {
                            @Override
                            public ArrayList<Long> call() throws Exception {
                                final ArrayList<Long> longs = new ArrayList<>();
                                longs.add(0L);
                                longs.add(1L);
                                return longs;
                            }
                        },
                        // 负责斐波拉契数列
                        // 函数第一个参数数据、函数第二个参数类型、返回值
                        new BiFunction<ArrayList<Long>, SynchronousSink<Long>, ArrayList<Long>>() {
                            @Override
                            public ArrayList<Long> apply(ArrayList<Long> longs, SynchronousSink<Long> sink) {
                                Long along = longs.get(longs.size() - 1);
                                Long along1 = longs.get(longs.size() - 2);
                                sink.next(along);
                                longs.add(along + along1);
                                return longs;
                            }
                        }).delayElements(Duration.ofMillis(500))
                .take(10)
                .subscribe(System.out::println);

        Thread.sleep(5000);
        // output: 1
        //1
        //2
        //3
        //5
        //8
        //13
        //21
        //34
    }
}

将disposable资源包装到响应式流中

package com.atguigu.reactor.programe;

import reactor.core.publisher.Flux;
import reactor.core.publisher.SynchronousSink;
import reactor.util.function.Tuples;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.function.BiFunction;

public class ProgramingDemo03 {
    static class Connection implements AutoCloseable {
        private final Random rnd = new Random();
        static Connection newConnection() {
            System.out.println("创建Connection对象");
            return new Connection();
        }
        public Iterable<String> getData() {
            if (rnd.nextInt(10) < 3) {
                throw new RuntimeException("通信异常");
            }
            return Arrays.asList("数据1", "数据2");
        }
        // close方法可以释放内部资源,并且应该始终被调用,即使在getData执行期间发生错误也是如此。
        @Override
        public void close() {
            System.out.println("关闭Connection连接");
        }
    }
    public static void main(String[] args) {
//        try (Connection connection = Connection.newConnection()) {
//            connection.getData().forEach(data -> System.out.println("接收的数据:" +
//                    data));
//        } catch (Exception e) {
//            System.err.println("错误信息:" + e);
//        }

        Flux.using(
                Connection::newConnection,
                connection -> Flux.fromIterable(connection.getData()),
                Connection::close
        ).subscribe(
                data -> System.out.println("onNext接收到数据:" + data),
                ex -> System.err.println("onError接收到的异常信息:" +ex),
                () -> System.out.println("处理完毕")
        );
    }
}

错误处理

  • onError
  //onError
        Flux.from(new Publisher<String>() {
            @Override
            public void subscribe(Subscriber<? super String> s) {
                s.onError(new RuntimeException("手动异常"));
            }
// }).subscribe(System.out::println);
            // 使用error信号流进行异常信息收集
        }).subscribe(System.out::println, System.err::println);
        // output: java.lang.RuntimeException: 手动异常
  • onErrorMap
package com.atguigu.reactor.error;

import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
// delayElements() 延迟的是序列中「每个元素的发射时机」,而 delaySequence() 延迟的是「整个序列的启动时机」
public class ErrorDemo01 {
    private static Random random = new Random();
    private static Flux<String> recommendedBooks(String userId){
        return Flux.defer(()->{
            if(random.nextInt(10) < 7){
                return Flux.<String>error(new RuntimeException("err"))
                        // 整体向后推移时间
                        .delaySequence(Duration.ofMillis(100));
            }else{
                return Flux.just("西游记","红楼梦")
                        .delayElements(Duration.ofMillis(10));
            }
        }).doOnSubscribe(
                item -> System.out.println("请求:" + userId)
        );
    }
    private static CountDownLatch latch = new CountDownLatch(1);
    public static void main(String[] args) throws InterruptedException {

        Flux.just("user-1000")
                .flatMap(user -> recommendedBooks(user))
                .onErrorMap(throwable -> {
                    if(throwable.getMessage().equals("err")){
                        return new Exception("业务异常");
                    }
                    return new Exception("未知异常");
                })
                .subscribe(
                        System.out::println,
                        ex -> {
                            System.err.println(ex);
                            latch.countDown();
                        },
                        () ->{
                            System.out.println("处理完毕");
                            latch.countDown();
                        }
                );
        latch.await();
    }
}

  • onErrorResume

    .onErrorResume(error -> Flux.just("三国演义"))//异常捕获返回备用的流

  • onErrorReturn

    .onErrorReturn("水浒传")//异常捕获返回备用的流

  • onErrorMap

    .onErrorMap(throwable -> {
    if(throwable.getMessage().equals("err")){
    return new Exception("业务异常");
    }
    return new Exception("未知异常");
    })

背压处理

package com.atguigu.reactor.pressure;

import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.concurrent.CountDownLatch;

public class PressureDemo01 {
    public static void main(String[] args) throws InterruptedException {
        // 定义CountDownLatch的作用就是确保让守护线程执行完毕,主线程再退出
        CountDownLatch latch = new CountDownLatch(1);
        Flux.range(1,1000)
                .delayElements(Duration.ofMillis(10))
// 处理不过来的时候,只处理最近刚来的
//                .onBackpressureLatest()
// 处理不过来的时候,直接丢弃
//                .onBackpressureDrop()
// 处理不过来的时候,最多在缓存队列中放400个元素
//                .onBackpressureBuffer(400)
// 处理不过来就直接抛异常
                .onBackpressureError()
                .delayElements(Duration.ofMillis(100))
                .subscribe(
                        System.out::println,
                        ex ->{
                            System.out.println(ex);
                            latch.countDown();
                        },
                        () ->{
                            System.out.println("处理完毕");
                            latch.countDown();
                        }
                );
        latch.await();
        System.out.println("main结束");
    }
}

Flux.range(1,1000)
    .delayElements(Duration.ofMillis(10))
    .onBackpressureBuffer(400)
    // .onBackpressureError()
    .delayElements(Duration.ofMillis(100))
这部分是 Reactor 的核心数据流逻辑,逐行解释:
Flux.range(1,1000):创建一个发射 1 到 1000 这 1000 个整数的数据流(Flux 是 Reactor 中表示 0 到 N 个元素的异步序列)。
delayElements(Duration.ofMillis(10)):让每个元素发射后延迟 10 毫秒再向下游传递,模拟数据源产生数据的速度。
onBackpressureBuffer(400):核心背压策略:
背压(Backpressure)是响应式编程中 “下游处理速度跟不上上游生产速度” 时的流量控制机制。
这里设置缓冲区大小为 400:当下游处理慢时,上游的数据会先缓存到这个缓冲区,最多缓存 400 个,避免数据积压导致内存溢出。
注释掉的onBackpressureError()是另一种背压策略:如果下游处理不过来,直接抛出异常。
delayElements(Duration.ofMillis(100)):再次让每个元素延迟 100 毫秒,模拟下游处理数据的慢速度(这也是触发背压的关键)。

热数据流和冷数据流

  • 冷数据
public static void main(String[] args) {
        Flux<String> coldPublisher = Flux.defer(()->{
            System.out.println("生成数据");
            return Flux.just(UUID.randomUUID().toString());
        });
        System.out.println("尚未生成数据");
        coldPublisher.subscribe(e -> System.out.println("onNext: " + e));
        coldPublisher.subscribe(e -> System.out.println("onNext: " + e));
        System.out.println("为两次订阅生成两次数据");
    }
  • 热数据

    Flux<Integer> source = Flux.range(0, 3)
                .doOnSubscribe(s -> System.out.println("对冷发布者的新订阅票据: " + s));
        ConnectableFlux<Integer> conn = source.publish();
        conn.subscribe(item -> System.out.println("[subscriber 1] onNext:" + item));
        conn.subscribe(item -> System.out.println("[subscriber 2] onNext:" + item));
        System.out.println("所有订阅这都准备建立连接");
        conn.connect();
    

    output:
    所有订阅这都准备建立连接
    对冷发布者的新订阅票据: reactor.core.publisher.FluxRange$RangeSubscription@7a92922
    [subscriber 1] onNext:0
    [subscriber 2] onNext:0
    [subscriber 1] onNext:1
    [subscriber 2] onNext:1
    [subscriber 1] onNext:2
    [subscriber 2] onNext:2

  • 缓存流元素

   Flux<Integer> source = Flux.range(0, 3)
                .doOnSubscribe(s -> System.out.println("对冷发布者的新订阅票据: " + s));
        ConnectableFlux<Integer> conn = source.publish();
        conn.subscribe(item -> System.out.println("[subscriber 1] onNext:" + item));
        conn.subscribe(item -> System.out.println("[subscriber 2] onNext:" + item));
        System.out.println("所有定于这都准备建立连接");
        conn.connect();
  • 共享数据流

todo 现在还看不懂,以后有时间再看看

处理时间

组合和转换响应式流

spring webflux高级实战

响应式web内核

基于webflux的纯函数式web

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.3</version>
        <relativePath/>
    </parent>
    <groupId>com.msb</groupId>
    <artifactId>msb-vip-webFlux.4.1.3</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-security</artifactId>-->
<!--        </dependency>-->
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
// 课程中的的项目,在启动起来之后,使用postman进行访问的时候,会出现404的情况,此时将下面这个依赖注释掉就可以了
// 但是当自己第二遍运行这个项目的时候,发现和下面的这个依赖又没有关系了,自己也很纳闷
<!--        <dependency>-->
<!--            <groupId>org.springframework.security</groupId>-->
<!--            <artifactId>spring-security-crypto</artifactId>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


</project>

总之,配置文件一定要按照课程上讲解的来进行配置。

所有课程已学完

posted on 2026-01-18 09:42  ~码铃薯~  阅读(0)  评论(0)    收藏  举报

导航