Как в Scala реализовать такой повторный вызов? - PullRequest
49 голосов
/ 28 октября 2011

Все еще новичок в Scala, и я сейчас ищу способ реализовать на нем следующий код:

@Override
public void store(InputStream source, String destination, long size) {

    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setContentLength(size);
    final PutObjectRequest request = new PutObjectRequest(
            this.configuration.getBucket(), destination, source, metadata);

    new RetryableService(3) {

        @Override
        public void call() throws Exception {
            getClient().putObject(request);
        }
    };

}

Каков наилучший способ реализовать ту же функциональность, которую реализует RetryableService, но вScala?

Он в основном вызывает метод call N раз, если все они терпят неудачу, тогда возникает исключение, если они успешны, он перемещается дальше.Этот ничего не возвращает, но у меня есть другая версия, которая позволяет возвращать значение (поэтому у меня есть два класса в Java), и я считаю, что я мог бы сделать с одним классом / функцией в Scala.

Любойидеи?

РЕДАКТИРОВАТЬ

Текущая реализация в Java выглядит следующим образом:

public abstract class RetryableService {

private static final JobsLogger log = JobsLogger
        .getLogger(RetryableService.class);

private int times;

public RetryableService() {
    this(3);
}

public RetryableService(int times) {
    this.times = times;
    this.run();
}

private void run() {

    RuntimeException lastExceptionParent = null;

    int x = 0;

    for (; x < this.times; x++) {

        try {
            this.call();
            lastExceptionParent = null;
            break;
        } catch (Exception e) {
            lastExceptionParent = new RuntimeException(e);
            log.errorWithoutNotice( e, "Try %d caused exception %s", x, e.getMessage() );

            try {
                Thread.sleep( 5000 );
            } catch (InterruptedException e1) {
                log.errorWithoutNotice( e1, "Sleep inside try %d caused exception %s", x, e1.getMessage() );
            }

        }

    }

    try {
        this.ensure();
    } catch (Exception e) {
        log.error(e, "Failed while ensure inside RetryableService");
    }

    if ( lastExceptionParent != null ) {
        throw new IllegalStateException( String.format( "Failed on try %d of %s", x, this ), lastExceptionParent);
    }   

}

public void ensure() throws Exception {
    // blank implementation
}

public abstract void call() throws Exception;

}

Ответы [ 13 ]

0 голосов
/ 12 апреля 2016

Это решение не оптимизировано компилятором для хвостовой рекурсии по какой-то причине (кто знает почему?), Но в случае редких повторных попыток будет вариант:

def retry[T](n: Int)(f: => T): T = {
  Try { f } recover {
    case _ if n > 1 => retry(n - 1)(f)
  } get
}

Использование:

val words: String = retry(3) {
  whatDoesTheFoxSay()
}

Конец ответа. Хватит читать здесь


Версия с результатом как попытка:

def reTry[T](n: Int)(f: => T): Try[T] = {
  Try { f } recoverWith {
    case _ if n > 1 => reTry(n - 1)(f)
  }
}

Использование:

// previous usage section will be identical to:
val words: String = reTry(3) {
  whatDoesTheFoxSay()
} get

// Try as a result:
val words: Try[String] = reTry(3) {
  whatDoesTheFoxSay()
}

Версия с функцией возврата Попробуйте

def retry[T](n: Int)(f: => Try[T]): Try[T] = {
  f recoverWith {
    case _ if n > 1 => reTry(n - 1)(f)
  }
}

Использование:

// the first usage section will be identical to:
val words: String = retry(3) {
  Try(whatDoesTheFoxSay())
} get

// if your function returns Try:
def tryAskingFox(): Try = Failure(new IllegalStateException)

val words: Try[String] = retry(3) {
    tryAskingFox()
}
0 голосов
/ 28 декабря 2014
//Here is one using Play framework

def retry[T](times:Int)(block: => Future[T])(implicit ctx: ExecutionContext):Future[T] = {

type V = Either[Throwable,T]
val i:Iterator[Future[Option[V]]] = 
  Iterator.continually(block.map(t => Right(t)).recover { case e => Left(e) }.map(t => Some(t)))
def _retry:Iteratee[V,V] = {
    def step(ctr:Int)(i:Input[V]):Iteratee[V,V] = i match {
        case Input.El(e) if (e.isRight) => Done(e,Input.EOF)
        case _ if (ctr < times) => Cont[V,V](i => step(ctr + 1)(i))
        case Input.El(e) => Done(e,Input.EOF)
    }
    Cont[V,V](i => step(0)(i))
}
Enumerator.generateM(i.next).run(_retry).flatMap { _ match {
  case Right(t) => future(t)
  case Left(e) => Future.failed(e)
}}
}
0 голосов
/ 05 июля 2014

Этот проект, кажется, предоставляет несколько хороших реализаций для различных механизмов повторения. https://github.com/hipjim/scala-retry

// define the retry strategy

implicit val retryStrategy =
    RetryStrategy.fixedBackOff(retryDuration = 1.seconds, maxAttempts = 2)

// pattern match the result

val r = Retry(1 / 1) match {
    case Success(x) => x
    case Failure(t) => log("I got 99 problems but you won't be one", t)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...