Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/371.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
RxJava中的指数退避_Java_Java 8_Rx Java_Exponential Backoff - Fatal编程技术网

RxJava中的指数退避

RxJava中的指数退避,java,java-8,rx-java,exponential-backoff,Java,Java 8,Rx Java,Exponential Backoff,我有一个API,它接受一个触发事件的可观察的 我想返回一个可观察的值,如果检测到Internet连接,该值每defaultDelay秒发出一个值,如果没有连接,则延迟numberOfFailedAttempts^2次 我尝试过多种风格,最大的问题是retryWhen的observable只评估一次: Observable .interval(defaultDelay,TimeUnit.MILLISECONDS) .observeOn(Schedulers.io()) .r

我有一个API,它接受一个触发事件的
可观察的

我想返回一个可观察的
值,如果检测到Internet连接,该值每
defaultDelay
秒发出一个值,如果没有连接,则延迟
numberOfFailedAttempts^2次

我尝试过多种风格,最大的问题是
retryWhen的
observable只评估一次:

Observable
    .interval(defaultDelay,TimeUnit.MILLISECONDS)
    .observeOn(Schedulers.io())
    .repeatWhen((observable) ->
         observable.concatMap(repeatObservable -> {
             if(internetConnectionDetector.isInternetConnected()){
                 consecutiveRetries = 0;
                 return observable;
             } else {
                 consecutiveRetries++;
                 int backoffDelay = (int)Math.pow(consecutiveRetries,2);
                 return observable.delay(backoffDelay, TimeUnit.SECONDS);
                }
         }).onBackpressureDrop())
    .onBackpressureDrop();

有什么办法可以做我想做的事吗?我发现了一个相关的问题(目前无法在搜索中找到),但所采取的方法似乎不适用于动态值。

您可以使用
retryWhen
操作符在没有连接时配置延迟。如何定期发出项目是一个单独的主题(查找
interval
timer
操作符)。如果你想不出来,就另问一个问题

我有一个关于我的Github的广泛的例子,但我会在这里给你要点

RetryWithDelay retryWithDelay = RetryWithDelay.builder()
    .retryDelayStrategy(RetryDelayStrategy.RETRY_COUNT)
    .build()

Single.fromCallable(() -> {
    ...
}).retryWhen(retryWithDelay)
.subscribe(j -> {
    ...
})

RetryWithDelay
定义如下。我使用的是RxJava2.x,所以如果您使用的是1.x,那么签名应该是
Func1我总是发现
retryWhen
有点低级,所以对于指数回退,我使用了一个经过单元测试的构建器(比如Abhijit),它可以随时用于RxJava1.x。我建议使用封顶版本,这样延迟的指数增长不会超过您定义的最大值

这是您使用它的方式:

observable.retryWhen(
    RetryWhen.exponentialBackoff(
        delay, maxDelay, TimeUNIT.SECONDS)
    .build());
我不同意
retryWhen
有bug,但如果发现bug,请将其报告给RxJava。bug修复得很快

您将需要Maven Central上的rxjava extras 0.8.0.6或更高版本:

<dependency>
    <groupId>com.github.davidmoten</groupId>
    <artifactId>rxjava-extras</artifactId>
    <version>0.8.0.6</version>
</dependency>

com.github.davidmoten

