scala - Spray.io: When (not) to use non-blocking route handling? -
if thinking of production grade rest api, should use non-blocking 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 answer 'yes', guidelines in deciding should , should not blocking code, , why?
spray uses akka underlying platform, recommendations same actors (blocking needs careful management). blocking code may require threads, may:
kill actor's lightweightness: millions of actors may operate on 1 thread default. let's 1 non-blocked actor requires 0.001 threads example. 1 blocked actor (which blocking time is, let's say, 100 times more usual) take 1 thread avg (not same thread). first, more threads have - more memory loose - every blocked thread holds full callstack allocated before blocking, including references stack (so gc can't erase them). second, if have more
number_of_processors
threads - loose performance. third, if use dynamical pool - adding new thread may take significant amount of time.cause thread's starvation - may have pool filled threads, doing nothing - new tasks can't processed before blocking operation complete (0 % cpu load, 100500 messages waiting processed). may cause deadlocks. however, akka uses fork-join-pool default if blocking code managed (surrounded
scala.concurrent.blocking
-await.result
have such surrounding inside ) - prevent starvation cost of creating new thread instead of blocked one, won't compensate other problems.traditionally cause deadlocks, it's bad design
if code blocking outside, can surround future:
import scala.concurrent._ val f = future { somenonblockingcode() blocking { //mark thread "blocked" fork-join-pool may create 1 compensate someblocking() } }
inside separate actor:
f pipeto sender //will send result `sender` actor
inside spray routing:
oncomplete(f) { .. }
it's better execute such futures inside separate pool/dispatcher (fork-join-pool based).
p.s. alternative futures (they may not convinient design perspectvive) may consider akka i/o, continuations/coroutines, actor's pools (also inside separate dispatcher), disruptor etc.
Comments
Post a Comment