如何通过Flux.interval减缓排放?

时间:2017-06-01 21:33:30

标签: project-reactor

这需要背压还是有更简单的方法?

例如,在下面的代码中,我希望每2秒调用一次spin函数。有时'旋转'可能需要比2秒间隔更长的计算时间,在这种情况下,我不希望任何间隔发射排队。但是在下面的代码中,它们会排队。

在下面的代码中,前4个旋转函数调用需要10秒,其余调用需要1秒。因此,一旦功能变得更快,Flux.interval排放就会“赶上”。但是,我不希望任何“追赶”发生

import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.Date;
import java.util.Iterator;

public class Test {
   public static void main(String[] args) {
        Iterator<Integer> secs = new Iterator<Integer>() {
            private int num = 0;
            @Override
            public boolean hasNext() {
                return true;
            }
            @Override
            public Integer next() {
                return num++ < 4 ? 10 : 1;
            }
        };

        Flux.interval(Duration.ofSeconds(5))
                .map(n -> {spin(secs.next()); return n;})
                .doOnNext(n -> log("Processed " + n))
                .blockLast();

    }

    private static void spin(int secs) {
        log("Current job will take " + secs + " secs");
        long sleepTime = secs*1000000000L; // convert to nanos
        long startTime = System.nanoTime();
        while ((System.nanoTime() - startTime) < sleepTime) {}
    }

    static void log(Object label) {
        System.out.println((new Date()).toString() + "\t| " +Thread.currentThread().getName()   + "\t| " + label);
    }
}

输出: 请注意,“已处理”时间戳最初间隔10秒,但从作业4到作业8,我不想发生“追赶”。我想在前一次调用之后的2秒内旋转执行

Thu Jun 01 17:16:23 EDT 2017    | parallel-1    | Current job will take 10 secs
Thu Jun 01 17:16:33 EDT 2017    | parallel-1    | Processed 0
Thu Jun 01 17:16:33 EDT 2017    | parallel-1    | Current job will take 10 secs
Thu Jun 01 17:16:43 EDT 2017    | parallel-1    | Processed 1
Thu Jun 01 17:16:43 EDT 2017    | parallel-1    | Current job will take 10 secs
Thu Jun 01 17:16:53 EDT 2017    | parallel-1    | Processed 2
Thu Jun 01 17:16:53 EDT 2017    | parallel-1    | Current job will take 10 secs
Thu Jun 01 17:17:03 EDT 2017    | parallel-1    | Processed 3
Thu Jun 01 17:17:03 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:04 EDT 2017    | parallel-1    | Processed 4
Thu Jun 01 17:17:04 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:05 EDT 2017    | parallel-1    | Processed 5
Thu Jun 01 17:17:05 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:06 EDT 2017    | parallel-1    | Processed 6
Thu Jun 01 17:17:06 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:07 EDT 2017    | parallel-1    | Processed 7
Thu Jun 01 17:17:07 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:08 EDT 2017    | parallel-1    | Processed 8
Thu Jun 01 17:17:08 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:09 EDT 2017    | parallel-1    | Processed 9
Thu Jun 01 17:17:13 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:14 EDT 2017    | parallel-1    | Processed 10
Thu Jun 01 17:17:18 EDT 2017    | parallel-1    | Current job will take 1 secs
Thu Jun 01 17:17:19 EDT 2017    | parallel-1    | Processed 11

2 个答案:

答案 0 :(得分:0)

@Test
public void testInterval() throws Exception {
    Flux.interval(Duration.ofSeconds(1))
            .subscribe(new Subscriber<Long>() {
                private Subscription subscription;

                @Override
                public void onSubscribe(final Subscription s) {
                    this.subscription = s;
                    s.request(1);
                }

                @Override
                public void onNext(final Long aLong) {
                    // execute the operation that cold possibly take longer than the interval (1s):
                    try {
                        Thread.sleep(5000);
                        System.out.println(aLong);
                    } catch (InterruptedException e) {
                    }

                    // request another item when we're done:
                    subscription.request(1);
                }

                @Override
                public void onError(final Throwable t) {
                }

                @Override
                public void onComplete() {
                }
            });

    Thread.sleep(12000);
}

这个想法是明确地要求&#34;下一个间隔&#34;一旦你的长期任务完成。

答案 1 :(得分:0)

您可以使用delayElements(Duration delay)方法延迟事件。如果您想要更精细的控制,可以使用delayUntil(Function<? super T,? extends Publisher<?>> triggerProvider)

根据您的具体要求,结果可能如下所示:

 Flux.interval(Duration.ofSeconds(5))
     .map(n -> {spin(secs.next()); return n;})
     .doOnNext(n -> log("Processed " + n))
     .delayUntil(n -> Mono.just(1).delayElements(Duration.Seconds(Math.max(0, 2 - n)))
     .blockLast();