복붙노트

[SCALA] 이와 같은 재 시도-수 호출을 구현하는 스칼라 방법은 무엇입니까?

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);
        }
    };

}

무엇 동일한 기능을 다시 시도 가능한 서비스가 구현 만에 스칼라를 구현하는 가장 좋은 방법이 있을까요?

그것은 기본적으로 호출 방법 N 시간을 호출, 그들 모두는 그들이 그것을에 이동 성공할 경우를 제외하고는 다음 발생하지 않을 경우. 이것은 아무것도 반환하지 않습니다하지만 내가 값을 반환 수 있습니다 또 다른 버전이 (그래서, 자바에서 두 개의 클래스를) 나는 내가 스칼라에서 하나의 클래스 / 기능을 할 수 있으리라 생각합니다.

어떤 아이디어?

편집하다

다음과 같이 자바의 현재 구현은 다음과 같습니다

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;

}

해결법

  1. ==============================

    1.재귀 + 최고 수준의 기능에 의해 이름 매개 변수는 멋진 ==.

    재귀 + 최고 수준의 기능에 의해 이름 매개 변수는 멋진 ==.

    def retry[T](n: Int)(fn: => T): T = {
      try {
        fn
      } catch {
        case e =>
          if (n > 1) retry(n - 1)(fn)
          else throw e
      }
    }
    

    사용법은 다음과 같이이다 :

    retry(3) {
      // insert code that may fail here
    }
    

    편집 : @ themel의 대답에 의해 영감을 약간 변형. 코드 중 하나 개 적은 라인 :-)

    def retry[T](n: Int)(fn: => T): T = {
      try {
        fn
      } catch {
        case e if n > 1 =>
          retry(n - 1)(fn)
      }
    }
    

    편집 다시는 : 재귀는 스택 추적에 여러 통화를 추가 한 나를 귀찮게. 어떤 이유로, 컴파일러는 캐치 처리기에 최적화 꼬리 재귀는 할 수 없습니다. 꼬리 재귀되지 캐치 처리기에서, 그래도 잘 최적화 :-)

    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): T = {
      val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
      r match {
        case Some(x) => x
        case None => retry(n - 1)(fn)
      }
    }
    

    편집 다시 한번 : 분명히 나는 ​​다시오고이 답변에 대한 대안을 계속 추가 할에게 취미를 만들려고하고 있습니다. 여기에 옵션을 사용하지만, 기능은 관용적 스칼라없는 단락 복귀를 사용하는 것보다 좀 더 간단합니다 꼬리 재귀 버전입니다.

    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): T = {
      try {
        return fn
      } catch {
        case e if n > 1 => // ignore
      }
      retry(n - 1)(fn)
    }
    

    스칼라 2.10 업데이트합니다. 내 취미이기 때문에, 나는 가끔이 답변을 다시 방문. 꼬리 재귀 방식으로 재 시도를 구현하는 깨끗한 방법을 제공합니다 도입 시도로 스칼라 2.10.

    // Returning T, throwing the exception on failure
    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): T = {
      util.Try { fn } match {
        case util.Success(x) => x
        case _ if n > 1 => retry(n - 1)(fn)
        case util.Failure(e) => throw e
      }
    }
    
    // Returning a Try[T] wrapper
    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): util.Try[T] = {
      util.Try { fn } match {
        case util.Failure(_) if n > 1 => retry(n - 1)(fn)
        case fn => fn
      }
    }
    
  2. ==============================

    2.scalaz.concurrent.Task [T]의 방법이있다 : http://docs.typelevel.org/api/scalaz/nightly/#scalaz.concurrent.Task

    scalaz.concurrent.Task [T]의 방법이있다 : http://docs.typelevel.org/api/scalaz/nightly/#scalaz.concurrent.Task

    def retry(delays: Seq[Duration], p: (Throwable) ⇒ Boolean = _.isInstanceOf[Exception]): Task[T]
    

    태스크 [T]를, 당신은 재시도 사이의 지연은 지연 매개 변수에 의해 정의되는 시간의 특정 번호를 재 시도하는 새로운 작업 [T]를 만들 수 있습니다 감안할 때. 예컨대 :

    // Task.delay will lazily execute the supplied function when run
    val myTask: Task[String] =
      Task.delay(???)
    
    // Retry four times if myTask throws java.lang.Exception when run
    val retryTask: Task[String] =
      myTask.retry(Seq(20.millis, 50.millis, 100.millis, 5.seconds))
    
    // Run the Task on the current thread to get the result
    val result: String = retryTask.run
    
  3. ==============================

    3.여기에 하나의 가능한 구현은 다음과 같습니다

    여기에 하나의 가능한 구현은 다음과 같습니다

    def retry[T](times: Int)(fn: => T) = 
        (1 to times).view flatMap (n => try Some(fn) catch {case e: Exception => None}) headOption
    

    이처럼 사용할 수 있습니다 :

    retry(3) {
        getClient.putObject(request)
    }
    

    재시도 일부 [T] 몸이 성공적으로 처리하지 않고 아무도 경우 몸은 던지는 경우 예외를 반환합니다.

    마지막으로 예외를 보블 싶은 경우에, 당신은 매우 유사한 접근 방식을 취할 수 대신 옵션 중 하나를 사용합니다 :

    def retry[T](times: Int)(fn: => T) = {
        val tries = (1 to times).toStream map (n => try Left(fn) catch {case e: Exception => Right(e)}) 
    
        tries find (_ isLeft) match {
            case Some(Left(result)) => result
            case _ => throw tries.reverse.head.right.get
        }
    }
    

    또한, 대신 단지 마지막 예외를 필요없이 끝에서 볼 수 있듯이, 나는 그들 모두를 가지고있다. 당신이 원한다면 당신은 또한 일부 AggregatingException에서 그 (것)들을 포장 할 수 있으며 다음을 던져. (편의상, 난 그냥 마지막으로 예외를 발생)

  4. ==============================

    4.나는이 좋을 것 -

    나는이 좋을 것 -

    def retry[T](n: Int)(code: => T) : T = { 
      var res : Option[T] = None
      var left = n 
      while(!res.isDefined) {
        left = left - 1 
        try { 
          res = Some(code) 
        } catch { 
          case t: Throwable if left > 0 => 
        }
      } 
      res.get
    } 
    

    그렇습니다:

    scala> retry(3) { println("foo"); }
    foo
    
    scala> retry(4) { throw new RuntimeException("nope"); }
    java.lang.RuntimeException: nope
            at $anonfun$1.apply(<console>:7)
            at $anonfun$1.apply(<console>:7)
            at .retry(<console>:11)
            at .<init>(<console>:7)
            at .<clinit>(<console>)
            at RequestResult$.<init>(<console>:9)
            at RequestResult$.<clinit>(<console>)
            at RequestResult$scala_repl_result(<console>)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
            at java.lang.reflect.Method.invoke(Method.java:597)
            at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
            at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter....
    scala> var i = 0 ;
    i: Int = 0
    
    scala> retry(3) { i = i + 1; if(i < 3) throw new RuntimeException("meh");}
    
    scala> i
    res3: Int = 3
    

    아마 스칼라 더 관용적으로 개선 될 수 있지만 어쨌든 마음으로 전체 표준 라이브러리를 알고 독자를 필요로 한 라이너의 큰 팬이 아니다.

  5. ==============================

    5.당신은 scala.util.control.Exception 사용하여 기능적인 스타일의 생각을 표현할 수있다 :

    당신은 scala.util.control.Exception 사용하여 기능적인 스타일의 생각을 표현할 수있다 :

    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): T =
      Exception.allCatch.either(fn) match {
        case Right(v)             => v;
        case Left(e) if (n <= 1)  => throw e;
        case _                    => retry(n - 1)(fn);
      }
    

    우리가 볼 수 있듯이, 꼬리 재귀 여기에 사용할 수 있습니다.

    이 방법은 당신이 유일한 예외의 특정 부분 집합을 재 시도 할 수 있도록, 캐치 용기 변수화 재 시도의 최종 버전이 같을 수 그래서 등 파이널 라이저를 추가 할 수있는 당신에게 추가 혜택을 제공합니다 :

    /** Retry on any exception, no finalizers. */
    def retry[T](n: Int)(fn: => T): T =
      retry(Exception.allCatch[T], n)(fn);
    
    /** Parametrized retry. */
    @annotation.tailrec
    def retry[T](theCatch: Exception.Catch[T], n: Int)(fn: => T): T =
      theCatch.either(fn) match {
        case Right(v)             => v;
        case Left(e) if (n <= 1)  => throw e;
        case _                    => retry(theCatch, n - 1)(fn);
      }
    

    이, 당신은 복잡한 물건을 같이 할 수 있습니다 :

    retry(Exception.allCatch andFinally { print("Finished.") }, 3) {
      // your scode
    }
    
  6. ==============================

    6.재시라는이 도움이 될 수 있습니다 기존 라이브러리가있다, 구아바 - 재 시도라고 너무 자바 라이브러리가있다.

    재시라는이 도움이 될 수 있습니다 기존 라이브러리가있다, 구아바 - 재 시도라고 너무 자바 라이브러리가있다.

    여기에 재 시도를 사용하는 몇 가지 예입니다 :

    // retry 4 times
    val future = retry.Directly(4) { () => doSomething }
    
    // retry 3 times pausing 30 seconds in between attempts
    val future = retry.Pause(3, 30.seconds) { () => doSomething }
    
    // retry 4 times with a delay of 1 second which will be multipled
    // by 2 on every attempt
    val future = retry.Backoff(4, 1.second) { () => doSomething }
    
  7. ==============================

    7.수락 솔루션과 같은 I,하지만 예외를 확인하시기 바랍니다 것은 치명적이다 :

    수락 솔루션과 같은 I,하지만 예외를 확인하시기 바랍니다 것은 치명적이다 :

    // Returning T, throwing the exception on failure
    @annotation.tailrec
    def retry[T](n: Int)(fn: => T): T = {
      Try { fn } match {
        case Success(x) => x
        case _ if n > 1 && NonFatal(e) => retry(n - 1)(fn)
        case Failure(e) => throw e
      }
    }
    

    당신은 스레드 인터럽트 아니 일반적으로 제어 흐름 예외를 다시 시도 할, 그리고하지 않습니다 ...

  8. ==============================

    8.당신은 당신이 시도하는 예외의 제어를 원하는 경우에, 당신은 scala.util.control.Exception의 방법을 사용할 수 있습니다 :

    당신은 당신이 시도하는 예외의 제어를 원하는 경우에, 당신은 scala.util.control.Exception의 방법을 사용할 수 있습니다 :

    import java.io._
    import scala.util.control.Exception._
    
    def ioretry[T](n: Int)(t: => T) = (
      Iterator.fill(n){ failing[T](classOf[IOException]){ Option(t) } } ++
      Iterator(Some(t))
    ).dropWhile(_.isEmpty).next.get
    

    (작성, 그것은 또한 널 (null)에 다시 시도 것이다. 옵션 (t) 부분이, 널 (null)을 반환 할 대신 반복자 채우기 내부) 일부 (t를 사용하는 경우.)

    하자이이 밖으로 시도

    class IoEx(var n: Int) {
      def get = if (n>0) { n -= 1; throw new IOException } else 5
    }
    val ix = new IoEx(3)
    

    작동합니까?

    scala> ioretry(4) { ix.get }
    res0: Int = 5
    
    scala> ix.n = 3
    
    scala> ioretry(2) { ix.get }
    java.io.IOException
        at IoEx.get(<console>:20)
        ...
    
    scala> ioretry(4) { throw new Exception }
    java.lang.Exception
        at $anonfun$1.apply(<console>:21)
        ...
    

    외모 좋은!

  9. ==============================

    9.나는에 다시 시도하는 예외를 필터링 할 수 있도록 이전의 대답을 적응 결국 :

    나는에 다시 시도하는 예외를 필터링 할 수 있도록 이전의 대답을 적응 결국 :

      /**
       * Attempt 'fn' up to 'attempts' times, retrying only if 'forExceptions' returns true for retry-able exceptions.
       */
      def retry[T](attempts: Int, forExceptions: (Throwable) => Boolean)(fn: => T): T =
      {
        // toStream creates a lazily evaluated list, which we map to a try/catch block resulting in an Either
        val tries = (1 to attempts).toStream map
          {
            n =>
              try
                Left(fn)
              catch
                {
                  case e if forExceptions(e) => Right(e)
                }
          }
    
        // find the first 'Either' where left is defined and return that, or if not found, return last
        // exception thrown (stored as 'right').  The cool thing is that because of lazy evaluation, 'fn' is only
        // evaluated until it success (e.g., until Left is found)
        tries find (_ isLeft) match
        {
          case Some(Left(result)) => result
          case _ => throw tries.reverse.head.right.get
        }
    
      }
    

    두 가지 방법으로 호출 할 수 있습니다 :

    val result = retry(4, _.isInstanceOf[SomeBadException])
    {
       boom.doit()
    }
    

    또는 (반환 값에 대해 걱정하지 않는다 또한 버전을 표시) 부분 기능

        def pf: PartialFunction[Throwable, Boolean] =
        {
          case x: SomeOtherException => true
          case _ => false
        }
    
       retry(4, pf)
       {
          boom.doit()
       }
    
  10. ==============================

    10.

    //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)
    }}
    }
    
  11. ==============================

    11.이 프로젝트는 서로 다른 재시도 메커니즘에 대한 몇 가지 좋은 구현을 제공하는 것 https://github.com/hipjim/scala-retry

    이 프로젝트는 서로 다른 재시도 메커니즘에 대한 몇 가지 좋은 구현을 제공하는 것 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)
    }
    
  12. ==============================

    12.이 솔루션은 (? 누가 이유를 알고있다), 그러나 드문 시도의 경우 옵션이 될 것입니다 어떤 이유로 꼬리 재귀에 컴파일러에 의해 최적화되어 있지 않습니다 :

    이 솔루션은 (? 누가 이유를 알고있다), 그러나 드문 시도의 경우 옵션이 될 것입니다 어떤 이유로 꼬리 재귀에 컴파일러에 의해 최적화되어 있지 않습니다 :

    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()
    }
    
  13. ==============================

    13.시도 간 일시와 재사용 가능한 개체 / 방법

    시도 간 일시와 재사용 가능한 개체 / 방법

    Retry(3, 2 seconds) { /* some code */ }
    

    암호:

    object Retry {
      def apply[A](times: Int, pause: Duration)(code: ⇒ A): A = {
        var result: Option[A] = None
        var remaining = times
        while (remaining > 0) {
          remaining -= 1
          try {
            result = Some(code)
            remaining = 0
          } catch {
            case _ if remaining > 0 ⇒ Thread.sleep(pause.toMillis)
          }
        }
        result.get
      }
    }
    
  14. from https://stackoverflow.com/questions/7930814/whats-the-scala-way-to-implement-a-retry-able-call-like-this-one by cc-by-sa and MIT license