引用: https://www.cnblogs.com/IcanFixIt/p/7245377.html
引用: https://juejin.im/post/5e59bc266fb9a07ca90c2ecb
什麼是流
流是由生產者生產並由一個或多個消費者消費的元素(item)的序列。
這種生產者-消費者模型也被稱爲source/sink模型
或發佈者-訂閱者(publisher-subscriber )模型
。
流的處理機制
有幾種流處理機制,其中pull模型和push模型是最常見的。
- 在
push模型
中,發佈者將元素推送
給訂閱者。 - 在
pull模式
中,訂閱者主動拉取發佈者生產的元素。
發佈者和訂閱者都以同樣的速率工作,這是一個理想的情況,這些模式非常有效。 但是,如果他們不按同樣的速率工作,這種情況下涉及的問題以及對應的解決辦法。
當發佈者比訂閱者快的時候
- 一種解決方案是:後者
必須有一個無邊界緩衝區來保存快速傳入的元素
,或者它必須丟棄它無法處理的元素
。 - 另一個解決方案是:使用一種稱爲背壓
(backpressure )
的策略。
背壓(back pressure)
如果生產者發出的信息比消費者能夠處理消息最大量還要多,消費者可能會被迫一直在抓消息,耗費越來越多的資源,埋下潛在的崩潰風險。爲了防止這一點,需要有一種機制使消費者可以通知
生產者,降低消息的生成速度。生產者可以採用多種策略來實現這一要求,這種機制稱爲背壓。
簡單來說:
- 背壓指的發佈者和訂閱者之間的互動
- 訂閱者可以告訴發佈者自己需要多少數據,可以調節數據流量,不會導致發佈者發佈數據過多導致數據浪費或壓垮訂閱者
響應式流
Reactive Stream 概念
Reactive Stream (響應式流/反應流) 是JDK9引入的一套標準,是一套基於發佈/訂閱模式的數據處理規範。響應式流從2013年開始,作爲提供非阻塞背壓的異步流處理標準的倡議。它旨在解決處理元素流的問題——如何將元素流從發佈者傳遞到訂閱者,而不需要發佈者阻塞
,或訂閱者有無限制的緩衝區
或丟棄
。
更確切地說,Reactive流目的是“找到最小的一組接口,方法和協議,用來描述必要的操作和實體以實現這樣的目標:以非阻塞背壓方式實現數據的異步流”。
反應式流 (Reactive Stream) 規範誕生
Subscription 接口定義了連接發佈者和訂閱者的方法
Publisher<T> 接口定義了發佈者的方法
Subscriber<T> 接口定義了訂閱者的方法
Processor<T,R> 接口定義了處理器
Reactive Stream 規範誕生後,RxJava 從 RxJava 2 開始實現 Reactive Stream 規範 , 同時 Spring提供的Reactor 框架(WebFlux的基礎) 等也相繼實現了 Reactive Stream 規範
訂閱者和發佈者之間的交互
-
發佈者(publisher)
是潛在無限數量的有序元素的生產者。 它根據收到的要求向當前訂閱者發佈(或發送)元素。 -
訂閱者(subscriber)
從發佈者那裏訂閱並接收元素。- 發佈者向訂閱者發送訂閱令牌
(subscription token
)。 - 訂閱者使用訂閱令牌從發佈者哪裏請求多個元素。
- 當元素準備就緒時,發佈者向訂閱者發送多個或更少的元素。
- 訂閱者使用訂閱令牌來
調整
請求數據的個數
- 發佈者向訂閱者發送訂閱令牌
-
訂閱(subscription)
表示訂閱者訂閱的一個發佈者的令牌。 當訂閱請求成功時,發佈者將其傳遞給訂閱者。 訂閱者使用訂閱令牌與發佈者進行交互,例如請求更多的元素(request)或取消訂閱(cancel)。
-處理者(processor)
充當訂閱者和發佈者的處理階段。 Processor接口繼承了Publisher和Subscriber接口。 它用於轉換發佈-訂閱者管道中的元素。 Processor<T,R>訂閱類型T的數據元素,接收並轉換爲類型R的數據
,併發布變換後的數據。 下圖顯示了處理者在發佈者——訂閱和管道中作爲轉換器的作用。 可以擁有多個處理者。
JDK9 Flow API
JDK9中Reactive Stream的實現規範 通常被稱爲 Flow API
,通過java.util.concurrent.Flow
來實現響應式流.
APIs
//package java.util.concurrent;
public final class Flow {
@FunctionalInterface
public static interface Publisher<T> {
public void subscribe(Subscriber<? super T> subscriber);
}
public static interface Subscriber<T> {
//發佈者subscribe訂閱者時觸發, 發送subscription
public void onSubscribe(Subscription subscription);
//訂閱者接收消息處理邏輯
public void onNext(T item);
public void onError(Throwable throwable);
//發佈者 close時觸發
public void onComplete();
}
public static interface Subscription {
//訂閱者向發佈者請求數據
public void request(long n);
public void cancel();
}
//轉換器 分別繼承了 Subscriber和Publisher接口
public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> {
}
}
demo1
private static void test01() throws InterruptedException {
Random random = new Random();
//1.自定義發佈者, 發佈數據類型爲Integer
//使用jdk9自帶的SubmissionPublisher,它實現了Publisher接口
SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();
//2.定義訂閱者
Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<Integer>() {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription sb) {
//建立訂閱關係
this.subscription = sb;
//2. 請求數據 ----
this.subscription.request(1);
}
@Override
public void onNext(Integer item) {
System.out.println("接收到數據:" + item);
this.subscription.request(2);
//已經達到了目標,調用cancel告訴發佈者,不在接收數據
// this.subscription.cancel();
}
@Override
public void onError(Throwable throwable) {
//出現異常
throwable.printStackTrace();
this.subscription.cancel();
}
@Override
public void onComplete() {
//在publisher.close();時觸發..
System.out.println("數據處理完了");
}
};
// 3.發佈者和訂閱者 建立訂閱關係
publisher.subscribe(subscriber);
//4.生產數據,併發布
publisher.submit(100);
publisher.submit(200);
publisher.submit(300);
publisher.submit(400);
//5.結束後,關閉發佈者
publisher.close();
Thread.currentThread().join(1000);
}
demo2–Processor
Proceeor主要的作用是用具加工、處理髮布者發佈的消息。
下例:用來展示Processor過濾掉髮布者生產的小於200的消息,並且對消息進行轉換Integer --> String
.
MyProcessor
static class MyProcessor extends SubmissionPublisher<String> implements Flow.Processor<Integer, String> {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription sb) {
//建立訂閱關係
this.subscription = sb;
//2. 請求數據 ----
this.subscription.request(1);
}
@Override
public void onNext(Integer item) {
System.out.println("processor接收到數據:" + item);
//過濾掉小於200的數據,
if(item > 200) {
//將消息轉換String
this.submit("String MSG-" + item);
}
this.subscription.request(1);
}
@Override
public void onError(Throwable throwable) {
//出現異常
throwable.printStackTrace();
this.subscription.cancel();
}
@Override
public void onComplete() {
System.out.println("Processor:數據處理完了");
}
}
test-case
private static void testProcessor() throws InterruptedException {
Random random = new Random();
//1.自定義發佈者, 發佈數據類型爲Integer
SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();
// 2.定義處理器Processor
MyProcessor processor = new MyProcessor();
// 3.發佈者和Processor建立關係
publisher.subscribe(processor);
//4.定義訂閱者
Flow.Subscriber<String> subscriber = new Flow.Subscriber<String>() {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription sb) {
//建立訂閱關係
this.subscription = sb;
//2. 請求數據 ----
this.subscription.request(1);
}
@Override
public void onNext(String item) {
System.out.println("=============subscriber接收到數據:" + item);
this.subscription.request(2);
//已經達到了目標,調用cancel告訴發佈者,不在接收數據
// this.subscription.cancel();
}
@Override
public void onError(Throwable throwable) {
//出現異常
throwable.printStackTrace();
this.subscription.cancel();
}
@Override
public void onComplete() {
//在publisher.close();時觸發..
System.out.println("數據處理完了");
}
};
//5. Processor同訂閱者建立關係
processor.subscribe(subscriber);
//4.生產數據,併發布
publisher.submit(100);
publisher.submit(200);
publisher.submit(300);
publisher.submit(400);
//5.結束後,關閉發佈者
publisher.close();
Thread.currentThread().join(1000);
}
執行結果:
processor接收到數據:100
processor接收到數據:200
processor接收到數據:300
processor接收到數據:400
Processor數據處理完了
=============subscriber接收到數據:String MSG-300
=============subscriber接收到數據:String MSG-400