Akka - 处理阻止操作

时间:2015-09-17 12:27:55

标签: scala asynchronous akka spray

我有一个使用Scala,Akka和Spray构建的应用程序。 在应用程序中,我有一个阻止调用外部bash脚本。 它通过sys.process调用,平均运行时间约为100毫秒。

我使用Curl& amp; Apache Bench获得一些性能指标。我还使用nanoTime()来调用sys.process本身。

如果我使用单个Curl调用进行测试,应用程序将按预期执行,sys.process调用大约需要100毫秒。

当我使用具有多个同时请求的Apache Bench增加服务器上的负载时,sys.process调用开始从100ms急剧增加到接近1000ms,具体取决于负载。

通过阅读Scala文档,我认为这里的问题是阻塞调用正在使用执行上下文中的所有可用线程,这会破坏性能。

但我怎样才能最好地迎合这个?

我尝试创建自定义执行上下文并在阻塞的未来中包装调用...

implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(100))
val future = Future {
  blocking {
    sys.process.Process(...),
      new java.io.File("/var/...")).!!
  }
}

然后我必须等待将来完成使用onComplete,但这反过来似乎阻止了线程,我看不到基于Apache Bench报告的每秒请求数量的任何明显的性能提升。

上面的代码位于Actor类中的方法中,并由receive方法调用。

任何人都可以建议我应该如何构造此代码以增加线程数量并尽可能减少阻塞代码所花费的时间? 即切断操作并在操作完成后返回操作。

由于

2 个答案:

答案 0 :(得分:0)

我会异步响应以避免阻塞行为:

${project.version}

答案 1 :(得分:0)

无需为此创建新线程。

class MyActor extends Actor {      
  def recieve = {
    case Request(file) => {
        val process=sys.process.Process.....
        self ! CheckProcess(process, file)
    }
    case CheckProcess(process, file)=>{
        if(doUppkeep(process)){
            self ! CheckProcess(process, file)
        } else {
            Done(file) 
        }
    }
    case Done(file) => completeRequest(file)
    }
  def doUpKeep(process):Boolean={
     // Do wathever is needed to keep the external process running
     // avoid looping since this pethod will be called again
     // return true if process is still running.
  }
}

如果您愿意,可以查看CheckProcess消息,而不是将它们推送到消息队列。

self.context.system.scheduler.scheduleOnce(10 milliseconds, self, CheckProcess(process, file))

这将使该过程表现得更好,但每个请求可能会变慢一些。

这种模式可以将资源的使用保持在最低限度,同时使所有流程都得到了保护。