Spray.io:何时(不)使用非阻塞路线处理? [英] Spray.io: When (not) to use non-blocking route handling?

查看:64
本文介绍了Spray.io:何时(不)使用非阻塞路线处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我们正在考虑生产级的REST API,是否应尽可能使用非阻塞,例如

If we are thinking of production grade REST API, should we use non-blocking as much as possible, e.g.

def insertDbAsync(rows: RowList): Future[Unit] = ...
...
val route =
path("database" / "insertRowList") {
  post {
    entity(as[RowList]) { rows =>
      log.info(s"${rows.length} rows received")
      val async = insertDbAsync(rows)
      onComplete(async) {
        case Success(response) =>
          complete("success")
        case Failure(t) =>
          complete("error")
      }
    }
  }
}

我在想答案很可能是是,但是在确定什么应该和不应该成为阻塞代码以及为什么时有什么指导原则?

I'm thinking that the answer will most likely be a 'yes', but what are some guidelines in deciding what should and should not be a blocking code, and why?

推荐答案

Spray使用Akka作为基础平台,因此建议与针对参与者的建议相同(阻止需要仔细管理)。阻塞代码可能需要太多线程,这可能会:

Spray uses Akka as underlying platform, so recommendations are same as for actors (Blocking Needs Careful Management). Blocking code may require too much threads, which may:


  • 杀死actor的轻量级:默认情况下,数百万个actor可能在一个线程上操作。假设一个不受阻碍的角色需要0.001个线程。一个被阻止的演员(阻止时间比平常多100倍)将平均消耗1个线程(并非总是同一个线程)。首先,拥有的线程越多-释放的内存就越多-每个阻塞的线程都会在阻塞之前拥有分配的完整调用堆栈,包括堆栈中的引用(因此GC无法擦除它们)。其次,如果您有多个 number_of_processors 个线程,则会降低性能。第三,如果您使用一些动态池-添加新线程可能会花费大量时间。

  • kill actor's lightweightness: millions of actors may operate on one thread by default. Let's say one non-blocked actor requires 0.001 threads for example. One blocked actor (which blocking time is, let's say, 100 times more than usual) will take 1 thread avg (not always same thread). First, The more threads you have - the more memory you loose - every blocked thread holds full callstack allocated before blocking, including references from stack (so GC can't erase them). Second, if you have more than number_of_processors threads - you will loose the performance. Third, if you use some dynamical pool - adding new thread may take some significant amount of time.

由于线程的饥饿-您的池中可能充满了线程,该操作什么都不做-因此无法在阻止操作完成之前处理新任务(0%CPU负载,但有100500条消息等待处理)。甚至可能导致死锁。但是,Akka默认情况下使用Fork-Join-Pool,因此,如果您的阻止代码是托管的(以 scala.concurrent.blocking - Await.result <包围) / code>内有这样的内容)-通过创建新线程(而不是阻塞一个线程)来防止饥饿,但这并不能弥补其他问题。

cause thread's starvation - you may have pool filled with threads, which doing nothing - so new tasks can't be processed before blocking operation complete (0 % CPU load, but 100500 messages waiting to be processed). It may even cause deadlocks. However, Akka uses Fork-Join-Pool by default so if your blocking code is managed (surrounded with scala.concurrent.blocking - Await.result have such surrounding inside ) - it will prevent starvation by cost of creating new thread instead of blocked one, but it won't compensate other problems.

传统上会导致死锁,因此对设计不利。

traditionally cause deadlocks, so it's bad for design

如果代码从外部阻塞,则可以包围

If code is blocking from outside, you can surround it with future:

 import scala.concurrent._
 val f = Future {
     someNonBlockingCode()
     blocking { //mark this thread as "blocked" so fork-join-pool may create another one to compensate
        someBlocking()
     }  
 }

内部独立演员:

 f pipeTo sender //will send the result to `sender` actor

内部喷雾路由:

 onComplete(f) { .. }

最好在单独的池/分派器中执行此类期货(分叉-

It's better to execute such futures inside separate pool/dispatcher (fork-join-pool based).

PS作为期货的替代方案(从设计角度来看它们可能不太方便),您可以考虑 Akka I / O ,Continuations / 协程,Actor的(也在单独的调度程序中),Disruptor等

P.S. As an alternative to futures (they may not be much convinient from design perspectvive) you may consider Akka I/O, Continuations/Coroutines, Actor's pools (also inside separate dispatcher), Disruptor etc.

这篇关于Spray.io:何时(不)使用非阻塞路线处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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