Reactor响应式编程系列(七)- 探索Processor

Reactor响应式编程系列导航

一. Processor简介

Processor既是一个Publisher也是一个Subscriber,是一个接口,其中的两个实现类:

  • MonoProcessor
  • FluxProcessor

FluxProcessor又可以衍生出多种Processor,负责应对不同的场景使用(我就只罗列几个比较常见的):

  • UnicastProcessor
  • DirectProcessor
  • EmitterProcessor

接下来就开始进行一一的介绍:

1.1 UnicastProcessor

案例1:

@Test
public void unicastTest() {
   UnicastProcessor<Object> processor = UnicastProcessor.create();
   FluxSink<Object> sink = processor.sink();
   sink.next("aaa");
   sink.next("aaa");
   sink.next("aaa");
   sink.next("aaa");
   Flux.just("Hello", "World").subscribe(processor);
   processor.subscribe(System.out::println);
   // 若打开以下注释,则报错,因为UnicastProcessor只允许有一个订阅者
   // processor.subscribe(System.out::println);
   sink.next("aaa");
}

运行结果如下:
在这里插入图片描述
若打开注释:
在这里插入图片描述

UnicastProcessor可以在多个线程中生产元素,即多个生产源同时生产、下发元素。为了保证元素在多线程的情况下有序的下发UnicastProcessor利用SerializedSink进行控制。

由于UnicastProcessor继承了FluxProcessor,因此我们先来看下父类的sink()方法:

@Deprecated
public final FluxSink<IN> sink() {
	return sink(FluxSink.OverflowStrategy.IGNORE);
}
↓↓↓↓↓
@Deprecated
public final FluxSink<IN> sink(FluxSink.OverflowStrategy strategy) {
	Objects.requireNonNull(strategy, "strategy");
	if (getBufferSize() == Integer.MAX_VALUE){
		strategy = FluxSink.OverflowStrategy.IGNORE;
	}

	FluxCreate.BaseSink<IN> s = FluxCreate.createSink(this, strategy);
	onSubscribe(s);

	if(s.isCancelled() ||
			(isSerialized() && getBufferSize() == Integer.MAX_VALUE)){
		return s;
	}
	// 默认情况下,永远返回True,这样就能得到FluxCreate.SerializedFluxSink
	if (serializeAlways())
		return new FluxCreate.SerializedFluxSink<>(s);
	else
		return new FluxCreate.SerializeOnRequestSink<>(s);
}
↓↓↓↓↓ serializeAlways() ↓↓↓↓↓
// 对于UnicastProcessor类来说,它并没有重写下面的方法,也就是沿用父类的方法
protected boolean serializeAlways() {
	return true;
}

UnicastProcessor可以作为订阅者和上游的源产生订阅关系,但是这里有个问题:

如果UnicastProcessor与多个分布在不同线程中的上游源产生了订阅关系,那么当有一个生产者元素下发完毕时,其他生产者是否继续下发元素?

来看下UnicastProcessor类中的几个重要方法:create(),我们可以发现可以通过自定义的队列来控制背压程度

@Deprecated
public final class UnicastProcessor<T> extends FluxProcessor<T, T>
		implements Fuseable.QueueSubscription<T>, Fuseable, InnerOperator<T, T>,
		           InternalManySink<T> {
	@Deprecated
	public static <E> UnicastProcessor<E> create() {
		return new UnicastProcessor<>(Queues.<E>unbounded().get());
	}
}

subscribe()方法中可以发现:

static final AtomicIntegerFieldUpdater<UnicastProcessor> ONCE =
			AtomicIntegerFieldUpdater.newUpdater(UnicastProcessor.class, "once");
@Override
public void subscribe(CoreSubscriber<? super T> actual) {
	Objects.requireNonNull(actual, "subscribe");
	// 1.通过原子类操作来控制订阅者的个数
	if (once == 0 && ONCE.compareAndSet(this, 0, 1)) {
		this.hasDownstream = true;
		actual.onSubscribe(this);
		this.actual = actual;
		if (cancelled) {
			this.hasDownstream = false;
		} else {
			drain(null);
		}
	}
	// 2.当产生第二个订阅关系的时候,会进入else语句 
	else {
		// 3.下发一个错误事件
		Operators.error(actual, new IllegalStateException("UnicastProcessor " +
				"allows only a single Subscriber"));
	}
}

onNext()方法中可以发现:

