序
本文主要研究下java裡頭的reactive streams與觀察者模式。
reactive streams
reactive程式設計正規化是一個非同步程式設計正規化,主要涉及資料流及變化的傳播,可以看做是觀察者設計模式的擴充套件。
java裡頭的iterator是以pull模型,即訂閱者使用next去拉取下一個資料;而reactive streams則是以push模型為主,訂閱者呼叫subscribe方法訂閱,釋出者呼叫訂閱者的onNext通知訂閱者新訊息。
reactive streams java api
reactive streams定義了4個java api,如下
Processor<T,R>
processor既是Subscriber也是Publisher,代表二者的處理階段
Publisher
publisher是資料的提供者, 將資料釋出給訂閱者
Subscriber
在呼叫Publisher.subscribe(Subscriber)之後,Subscriber.onSubscribe(Subscription)將會被呼叫
Subscription
Subscription代表訂閱者與釋出者的一次訂閱週期,一旦呼叫cancel去掉訂閱,則釋出者不會再推送訊息。
觀察者模式
觀察者模式的實現有推模型和拉模型
- 拉模型
即釋出者通知訂閱有新訊息,訂閱者再去找釋出者拉取
- 推模型
即釋出者通知訂閱者有訊息,通知的時候已經帶上了一個新訊息
reactor例項
maven
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
複製程式碼
reactor 3 是java裡頭reactive streams的一個實現,基於reactive streams的java api,是spring 5反應式程式設計的基礎。
Flux例項
@Test
public void testBackpressure(){
Flux.just(1, 2, 3, 4)
.log()
.subscribe(new Subscriber<Integer>() {
private Subscription s;
int onNextAmount;
@Override
public void onSubscribe(Subscription s) {
this.s = s;
s.request(2);
}
@Override
public void onNext(Integer integer) {
System.out.println(integer);
onNextAmount++;
if (onNextAmount % 2 == 0) {
s.request(2);
}
}
@Override
public void onError(Throwable t) {}
@Override
public void onComplete() {}
});
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
複製程式碼
小結
從上面的程式碼看,reactive streams實際上是推拉結合的模式的結合。為什麼還要拉呢?
rabbitmq vs kafka
rabbitmq是以推為主的,如果消費者消費能力跟不上,則訊息會堆積在記憶體佇列中(
必要時可能寫磁碟
)
kafka則是以拉為主的,生產者推送訊息到broker,消費者自己根據自己的能力從broker拉取訊息,由於訊息是持久化的,因此無需關心生產消費速率的不平衡
backpressure
backpressure這個是為處理生產速率與消費速率不平衡這個問題而衍生出來的,訂閱者可以在next方法裡頭根據自己的情況,使用request方法告訴釋出者要取N個資料,釋出者則向訂閱者推送N個資料。通過request達到訂閱者對釋出者的反饋。而對於釋出者而言,為了實現backpressure,則需要有一個快取佇列來緩衝訂閱者沒來得及消費的資料。涉及到緩衝,就涉及容量是有界還是無界,如果是有界則在緩衝慢的時候,處理策略是怎樣等等。