flux 中 skip 的使用和原理

前言

本文主要简单分析一下 Flux 中 skip() 方法的基本使用和实现的原理

一、skipFirst

跳过发布的前N个元素

1、示例

生成0到4的序列,并跳过前2个元素

	@Test
    public void skipFirst() throws InterruptedException {
        Flux.range(0, 5).skip(2).subscribe(System.out::println);
    }

结果:

2
3
4

2、原理

下面分析一下实现的原理

(1)Flux.range(0, 5)

public static Flux<Integer> range(int start, int count) {
		if (count == 1) {
			return just(start);
		}
		if (count == 0) {
			return empty();
		}
		//创建 FluxRange
		return onAssembly(new FluxRange(start, count));
	}

(2)skip(2)

public final Flux<T> skip(long skipped) {
		if (skipped == 0L) {
			return this;
		}
		else {
			//创建 FluxSkip
			return onAssembly(new FluxSkip<>(this, skipped));
		}
	}

(3)subscribe(System.out::println)

public final Disposable subscribe(Consumer<? super T> consumer) {
		Objects.requireNonNull(consumer, "consumer");
		return subscribe(consumer, null, null);
	}
	
public final Disposable subscribe(
			@Nullable Consumer<? super T> consumer,
			@Nullable Consumer<? super Throwable> errorConsumer,
			@Nullable Runnable completeConsumer) {
		return subscribe(consumer, errorConsumer, completeConsumer, (Context) null);
	}

public final Disposable subscribe(
			@Nullable Consumer<? super T> consumer,
			@Nullable Consumer<? super Throwable> errorConsumer,
			@Nullable Runnable completeConsumer,
			@Nullable Context initialContext) {
			//创建 LambdaSubscriber
		return subscribeWith(new LambdaSubscriber<>(consumer, errorConsumer,
				completeConsumer,
				null,
				initialContext));
	}

(4)subscribeWith()

public final <E extends Subscriber<? super T>> E subscribeWith(E subscriber) {
		subscribe(subscriber);
		return subscriber;
	}

(4)subscribe()

构建发布者订阅者的执行链

发布者 : FluxRange

订阅者:SkipSubscriber -> LambdaSubscriber -> consumer

public final void subscribe(Subscriber<? super T> actual) {
		CorePublisher publisher = Operators.onLastAssembly(this);
		CoreSubscriber subscriber = Operators.toCoreSubscriber(actual);

		try {
			if (publisher instanceof OptimizableOperator) {
				OptimizableOperator operator = (OptimizableOperator) publisher;
				//构建执行链
				while (true) {
					subscriber = operator.subscribeOrReturn(subscriber);
					if (subscriber == null) {
						// null means "I will subscribe myself", returning...
						return;
					}
					OptimizableOperator newSource = operator.nextOptimizableSource();
					if (newSource == null) {
						publisher = operator.source();
						break;
					}
					operator = newSource;
				}
			}
			
			//发布者调用subscribe
			publisher.subscribe(subscriber);
		}
		catch (Throwable e) {
			Operators.reportThrowInSubscribe(subscriber, e);
			return;
		}
	}

(5)publisher.subscribe(subscriber)

FluxRange.java

public void subscribe(CoreSubscriber<? super Integer> actual) {
		long st = start;
		long en = end;
		if (st == en) {
			Operators.complete(actual);
			return;
		} else
		if (st + 1 == en) {
			actual.onSubscribe(Operators.scalarSubscription(actual, (int)st));
			return;
		}
		
		if (actual instanceof ConditionalSubscriber) {
			actual.onSubscribe(new RangeSubscriptionConditional((ConditionalSubscriber<? super Integer>) actual, st, en));
			return;
		}
		//调用订阅者的onSubscribe方法建立订阅关系
		actual.onSubscribe(new RangeSubscription(actual, st, en));
	}

(6)onSubscribe()

建立订阅关系

SkipSubscriber.java

	public void onSubscribe(Subscription s) {
			if (Operators.validate(this.s, s)) {
				this.s = s;
				long n = remaining;
				actual.onSubscribe(this);
				s.request(n);
			}
		}
LambdaSubscriber.java

	@Override
	public final void onSubscribe(Subscription s) {
		if (Operators.validate(subscription, s)) {
			this.subscription = s;
			if (subscriptionConsumer != null) {
				try {
					subscriptionConsumer.accept(s);
				}
				catch (Throwable t) {
					Exceptions.throwIfFatal(t);
					s.cancel();
					onError(t);
				}
			}
			else {
				//通过关系请求数据
				s.request(Long.MAX_VALUE);
			}
		}
	}

(7)request()

SkipSubscriber.java

		@Override
		public void request(long n) {
			s.request(n);
		}
RangeSubscription.java

		@Override
		public void request(long n) {
			if (Operators.validate(n)) {
				if (Operators.addCap(REQUESTED, this, n) == 0) {
					if (n == Long.MAX_VALUE) {
						fastPath();
					} else {
						slowPath(n);
					}
				}
			}
		}