// onNext()调用了这个方法,该方法发射的元素流是非空的
@Override
// 代码A
public void emitNext(T value, Sinks.EmitFailureHandler failureHandler) {
	if (onOverflow == null) {
		// 查看代码C
		InternalManySink.super.emitNext(value, failureHandler);
		return;
	}

	InternalManySink.super.emitNext(
			value, (signalType, emission) -> {
				// 会根据provider的类型来决定返回值
				// 若Provider是Sinks.EmitFailureHandler,则返回true,即需要重试
				boolean shouldRetry = failureHandler.onEmitFailure(SignalType.ON_NEXT, emission);
				if (!shouldRetry) {
					// ...
				}
				return shouldRetry;
			}
	);
}
// 代码B
@Override
public EmitResult tryEmitNext(T t) {
	// ☆
	if (done) {
		return EmitResult.FAIL_TERMINATED;
	}
	if (cancelled) {
		return EmitResult.FAIL_CANCELLED;
	}
	// 可见这里将元素存储到了队列中
	if (!queue.offer(t)) {
		return (once > 0) ? EmitResult.FAIL_OVERFLOW : EmitResult.FAIL_ZERO_SUBSCRIBER;
	}
	drain(t);
	return EmitResult.OK;
}
// 代码C
interface InternalManySink<T> extends Sinks.Many<T>, ContextHolder {

	@Override
	// value是要发射出的元素,不能为空;failureHandler是一个失败策略,允许失败重试
	default void emitNext(T value, Sinks.EmitFailureHandler failureHandler) {
		for (;;) {
			// 这里是将元素存储于Queue中,若元素下发结束,则看后面的结果处理switch
			Sinks.EmitResult emitResult = tryEmitNext(value);
			if (emitResult.isSuccess()) {
				return;
			}

			boolean shouldRetry = failureHandler.onEmitFailure(SignalType.ON_NEXT,
					emitResult);
			// 若允许失败重试,那么进入循环,直到做出下面的结果处理
			if (shouldRetry) {
				continue;
			}

			switch (emitResult) {
				// 不作处理,此时没有订阅服务器可以从中获取上下文
				case FAIL_ZERO_SUBSCRIBER:
					return;
				// 放弃该值,并进行错误事件的传播
				case FAIL_OVERFLOW:
					Operators.onDiscard(value, currentContext());
					emitError(Exceptions.failWithOverflow("Backpressure overflow during Sinks.Many#emitNext"),failureHandler);
					return;
				// 放弃该值
				case FAIL_CANCELLED:
					Operators.onDiscard(value, currentContext());
					return;
				// 一般元素下发结束后,会执行扔掉这个值
				case FAIL_TERMINATED:
					Operators.onNextDropped(value, currentContext());
					return;
				// 抛异常
				case FAIL_NON_SERIALIZED:
					throw new EmissionException(emitResult,
							"Spec. Rule 1.3 - onSubscribe, onNext, onError and onComplete signaled to a Subscriber MUST be signaled serially."
					);
				default:
					throw new EmissionException(emitResult, "Unknown emitResult value");
			}
		}
	}
}

在订阅了上游源的情况下,会在上游元素下发完毕的时候调用onComplete()方法,此时会给done属性赋值true。那么此时若UnicastProcessor作为订阅者和上游不同线程的多个生产者之间存在订阅关系,回到onNext()方法中的代码B(注释中有☆),可以看到if(done) {return EmitResult.FAIL_TERMINATED;},即其他生产者的元素会被直接抛弃。 那么上述的问题也就随之解决了。

// onComplete()最后调用了这个方法
@Override
public EmitResult tryEmitComplete() {
	if (done) {
		return EmitResult.FAIL_TERMINATED;
	}
	if (cancelled) {
		return EmitResult.FAIL_CANCELLED;
	}

	done = true;

	doTerminate();

	drain(null);
	return Sinks.EmitResult.OK;
}

总结1:

  • 只要有一个生产者元素下发完毕,其他的生产者元素都会被抛弃。
  • UnicastProcessor作为发布者,可以在多个线程中同时发布元素,但是只能和一个订阅者建立订阅关系。
  • 支持背压。

1.2 DirectProcessor

DirectProcessor的特点如下:

  • DirectProcessor可以接收n个Subscriber
  • DirectProcessor不支持背压,其内部也没有用于存储元素的数据结构。
  • DirectProcessor作为生产者存在的时候,只要对应的订阅者中有一个元素的请求数量为0,就会移除该订阅者,并同时发送一个错误事件。

来看下它相关的onNext()源码:

// DirectProcessor.DirectInner.tryEmitNext
boolean tryEmitNext(T value) {
	if (requested != 0L) {
		if (isCancelled()) {
			return false;
		}
		actual.onNext(value);
		if (requested != Long.MAX_VALUE) {
			REQUESTED.decrementAndGet(this);
		}
		return true;
	}
	// 这里可以侧面看出来,只要订阅者的元素请求数量为0,那么就直接返回,在后面会进行移除操作
	return false;
}

