6

I have one example of a GET method that generates a Randon INT and sends back to the client

@GetMapping
public Flux<String> search() {
    return Flux.create(fluxSink -> {

            Random r = new Random();
            int n;
            for (int i = 0; i < 10; i++) {
                n= r.nextInt(1000);
                System.out.println("Creating:"+n);
                fluxSink.next(String.valueOf(n));

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            fluxSink.complete();


    });
}

Curl:

curl -v -H "Accept: text/event-stream" -X GET 'http://localhost:8080/stream

With this code using a CURL command I can only see the numbers in my client when the fluxSink.complete happens.

Now If I change my code to:

@GetMapping
public Flux<String> search() {
    return Flux.create(fluxSink -> {
        Thread t = new Thread(() -> {
            Random r = new Random();
            int n;
            for (int i = 0; i < 10; i++) {
                n= r.nextInt(1000);
                System.out.println("Creating:"+n);
                fluxSink.next(String.valueOf(n));

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            fluxSink.complete();
        });
        t.start();
    });
}

Wrapping up the process into a Thread it works fine. I can see the data being transferred fine when the fluxSink.next happens.

Can anyone explain this effect? How can I see the data flowing without explicitly using a Thread?

Thank you!

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176

1 Answers1

2

Flux.create() is about "emitting multiple elements in a synchronous or asynchronous manner through the FluxSink API". In your case, you're synchronously emitting all elements and you're giving no chance to Reactor to do work since you're never exiting that method until everything is done. In a nutshell, you're blocking the current thread, which is forbidden with Reactor.

Using a thread leaves a chance to Reactor to dispatch those signals. But the spirit of Flux.create() (as seen in its javadoc), is about adapting to callbacks.

In this case, maybe Flux.generate would be a better fit. Trying to simulate latency with Thread is not a good idea. You could instead do something like:

Flux<String> response = Flux.just(aListOfElements).delayElements(Duration.ofSeconds(2));
Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • I got what you are saying and you are correct, that was a simplistic example. In my real world case, I need to create my own flux from a non-reactive source. So What I have done was wrap up the method I call from the flux.create with the @Async spring annotation. With that I was able to achieve the effect I wanted. Thank you – Rodrigo De Oliveira Murta Jun 08 '18 at 22:50
  • 1
    I would definitely advise against that. Did you configure a specific executor for that? In any case you should instead use the publishOn operator to process that work on a scheduler (see reactor reference documentation) – Brian Clozel Jun 09 '18 at 07:45
  • Can this approach somehow be adapted to handle cases with, e.g. an endless list of elements? My use case looks , I receive some messages via a blocking call and try to provide them as a Flux. – kleino Aug 20 '18 at 12:36
  • If you're receiving those in a callback, then `Flux.create` should work. The Reactor reference documentation has some examples for this. – Brian Clozel Aug 20 '18 at 12:38