【问题标题】:Java 8 - retry a method until a condition is fulfilled (in intervals)Java 8 - 重试方法直到满足条件(间隔)
【发布时间】:2015-06-22 20:49:18
【问题描述】:

我想创建一个可以运行方法的类,直到满足有关返回值的条件。

它应该看起来像这样

methodPoller.poll(pollDurationSec, pollIntervalMillis)
            .method(dog.bark())
            .until(dog -> dog.bark().equals("Woof"))
            .execute();

我的方法 poller 看起来有点像这样 () // 跟随 GuiSim 回答

public class MethodPoller {
    Duration pollDurationSec;
    int pollIntervalMillis;


    public MethodPoller() {
    }

    public MethodPoller poll(Duration pollDurationSec, int pollIntervalMillis) {
        this.pollDurationSec = pollDurationSec;
        this.pollIntervalMillis = pollIntervalMillis;
        return this;
    }

    public <T> MethodPoller method(Supplier<T> supplier) {

        return this;
    }

    public <T> MethodPoller until(Predicate<T> predicate) {

        return this;
    }
}

但我很难从这里开始。
在满足条件之前,如何实现对一般方法的重试?
谢谢。

【问题讨论】:

  • 好吧,关于方法本身,您可以引用目标对象、它的反射方法和所需的参数。您也可以将其封装在 lambda 中,但这绝对取决于该方法的传递方式 - 显式或发现?参数呢?
  • @rails 如果我提供的答案让您满意,请采纳。如果您还有其他问题,请不要犹豫。

标签: java java-8 hamcrest


【解决方案1】:

是的,这可以在 Java 7 中轻松完成,甚至使用 Java 8 更简洁。

method 方法的参数应该是java.util.function.Supplier&lt;T&gt;until 方法的参数应该是java.util.function.Predicate&lt;T&gt;

然后您可以使用方法引用或 lambda 表达式来创建您的 Poller,如下所示:

myMethodPoller.poll(pollDurationInteger, intervalInMillisecond)
          .method(payment::getStatus)
          .until (paymentStatus -> paymentStatus.getValue().equals("COMPLETED"))
          .execute();

附带说明,如果您要使用 Java 8,我建议使用 java.time.Duration 而不是整数来表示轮询持续时间和间隔。

我还建议您查看https://github.com/rholder/guava-retrying,这是一个您或许可以使用的库。如果没有,它可能会成为您的 API 的一个很好的灵感,因为它具有很好的流畅 API。

编辑: 在更新问题之后,这里是一个简单的实现。我留下了一些部分供你以 TODO 的形式完成。

import java.time.Duration;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class MethodPoller<T> {

    Duration pollDurationSec;
    int pollIntervalMillis;

    private Supplier<T> pollMethod = null;
    private Predicate<T> pollResultPredicate = null;

    public MethodPoller() {
    }

    public MethodPoller<T> poll(Duration pollDurationSec, int pollIntervalMillis) {
        this.pollDurationSec = pollDurationSec;
        this.pollIntervalMillis = pollIntervalMillis;
        return this;
    }

    public MethodPoller<T> method(Supplier<T> supplier) {
        pollMethod = supplier;
        return this;
    }

    public MethodPoller<T> until(Predicate<T> predicate) {
        pollResultPredicate = predicate;
        return this;
    }

    public T execute()
    {
        // TODO: Validate that poll, method and until have been called.

        T result = null;
        boolean pollSucceeded = false;
        // TODO: Add check on poll duration
        // TODO: Use poll interval
        while (!pollSucceeded) {
            result = pollMethod.get();
            pollSucceeded = pollResultPredicate.test(result);
        }

        return result;
    }
}

使用示例:

import static org.junit.Assert.assertTrue;
import java.util.UUID;
import org.junit.Test;

public class MethodPollerTest
{

    @Test
    public void test()
    {
        MethodPoller<String> poller = new MethodPoller<>();
        String uuidThatStartsWithOneTwoThree = poller.method(() -> UUID.randomUUID().toString())
                                                     .until(s -> s.startsWith("123"))
                                                     .execute();
        assertTrue(uuidThatStartsWithOneTwoThree.startsWith("123"));
        System.out.println(uuidThatStartsWithOneTwoThree);
    }
}

【讨论】:

  • 谢谢。您的答案中有很多宝石。更新了问题,因为我仍然需要一些帮助。谢谢。
  • 我可以建议将while 循环压缩为while(!pollResultPredicate.test(pollMethod.get()) Thread.sleep(calculateDuration());
  • @ndm13 这取决于。如果需要在通过验证后获取结果,则需要保留一个引用。
  • public MethodPoller poll 应该是 public MethodPoller&lt;T&gt; poll 以避免未经检查的原始类型
  • 这会阻止线程在执行时处理其他请求吗?
【解决方案2】:

你可以用Awaitility代替自己写这个吗?

await()
    .atMost(3, SECONDS)
    .until(dog::bark, equalTo("woof"));

【讨论】:

  • 即使这真的很好用 Awaitility 更适合测试代码,而不是生产
  • @Icardito,忙等待不是生产的最佳实践,但如果您已经有,则无需自己实现。
【解决方案3】:

这是使用Failsafe的解决方案:

RetryPolicy<String> retryPolicy = new RetryPolicy<String>()
  .handleIf(bark -> bark.equals("Woof"))
  .withMaxDuration(pollDurationSec, TimeUnit.SECONDS);
  .withDelay(pollIntervalMillis, TimeUnit.MILLISECONDS);

Failsafe.with(retryPolicy).get(() -> dog.bark());

如您所见,非常简单,并且可以处理您的确切场景。

【讨论】:

    【解决方案4】:

    您可以使用RxJava

      Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
                    .map(tick -> dog)
                    .takeWhile( dog-> { return ! dog.bark().equals("Woof"); })
                    .subscribe(dog ->dog.bark());
    
    
            try {
                Thread.sleep(10000);
            }catch(Exception e){}
    

    http://blog.freeside.co/2015/01/29/simple-background-polling-with-rxjava/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-10
      • 1970-01-01
      • 1970-01-01
      • 2014-01-23
      • 2015-07-28
      • 1970-01-01
      • 1970-01-01
      • 2013-12-28
      相关资源
      最近更新 更多