void fastPath() {
			final long e = end;
			final Subscriber<? super Integer> a = actual;

			for (long i = index; i != e; i++) {
				if (cancelled) {
					return;
				}
				
				//调用 onNext 发布数据
				a.onNext((int) i);
			}

			if (cancelled) {
				return;
			}

			a.onComplete();
		}

(8)onNext()

		@Override
		public void onNext(T t) {
			//需要跳过的数量
			long r = remaining;
			if (r == 0L) {
				//需要跳过的元素为0了,则往下一个订阅者传递数据
				actual.onNext(t);
			}
			else {
				//跳过元素时执行回调
				Operators.onDiscard(t, ctx);
				//跳过元素减1
				remaining = r - 1;
			}
		}

二、skipTime

跳过一段时间内发布的元素

1、示例

从无限流中获取元素,跳过前 1s 内发布的元素

	@Test
    public void skipTime() {
        Flux.fromStream(Stream.iterate(0, a -> a + 1))
                .skip(Duration.ofSeconds(1))
                .subscribe(v -> {
                    System.out.println(v);
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
    }

结果:

在这里插入图片描述

2、原理

(1) Flux.fromStream(Stream.iterate(0, a -> a + 1))

public static <T> Flux<T> fromStream(Stream<? extends T> s) {
		Objects.requireNonNull(s, "Stream s must be provided");
		//创建 FluxStream
		return onAssembly(new FluxStream<>(() -> s));
	}

(2).skip(Duration.ofSeconds(1))

public final Flux<T> skip(Duration timespan) {
		return skip(timespan, Schedulers.parallel());
	}
public final Flux<T> skip(Duration timespan, Scheduler timer) {
		if(!timespan.isZero()) {
			//创建 delay、skipUntilOther
			return skipUntilOther(Mono.delay(timespan, timer));
		}
		else{
			return this;
		}
	}

(3)Mono.delay

public static Mono<Long> delay(Duration duration, Scheduler timer) {
		//创建 MonoDelay
		return onAssembly(new MonoDelay(duration.toNanos(), TimeUnit.NANOSECONDS, timer));
	}

(4)skipUntilOther()

public final Flux<T> skipUntilOther(Publisher<?> other) {
		//创建 FluxSkipUntilOther
		return onAssembly(new FluxSkipUntilOther<>(this, other));
	}

(5)subscribe()

跟上面例子一样,最终会进入到 Flux 的 subscribe() 建立发布订阅执行链

public final void subscribe(Subscriber<? super T> actual) {
		CorePublisher publisher = Operators.onLastAssembly(this);
		CoreSubscriber subscriber = Operators.toCoreSubscriber(actual);

		try {
			if (publisher instanceof OptimizableOperator) {
				OptimizableOperator operator = (OptimizableOperator) publisher;
				while (true) {
					subscriber = operator.subscribeOrReturn(subscriber);
					if (subscriber == null) {
						// null means "I will subscribe myself", returning...
						return;
					}
					OptimizableOperator newSource = operator.nextOptimizableSource();
					if (newSource == null) {
						publisher = operator.source();
						break;
					}
					operator = newSource;
				}
			}

			publisher.subscribe(subscriber);
		}
		catch (Throwable e) {
			Operators.reportThrowInSubscribe(subscriber, e);
			return;
		}
	}

(6)FluxSkipUntilOther

下面看一下 FluxSkipUntilOther 的 subscribeOrReturn() 方法

	@Override
	public CoreSubscriber<? super T> subscribeOrReturn(CoreSubscriber<? super T> actual) {
		//创建 SkipUntilMainSubscriber
		SkipUntilMainSubscriber<T> mainSubscriber = new SkipUntilMainSubscriber<>(actual);
		//创建 SkipUntilOtherSubscriber
		SkipUntilOtherSubscriber<U> otherSubscriber = new SkipUntilOtherSubscriber<>(mainSubscriber);
		
		//这里的other是刚才创建的 MonoDelay
		other.subscribe(otherSubscriber);

		return mainSubscriber;
	}

在构建外层发布订阅执行链时,在构建过程中进入 FluxSkipUntilOther 的 subscribeOrReturn() 方法,在这里又构建了 skip() 后续的发布订阅执行链,这里是MonoDelay 的执行链,首先会被执行 ,下面分析 MonoDelay 的执行链

(7)MonoDelay.subscribe()

	@Override
	public void subscribe(CoreSubscriber<? super Long> actual) {
		boolean failOnBackpressure = actual.currentContext().getOrDefault(CONTEXT_OPT_OUT_NOBACKPRESSURE, false) == Boolean.TRUE;
		
		//创建 MonoDelayRunnable 
		MonoDelayRunnable r = new MonoDelayRunnable(actual, failOnBackpressure);
		
		//执行skip() 后续的发布订阅
		actual.onSubscribe(r);

		try {
			//延时调度执行 MonoDelayRunnable 
			r.setCancel(timedScheduler.schedule(r, delay, unit));
		}
		catch (RejectedExecutionException ree) {
			if(!MonoDelayRunnable.wasCancelled(r.state)) {
				actual.onError(Operators.onRejectedExecution(ree, r, null, null,
						actual.currentContext()));
			}
		}
	}

(8)MonoDelayRunnable

		@Override
		public void run() {
			int previousState = markDelayDone(this);
			//检验中台
			if (wasCancelled(previousState) || wasDelayDone(previousState)) {
				return;
			}
			//请求数据的状态
			if (wasRequested(previousState)) {
				//传播延时
				propagateDelay();
			}
			else if (failOnBackpressure) {
				actual.onError(Exceptions.failWithOverflow("Could not emit value due to lack of requests"));
			}
		}

(9)propagateDelay()

	private void propagateDelay() {
			//校验状态
			int previousState = markPropagated(this);
			if (wasCancelled(previousState)) {
				return;
			}
			try {
				//打开传递数据的开关
				actual.onNext(0L);
				actual.onComplete();
			}
			catch (Throwable t){
				actual.onError(Operators.onOperatorError(t, actual.currentContext()));
			}
		}
		@Override
		public void onNext(U t) {
			if (main.gate) {
				return;
			}
			SkipUntilMainSubscriber<?> m = main;
			m.other.cancel();
			//将 gate 设置为 true,即将请求数据的闸门打开
			m.gate = true;
			m.other = Operators.cancelledSubscription();
		}

然后接着执行外层的发布订阅执行链,详细过程就省略了,下面看一下 SkipUntilMainSubscriber

(10)SkipUntilMainSubscriber

在元素传递的过程中,会进入到 SkipUntilMainSubscriber 中的 onNext() 方法

		@Override
		public void onNext(T t) {
			//如果阀门打开了,则将元素传递给下一个订阅者
			if (gate) {
				actual.onNext(t);
			}
			else {
				//阀门没有打开
				//丢弃元素回调
				Operators.onDiscard(t, ctx);
				//请求下一个元素
				main.request(1);
			}
		}

三、skipLast

跳过发布的后N个元素

1、示例

创建0到4的序列,跳过后2个元素

 	@Test
    public void skipLast() {
        Flux.range(0, 5).skipLast(2).subscribe(System.out::println);
    }

2、原理

具体的逻辑处理是在 FluxSkipLast 中
(1)subscribeOrReturn()

创建了 SkipLastSubscriber

	@Override
	public CoreSubscriber<? super T> subscribeOrReturn(CoreSubscriber<? super T> actual) {
		return new SkipLastSubscriber<>(actual, n);
	}

(2)SkipLastSubscriber

按照示例,当第三个元素到达onNext() 方法时,会处理第一个元素;

最终最后两个元素不会被处理

		@Override
		public void onNext(T t) {
			//判断队列的数量跟跳过数量是否相等
			if (size() == n) {
				//相等了,则从队列中取出第一个元素,传递给下一个订阅者
				actual.onNext(pollFirst());
			}
			//不相等时,将元素添加到队列中
			offerLast(t);

		}

四、skipUntil

跳过直到条件成立

1、示例

 	@Test
    public void skipUntil() {
        Flux.range(0, 5).skipUntil(i -> i > 2).subscribe(System.out::println);
    }

2、原理

(1)FluxSkipUntil

创建了 SkipUntilSubscriber

	@Override
	public CoreSubscriber<? super T> subscribeOrReturn(CoreSubscriber<? super T> actual) {
		return new SkipUntilSubscriber<>(actual, predicate);
	}

(2)SkipUntilSubscriber

		@Override
		public boolean tryOnNext(T t) {
			//出现异常或已经完成状态,丢弃元素
			if (done) {
				Operators.onNextDropped(t, ctx);
				return true;
			}
			
			//跳过元素完成了,将元素传递给订阅者
			if (doneSkipping) {
				actual.onNext(t);
				return true;
			}
			boolean b;
			
			//校验跳过元素条件
			try {
				b = predicate.test(t);
			}
			catch (Throwable e) {
				onError(Operators.onOperatorError(s, e, t, ctx));

				return true;
			}

			if (b) {
				//条件成立,将 doneSkipping 设置为true
				doneSkipping = true;
				//消费元素
				actual.onNext(t);
				return true;
			}
			
			//条件不成立,丢弃元素
			Operators.onDiscard(t, ctx);
			return false;
		}

五、skipUntilOther

跳过直到另一发布者执行完毕

如下示例,这里的执行流程跟上面的 skipTime 是一样的,原理就是 other 执行完毕后会打开阀门,此时发布者发布的元素才会传递给订阅者

 	 @Test
    public void skipUntilOther() {
        Flux.fromStream(Stream.iterate(0, a -> a + 1))
                .skipUntilOther(Mono.delay(Duration.ofSeconds(1), Schedulers.parallel()))
                .subscribe(v -> {
                    System.out.println(v);
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值