Mau*_*res 51 java functional-programming 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);
}
};
}
Run Code Online (Sandbox Code Playgroud)
在Scala中实现RetryableService实现的相同功能的最佳方法是什么?
它基本上调用了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;
}
Run Code Online (Sandbox Code Playgroud)
lee*_*777 168
递归+ 第一类函数按名称参数==真棒.
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
Run Code Online (Sandbox Code Playgroud)
用法是这样的:
retry(3) {
// insert code that may fail here
}
Run Code Online (Sandbox Code Playgroud)
编辑:受@themel的回答启发的微小变化.少一行代码:-)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
Run Code Online (Sandbox Code Playgroud)
再次编辑:递归困扰我,因为它添加了几次调用堆栈跟踪.由于某种原因,编译器无法在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 None => retry(n - 1)(fn)
}
}
Run Code Online (Sandbox Code Playgroud)
再次编辑:显然我会把它作为一个爱好继续回来并添加这个答案的替代品.这是一个尾递归版本比使用更简单Option,但使用return短路函数不是惯用的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)
}
Run Code Online (Sandbox Code Playgroud)
Scala 2.10更新.作为我的爱好,我偶尔会重温这个答案.Scala 2.10引入了Try,它提供了一种以尾递归方式实现重试的简洁方法.
// 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
}
}
Run Code Online (Sandbox Code Playgroud)
有一种方法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]
Run Code Online (Sandbox Code Playgroud)
给定a Task[T],您可以创建一个新的Task[T],它将重试一定次数,其中重试之间的延迟由delays参数定义.例如:
// 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
Run Code Online (Sandbox Code Playgroud)
这是一个可能的实现:
def retry[T](times: Int)(fn: => T) =
(1 to times).view flatMap (n => try Some(fn) catch {case e: Exception => None}) headOption
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
retry(3) {
getClient.putObject(request)
}
Run Code Online (Sandbox Code Playgroud)
retrySome[T]如果正确处理了body并且Nonebody只是抛出异常,也会返回.
如果你想填补最后一个异常,那么你可以采取非常类似的方法,但使用Either而不是Option:
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
}
}
Run Code Online (Sandbox Code Playgroud)
另外,正如你所看到的那样,最后,我没有最后一个例外,而是拥有它们.所以AggregatingException如果你愿意,你也可以将它们包装成一些,然后扔掉.(为简单起见,我只抛出最后一个异常)