什么是Scala的方式来实现像这样的可重试的电话? [英] What's the Scala way to implement a retry-able call like this one?

查看:315
本文介绍了什么是Scala的方式来实现像这样的可重试的电话?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

仍然是Scala的新手,我现在正在寻找一种方法来实现下面的代码:

  @覆盖
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
}
};

}

什么是实现相同功能的最佳方式RetryableService实现但是在Scala中?



它基本上调用调用方法N次,如果所有的都失败,成功它继续前进。这个不返回任何东西,但我有另一个版本,允许返回一个值(所以,我有两个类在Java),我相信我可以做一个单一的类/函数在Scala。



有任何想法?



EDIT



在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 cause exception%s,x,e.getMessage());

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

}

}

try {
this.ensure();
} catch(Exception e){
log.error(e,Failed while Confirm 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;

}


解决方案

递归+

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

用法如下:

  retry 
//插入可能失败的代码
}

:由 @themel 的答案启发的细微变化。少一行代码: - )

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

再次编辑:递归困扰我,因为它添加了对堆栈跟踪的几个调用。由于某种原因,编译器不能优化catch处理程序中的尾递归。尾递归不在catch处理程序,但是,优化只是很好: - )

  @ 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无=> retry(n-1)(fn)
}
}



< >再次编辑:显然,我会让它成为一个爱好,以继续回来,并添加这个答案的替代品。这里有一个尾递归版本,比使用 Option 稍微简单些,但是使用 return 来短路函数不是惯用的Scala。

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

Scala 2.10更新。正如我的爱好,我偶尔回顾这个答案。 Scala 2.10,介绍 Try ,它提供了一种干净的实现方式以尾递归方式重试。

  //返回T,失败时抛出异常
@ annotation.tailrec
def retry [T](n:Int)(fn:=> T):T = {
util.Try {fn} match {
case util.Success > x
case _ if n> 1 => retry(n-1)(fn)
case util.Failure(e)=> throw e
}
}

//返回一个Try [T]包装器
@ annotation.tailrec
def retry [T](n:Int )(fn:=> T):util.Try [T] = {
util.Try {fn} match {
case x:util.Success [T] =& x
case _ if n> 1 => retry(n-1)(fn)
case fn => fn
}
}


Still the newbie in Scala and I'm now looking for a way to implement the following code on it:

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

}

What would be the best way to implement the same funcionality that RetryableService implements but in Scala?

It basically calls the call method N times, if all of them fail the exception is then raised, if they succeed it moves on. This one does not return anything but then I have another version that allows for returning a value (so, i have two classes in Java) and I believe I could do with a single class/function in Scala.

Any ideas?

EDIT

Current implementation in java is as follows:

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;

}

解决方案

Recursion + first class functions by-name parameters == awesome.

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

Usage is like this:

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

Edit: slight variation inspired by @themel's answer. One fewer line of code :-)

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

Edit Again: The recursion bothered me in that it added several calls to the stack trace. For some reason, the compiler couldn't optimize tail recursion in the catch handler. Tail recursion not in the catch handler, though, optimizes just fine :-)

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

Edit yet again: Apparently I'm going to make it a hobby to keep coming back and adding alternatives to this answer. Here's a tail-recursive version that's a bit more straightforward than using Option, but using return to short-circuit a function isn't idiomatic Scala.

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

Scala 2.10 update. As is my hobby, I revisit this answer occasionally. Scala 2.10 as introduced Try, which provides a clean way of implementing retry in a tail-recursive way.

// 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 x: util.Success[T] => x
    case _ if n > 1 => retry(n - 1)(fn)
    case fn => fn
  }
}

这篇关于什么是Scala的方式来实现像这样的可重试的电话?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