This series of articles index the "Response Spring's word Wizard"
Previously feed-response Flow | Reactor 3 Quick Start | Responsive Flow Specification
This article source
2.2 Custom Data Flow
This section describes how to onNext
onError
onComplete
Create a Flux or Mono by defining the appropriate events (, and). Reactor provides, and, generate
create
push
and handle
so on, all of these methods use sink (pools) to generate data streams.
Sink, as the name implies, is the pool, you can imagine the kitchen pool look. As shown in the following:
The methods described below have a sink provided to the method user, usually exposing at least three methods to us, next
error
and complete
. Next and error are equivalent to two drains, and we keep putting the custom data into next, and reactor will string us into a publisher stream until a wrong data is put to the error port, or a button is clicked complete
, and the data flow terminates.
2.2.1 Generate
generate
is a way to emit data on a synchronous basis. Because the sink it provides is one SynchronousSink
, and its next()
method can only be called once per callback.
generate
There are three types of signatures for this method:
public static <T> Flux<T> generate(Consumer<SynchronousSink<T>> generator) public static <T, S> Flux<T> generate(Callable<S> stateSupplier, BiFunction<S, SynchronousSink<T>, S> generator) public static <T, S> Flux<T> generate(Callable<S> stateSupplier, BiFunction<S, SynchronousSink<T>, S> generator, Consumer<? super S> stateConsumer)
1) using Synchronoussink to generate data streams
@Test public void testGenerate1() { final AtomicInteger count = new AtomicInteger(1); // 1 Flux.generate(sink -> { sink.next(count.get() + " : " + new Date()); // 2 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (count.getAndIncrement() >= 5) { sink.complete(); // 3 } }).subscribe(System.out::println); // 4 }
- Used for counting;
- Put custom data into the pool;
- Tell
generate
the method that the custom data has been sent out;
- Triggers the data flow.
The output is printed every 1 seconds for a total of 5 printing times.
2) Add a companion state
For the above example, count
for the record state, the count is stopped when the value reaches 5. Because it is used inside a lambda, it must be of the final type and cannot be a primitive type (such as int
) or an immutable type (such as Integer
).
If you use the second method signature, the above example can be changed like this:
@Test public void testGenerate2() { Flux.generate( () -> 1, // 1 (count, sink) -> { // 2 sink.next(count + " : " + new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (count >= 5) { sink.complete(); } return count + 1; // 3 }).subscribe(System.out::println); }
- Initializes the state value;
- The second argument is
BiFunction
that the input is state and sink;
- Each cycle returns a new status value for the next use.
3) Complete post-processing
The third method signature in addition to the state, sink, there is one, which is Consumer
Consumer
executed after the data flow has been sent out.
Flux.generate( () -> 1, (count, sink) -> { sink.next(count + " : " + new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (count >= 5) { sink.complete(); } return count + 1; }, System.out::println) // 1 .subscribe(System.out::println); }
- Finally, the count value is printed out.
If state uses a database connection or other resource that needs to be cleaned, this Consumer lambda can be used to finalize the resource cleanup task.
2.2.2 Create
create
is a more advanced method of creating flux, which can generate data streams either synchronously or asynchronously, and can emit multiple elements at a time.
create
Use, the FluxSink
latter also provides methods such as Next,error and complete. Unlike generate, create does not require a state value, and on the other hand, it can trigger multiple events in the callback, even if the event occurs at some time in the future.
The common scenario for create is to turn an existing API into a responsive one, such as a listener's async method.
Write an event source first:
public class Myeventsource {private list<myeventlistener> listeners; Public Myeventsource () {this.listeners = new arraylist<> (); } public void register (MyEventListener listener) {//1 listeners.add (listener); } public void Newevent (MyEvent event) {for (MyEventListener listener:listeners) { Listener.onnewevent (event); 2}} public void eventstopped () {for (MyEventListener listener: Listeners) {listener.oneventstopped (); 3}} @Data @NoArgsConstructor @AllArgsConstructor public static class My Event {//4 private Date timestemp; Private String message; } }
- Register the Listener;
- Issue a new event to the listener;
- Tells the listener that the event source has stopped;
- event class, using the Lombok annotation.
Prepare a listener interface that can listen for two events on the top 2nd and 3: (1) New arrivals, and MyEvent
(2) event sources to stop. As follows:
public interface MyEventListener { void onNewEvent(MyEventSource.MyEvent event); void onEventStopped(); }
The following test method logic is: Create a listener registered to the event source, the listener receives the event callback again when the Flux.create
sink to convert a series of events into an asynchronous stream of events:
@Test public void Testcreate () throws interruptedexception {Myeventsource EventSource = new Myeventsource () ; 1 flux.create (sink, {eventsource.register) (new MyEventListener () {//2 @Override public void Onnewevent (Myeventsource.myevent event) { Sink.next (event); 3} @Override public void oneventstopped () { Sink.complete (); 4}}); }). Subscribe (system.out::p rintln); 5 for (int i = 0; i <; i++) {//6 random random = new random (); TimeUnit.MILLISECONDS.sleep (Random.nextint (1000)); Eventsource.newevent (New Myeventsource.myevent (New Date (), "event-" + i)); } eventsource.eventstopped (); 7}
- Event source;
- Registers a listener created with an anonymous inner class to the event source;
- The listener sends the event back through sink when the event callback is received;
- When the listener receives a callback from the source stop, it sends the completion signal through the sink;
- Trigger the Subscription (no event has been generated at this time);
- The loop produces 20 events, each with a random time of no more than 1 seconds;
- Last stop event source.
Run this test method, 20 MyEvent
print out in succession.
If the above method is replaced with a create
generate
method, an exception is reported:
java.lang.IllegalStateException: The generator didn‘t call any of the SynchronousSink method
proves that generate
the asynchronous approach is not supported.
create
Method also has a Variant method push
that is suitable for generating event streams. With create 类似,
push can also be asynchronous and be able to use the various back-pressure strategies above. So the above example can be replaced with a push
method. The difference is that, push
in a method, the call next
, complete
or error
the same thread must be the same.
In addition to next
, complete
or error
methods, FluxSink
There are onRequest
methods that can be used to respond to a downstream subscriber's request event. Thus not only can the upstream be pushed downstream when the data is ready, as in the previous example, but downstream can also pull data that is already ready from the upstream. This is a push/pull blending pattern. Like what:
Flux<String> bridge = Flux.create(sink -> { myMessageProcessor.register( new MyMessageListener<String>() { public void onMessage(List<String> messages) { for(String s : messages) { sink.next(s); // 1 } } }); sink.onRequest(n -> { // 2 List<String> messages = myMessageProcessor.request(n); // 3 for(String s : message) { sink.next(s); } }); ... }
- Push mode, the initiative to send data downstream;
- Called when a downstream request is made;
- Responds to downstream requests and queries for a message that is available.
Reactor 3 Custom Data Flow--response spring's DAO spell device