从0.1.4开始,代码中有两个错误:

  • 为了重复某个可观察序列,该序列必须是有限的。也就是说,你最好像我在下面的示例中所做的那样,使用
    just
    fromCallable
    之类的内容,而不是
    interval
  • repeatWhen
    的内部函数中,您需要返回新的延迟可观测源,因此您必须返回
    observatable.delay()
    ,而不是
    observatable.timer()
  • 工作代码:

    public void testRepeat() throws InterruptedException {
        logger.info("test start");
    
        int DEFAULT_DELAY = 100; // ms
        int ADDITIONAL_DELAY = 100; // ms
        AtomicInteger generator = new AtomicInteger(0);
        AtomicBoolean connectionAlive = new AtomicBoolean(true); // initially alive
    
        Disposable subscription = Observable.fromCallable(generator::incrementAndGet)
                .repeatWhen(counts -> {
                    AtomicInteger retryCounter = new AtomicInteger(0);
                    return counts.flatMap(c -> {
                        int retry = 0;
                        if (connectionAlive.get()) {
                            retryCounter.set(0); // reset counter
                        } else {
                            retry = retryCounter.incrementAndGet();
                        }
                        int additionalDelay = ADDITIONAL_DELAY * (int) Math.pow(retry, 2);
                        logger.info("retry={}, additionalDelay={}ms", retry, additionalDelay);
                        return Observable.timer(DEFAULT_DELAY + additionalDelay, TimeUnit.MILLISECONDS);
                    });
                })
                .subscribe(v -> logger.info("got {}", v));
    
        Thread.sleep(220);
        logger.info("connection dropped");
        connectionAlive.set(false);
        Thread.sleep(2000);
        logger.info("connection is back alive");
        connectionAlive.set(true);
        Thread.sleep(2000);
        subscription.dispose();
        logger.info("test complete");
    }
    

    请参阅关于
    repeatWhen

    的详细文章问题中的示例可能来自我的尝试,因为它似乎混合了我使用的两种方法(一种是基于计时器+重试,一种是基于间隔+延迟订阅),问题实际上来自那篇文章,这表示要重试/重复的可观察输入应该再次使用。不使用该可观察项是否会导致订阅泄漏问题?@SockedTrailMix是关于第一级输入的,而不是关于内部
    flatMap
    。关于非常相似的模式,请参见文章中的最后一个示例。哦,我明白了,很抱歉我错过了
    计数
    是获得flatMap的原因。我知道我在某处看到过这一点!我不想在这里重新发明轮子,所以我可能会继续,看看实现,看看我应该怎么做今天我注意到我忘了实现最大后退,但在使用0.8.0.6版时,我似乎不存在该方法签名。前几天我很忙,所以我告诉自己,我会回到我需要它的代码段,似乎该解决方案没有我预期的行为,并且在成功调用后应该重置重试(这是有道理的,因为这需要“带外”通信)。我认为下面概述的repeatWhen方法是我目前的情况所需要的,这种方法似乎针对更常见的情况进行了优化,即“重试直到成功”与“总是重试,如果不成功则延迟更长”如何
    repeat().retryWhen()
    为了满足您的“始终重试”要求?这就是我根据retryWhen observable+flatMap的答案所做的。我认为我缺少的两件事是在retryWhen observable上进行flatMap,而不是仅仅返回一个新的,以及不从flatMap内部返回源observable(这导致了轻微的内存泄漏,无法正常工作)
    public void testRepeat() throws InterruptedException {
        logger.info("test start");
    
        int DEFAULT_DELAY = 100; // ms
        int ADDITIONAL_DELAY = 100; // ms
        AtomicInteger generator = new AtomicInteger(0);
        AtomicBoolean connectionAlive = new AtomicBoolean(true); // initially alive
    
        Disposable subscription = Observable.fromCallable(generator::incrementAndGet)
                .repeatWhen(counts -> {
                    AtomicInteger retryCounter = new AtomicInteger(0);
                    return counts.flatMap(c -> {
                        int retry = 0;
                        if (connectionAlive.get()) {
                            retryCounter.set(0); // reset counter
                        } else {
                            retry = retryCounter.incrementAndGet();
                        }
                        int additionalDelay = ADDITIONAL_DELAY * (int) Math.pow(retry, 2);
                        logger.info("retry={}, additionalDelay={}ms", retry, additionalDelay);
                        return Observable.timer(DEFAULT_DELAY + additionalDelay, TimeUnit.MILLISECONDS);
                    });
                })
                .subscribe(v -> logger.info("got {}", v));
    
        Thread.sleep(220);
        logger.info("connection dropped");
        connectionAlive.set(false);
        Thread.sleep(2000);
        logger.info("connection is back alive");
        connectionAlive.set(true);
        Thread.sleep(2000);
        subscription.dispose();
        logger.info("test complete");
    }