DirectProcessor作为订阅者,并订阅了一个发布源的时候,发布源下发元素结束时会调用DirectProcessoronComplete()方法:

private static final AtomicReferenceFieldUpdater<DirectProcessor, DirectInner[]>SUBSCRIBERS =
			AtomicReferenceFieldUpdater.newUpdater(DirectProcessor.class, DirectInner[].class, "subscribers");
			
@Override
public void onComplete() {
	@SuppressWarnings("unused") Sinks.EmitResult emitResult = tryEmitComplete();
}

private EmitResult tryEmitComplete() {
	// 1.get之后会执行set方法,将TERMINATED赋值给subscribers
	@SuppressWarnings("unchecked")
	DirectInner<T>[] inners = SUBSCRIBERS.getAndSet(this, SinkManyBestEffort.TERMINATED);
	// 2.而上游源有元素下发的时候,会判断subscribers是否等于TERMINATED
	// 若是,则后续接收的元素都会被抛弃掉
	if (inners == SinkManyBestEffort.TERMINATED) {
		return EmitResult.FAIL_TERMINATED;
	}

	for (DirectInner<?> s : inners) {
		s.emitComplete();
	}
	return EmitResult.OK;
}

DirectProcessor作为发布者的时候,当有订阅者订阅时,会先将订阅者包装为DirectInner对象,来看下它的subscribe()方法:

@Override
public void subscribe(CoreSubscriber<? super T> actual) {
	Objects.requireNonNull(actual, "subscribe");
	// 1.先进行兑现规定包装
	DirectInner<T> p = new DirectInner<>(actual, this);
	actual.onSubscribe(p);
	// 2.调用add方法,若已经有订阅者订阅,并且元素下发完毕,即调用了onComplete方法
	// 根据上文说法,会将subscribe赋值为 SinkManyBestEffort.TERMINATED
	if (add(p)) {
		if (p.isCancelled()) {
			remove(p);
		}
	}
	// 4.此时该源对于某一个订阅者而言,元素下发完毕,那么过程没报错,则调用actual.onComplete()
	// 否则actual.onError(e);
	else {
		Throwable e = error;
		if (e != null) {
			actual.onError(e);
		}
		else {
			actual.onComplete();
		}
	}
}
@Override
public boolean add(DirectInner<T> s) {
	DirectInner<T>[] a = subscribers;
	// 3.那么此时会返回false
	if (a == SinkManyBestEffort.TERMINATED) {
		return false;
	}
	// ...
}

案例2:

@Test
public void directTest() {
    DirectProcessor<Object> directProcessor = DirectProcessor.create();
    FluxSink<Object> sink = directProcessor.sink();
    // 1. 这里的元素是不会输出的,因为此时还并没有订阅者,
    sink.next("000");
    // 2.订阅后,即可输出发布源发出的元素
    directProcessor.subscribe(System.out::println);
    sink.next("111");
    directProcessor.subscribe(System.out::println);
    sink.next("222");
    // 上游的源一旦发布元素结束,即调用对应的onComplete方法,将对应的subscribe改为TERMlNATED 
    // 此时后面的元素都会被抛弃,因此333,444都不会被输出
    Flux.just("Hello", "DockerX").subscribe(directProcessor);
    sink.next("333");
    sink.next("444");
    directProcessor.subscribe(System.out::println);
    sink.next("555");
}

输出结果如下:
在这里插入图片描述

总结2:

  1. DirectProcessor可以和多个订阅者建立订阅关系。
  2. 只要有一个数据源的元素下发结束,其他数据源的元素就会被抛弃。
  3. 不支持背压,也无数据存储。

1.3 EmitterProcessor

EmitterProcessor的特点如下:

  • EmitterProcessor可以有多个订阅者,并提供背压支持。
  • EmitterProcessor可以作为订阅者订阅上游,同步向下发送元素给自己的订阅者。

EmitterProcessor有一个很特别的点就是:在没有订阅者的时候,它依然可以接受上游源下发的元素。(从案例2我们可以发现,还没有订阅之前,下发的元素是不会输出的)

我们知道,当上游的元素下发完毕之前都没有任何的订阅者进行订阅,那么下发结束后,自然而然的会调用onComplete()方法,也因此会出现案例2这样的情况,但是对于EmitterProcessor而言,它能够将元素缓存在自定义的队列中

同时,EmitterProcessor还有一个本地变量autoCancel,其目的是:在EmitterProcessor有关订阅者解除订阅,并且订阅关系数为0的时候,自动清除内部的缓存队列,并停止接收新的订阅关系。

