翻譯自:https://tech.io/playgrounds/929/reactive-programming-with-reactor-3/Request
Request
Description
Remember this diagram?
There's one aspect to it that we didn't cover: the volume control. In Reactive
Streams terms this is called backpressure
. It is a feedback mechanism that
allows a Subscriber
to signal to its Publisher
how much data it is prepared
to process, limiting the rate at which the Publisher
produces data.
有一個(gè)方面我們沒有涉及:容量控制。在反應(yīng)流術(shù)語中,這稱為backpressure(背壓)。這是一種反饋機(jī)制,
允許訂閱者向其發(fā)布者發(fā)出信號,告知其準(zhǔn)備處理多少數(shù)據(jù),從而限制發(fā)布者生成數(shù)據(jù)的速率。
This control of the demand is done at the Subscription
level: a Subscription
is
created for each subscribe()
call and it can be manipulated to either cancel()
the flow of data or tune demand with request(long)
.
這種對需求的控制是在訂閱級別上完成的:為每個(gè)subscribe()調(diào)用創(chuàng)建一個(gè)訂閱,可以對其進(jìn)行操作,
以cancel()數(shù)據(jù)流或使用request(long)調(diào)整需求。
Making a request(Long.MAX_VALUE)
means an unbounded demand, so the
Publisher
will emit data at its fastest pace.
request(Long.MAX_VALUE)意味著無限的需求,因此發(fā)布者將以最快的速度發(fā)布數(shù)據(jù)。
Practice
The demand can be tuned in the StepVerifier
as well, by using the relevant
parameter to create
and withVirtualTime
for the initial request, then
chaining in thenRequest(long)
in your expectations for further requests.
也可以在StepVerifier中調(diào)整需求,通過使用相關(guān)參數(shù)來create和withVirtualTime 初始請求,
然后將thenRequest(long)鏈接到您對進(jìn)一步請求的期望中。
In this first example, create a StepVerifier
that produces an initial
unbounded demand and verifies 4 values to be received, before completion.
This is equivalent to the way you've been using StepVerifier so far.
在第一個(gè)示例中,創(chuàng)建一個(gè)StepVerifier,生成初始無界需求,并在完成之前驗(yàn)證要接收的4個(gè)值。
這相當(dāng)于您目前使用StepVerifier的方式。
// Create a StepVerifier that initially requests all values and expect 4 values to be received
StepVerifier requestAllExpectFour(Flux<User> flux) {
return StepVerifier
.create(flux)
.thenRequest(Long.MAX_VALUE)
.expectNextCount(4)
.expectComplete();
}
Next we will request values one by one: for that you need an initial request,
but also a second single request after you've received and asserted the first element.
接下來,我們將逐個(gè)請求值:為此,您需要一個(gè)初始請求,但也需要在收到并斷言第一個(gè)元素后 做第二個(gè)單獨(dú)地請求。
Without more request, the source will never complete unless you cancel it.
This can be done instead of the terminal expectations by using .thenCancel()
.
If you want to also ensure no incoming signal is received over a Duration
you
can instead use .expectTimeout(Duration)
.
如果沒有更多的請求,源將永遠(yuǎn)無法完成,除非您取消它。這可以通過使用.thenCancel()來代替終端期望。
如果您還想確保在一段時(shí)間內(nèi)沒有接收到任何傳入信號,則可以使用.expectTimeout(Duration)。
// Create a StepVerifier that initially requests 1 value and expects User.SKYLER then requests another value and expects User.JESSE then stops verifying by cancelling the source
StepVerifier requestOneExpectSkylerThenRequestOneExpectJesse(Flux<User> flux) {
return StepVerifier
.create(flux)
.thenRequest(1).expectNext(User.SKYLER)
.thenRequest(1).expectNext(User.JESSE)
.thenCancel();
}
A note on debugging
How to check that the previous sequence was requested one by one, and that a
cancellation happened?
如何檢查前一個(gè)序列是否被逐個(gè)請求,以及是否發(fā)生了取消?
It's important to be able to debug reactive APIs, so in the next example we will
make use of the log
operator to know exactly what happens in terms of signals
and events.
能夠調(diào)試反應(yīng)式API很重要,因此在下一個(gè)示例中,我們將使用日志運(yùn)算符來準(zhǔn)確了解信號和事件的情況。
Use the repository
to get a Flux
of all users, then apply a log to it.
Observe in the console below how the underlying test requests it, and the other
events like subscribe, onNext...
使用repository獲取所有用戶的Flux,然后對其應(yīng)用日志。在下面的控制臺(tái)中觀察底層測試如何請求它,
以及其他事件,如subscribe、onNext等
// Return a Flux with all users stored in the repository that prints automatically logs for all Reactive Streams signals
Flux<User> fluxWithLog() {
return repository.findAll().log();
}
運(yùn)行日志如下:
2022-07-25 09:46:53 [main] INFO reactor.Flux.Zip.1 - onSubscribe(FluxZip.ZipCoordinator)
2022-07-25 09:46:53 [main] INFO reactor.Flux.Zip.1 - request(1)
2022-07-25 09:46:53 [parallel-1] INFO reactor.Flux.Zip.1 - onNext(Person{username='swhite', firstname='Skyler', lastname='White'})
2022-07-25 09:46:53 [parallel-1] INFO reactor.Flux.Zip.1 - request(1)
2022-07-25 09:46:54 [parallel-1] INFO reactor.Flux.Zip.1 - onNext(Person{username='jpinkman', firstname='Jesse', lastname='Pinkman'})
2022-07-25 09:46:54 [parallel-1] INFO reactor.Flux.Zip.1 - request(2)
2022-07-25 09:46:54 [parallel-1] INFO reactor.Flux.Zip.1 - onNext(Person{username='wwhite', firstname='Walter', lastname='White'})
2022-07-25 09:46:54 [parallel-1] INFO reactor.Flux.Zip.1 - onNext(Person{username='sgoodman', firstname='Saul', lastname='Goodman'})
2022-07-25 09:46:54 [parallel-1] INFO reactor.Flux.Zip.1 - onComplete()
If you want to perform custom actions without really modifying the elements
in the sequence, you can use the "side effect" methods that start with do
/doOn
.
如果您想在不修改序列中元素的情況下執(zhí)行自定義操作,可以使用以do/doOn開頭的“副作用”方法。
For example, if you want to print "Requested" each time the operator receives
a request, use doOnRequest
. If you want to print "Starting" first, upon
subscription before any signal has been received, use doFirst
, etc.
例如,如果您想在運(yùn)算符每次收到請求時(shí)打印"Requested",請使用doOnRequest。
如果您想先打印"Starting",在收到任何信號之前訂閱時(shí),請使用doFirst等。
Each doOn
method takes a relevant callback representing the custom action for
the corresponding event.
每個(gè)doOn方法接受一個(gè)相關(guān)回調(diào),表示相應(yīng)事件的自定義操作。
Note that you should not block or invoke operations with latency in these callbacks
(which is also true of other operator callbacks like map
): it's more for quick
operations.
注意,您不應(yīng)該在這些回調(diào)中阻塞或調(diào)用具有延遲的操作(對于其他操作符回調(diào),如map也是如此):它更適合快速操作。
// Return a Flux with all users stored in the repository that prints "Starring:" at first, "firstname lastname" for all values and "The end!" on complete
Flux<User> fluxWithDoOnPrintln() {
return repository
.findAll()
.doFirst(()->System.out.println("Starring:"))
.doOnNext(user -> System.out.println(user.getFirstname()+" "+user.getLastname()))
.doOnComplete(()->System.out.println("The end!"));
}