来看下相关的源码:

// EmitterProcessor.EmitterInner
@Override
void removeAndDrainParent() {
	parent.remove(this);
	parent.drain();
}
↓↓↓remove()↓↓↓
final void remove(FluxPublish.PubSubInner<T> inner) {
	for (; ; ) {
		// ...
		if (SUBSCRIBERS.compareAndSet(this, a, b)) {
			// 清除队列中的缓存
			if (autoCancel && b == EMPTY && Operators.terminate(S, this)) {
				// ...
				Queue<T> q = queue;
				if (q != null) {
					q.clear();
				}
			}
		}
		return;
	}
}

案例3:

@Test
public void emitterTest() {
    EmitterProcessor<Object> emitterProcessor = EmitterProcessor.create();
    FluxSink<Object> sink = emitterProcessor.sink();
    // 1.此时没有订阅者,但是会将这个元素存储到队列中
    sink.next("000");
    // 2.此时有了订阅者,会将队列中的元素获取到,也因此000可以获取,但是如果用DirectProcessor就不能消费。
    // 同时保证该元素不会被重复消费,只有第一个订阅者能够取到
    Disposable s1 = emitterProcessor.subscribe(System.out::println);
    sink.next("xxx");// 发布了元素xxx,此时订阅者s1已经产生订阅关系,可以输出元素,1条数据
    Disposable s2 = emitterProcessor.subscribe(System.out::println);
    sink.next("aaa");// 发布了元素aaa,此时订阅者s1和s2已经产生订阅关系,可以输出元素,因此有2条数据
    Flux.just("Hello", "DockerX").subscribe(emitterProcessor);// 这里会提示,一个订阅者不能同时订阅两个生产源
    sink.next("bbb");// 发布了元素bbb,此时订阅者s1和s2已经产生订阅关系,可以输出元素,因此有2条数据
    Disposable s3 = emitterProcessor.subscribe(System.out::println);
    sink.next("ccc");// 发布了元素ccc,此时订阅者s1、s2和s3已经产生订阅关系,可以输出元素,因此有3条数据
    s1.dispose();// 关闭
    s2.dispose();// 关闭
    s3.dispose();// 关闭
    // 订阅关系已经取消,因此下面的ddd不能够获取到
    sink.next("ddd");
}

结果如下:
在这里插入图片描述
对于上面注释中提到的报错信息,我们来看下其源码。EmitterProcessorsink()方法用的是父类FluxProcessor的实现,调用过程中会调用到onSubscribe()方法,其有自己的实现:

@Override
public void onSubscribe(final Subscription s) {
	// setOnce的意思是,只能设置一次,不能设置第二次
	// 因此案例3中,我们订阅第二次的时候,就会报错,只不过我的代码不知道为什么打印不出日志,看不到
	// 那么为什么案例3中,调用了Flux.just("Hello", "DockerX").subscribe(emitterProcessor)就算二次调用了?
	// 因为代码的最开始emitterProcessor.sink();这行代码,内部调用了onSubscribe方法。
	// 所以这里就报错了,当然代码还是能正常跑的
	if (Operators.setOnce(S, this, s)) {
		// ...
		queue = Queues.<T>get(prefetch).get();
		s.request(Operators.unboundedOrPrefetch(prefetch));
	}
}

public static <F> boolean setOnce(AtomicReferenceFieldUpdater<F, Subscription> field, F instance, Subscription s) {
	// ...
	reportSubscriptionSet();
	return false;
}
// Operators
public static void reportSubscriptionSet() {
	if (log.isDebugEnabled()) {
		// 报错时,会提示如下的信息
		log.debug("Duplicate Subscription has been detected",
				Exceptions.duplicateOnSubscribeException());
	}
}

总结3:

  • EmitterProcessor作为源可以有多个订阅者,并且在没有订阅的情况下,可以存储上游的元素到队列中,并且不会被重复消费(被第一个订阅者给获取到),提供背压。
  • EmitterProcessor在订阅关系数为0的时候,会自动清理队列中的缓存。
  • EmitterProcessor作为订阅者不能同时订阅两个数据源。

最后,文章到这里也就完了,后面可能会补充ReplayProcessor这个类,因为这个类有点复杂,所以我没打算在本篇文章叙述(书中对这个类的分析也有几十页,我太懒啦~) 下一篇准备回头看下前几篇文章一直有穿插的一个重要类:QueueSubscription,看看它到底有着什么样的作用~

感谢大家的观看~○( ^皿^)っHiahiahia…

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zong_0915

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

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

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

打赏作者

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

抵扣说明:

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

余额充值