don't block - how to mess up akka and spray

27
Don't Block How to Mess Up Akka and Spray Zach Cox - @zcox - [email protected] iascala - Feb 2014

Upload: zach-cox

Post on 05-Jul-2015

4.379 views

Category:

Technology


0 download

DESCRIPTION

Presented at Feb 2014 Iowa Scala Enthusiasts meetup: http://www.meetup.com/ia-scala/events/158419152/ Git repo contains runnable code samples not in slides: https://github.com/zcox/iascala-dont-block Actors and Futures make writing concurrent, asynchronous, non-blocking, reactive applications much simpler than dealing with threads, locks and synchronization directly. However, it’s also really easy to inadvertently block in the wrong place and bring your entire system to a grinding halt. We’ll demonstrate some of the common, simple ways to destroy Akka and Spray applications, examine the internals of these libraries to explore what’s happening, and discuss some best practices they provide for avoiding these problems.

TRANSCRIPT

Page 1: Don't Block - How to Mess Up Akka and Spray

Don't BlockHow to Mess Up Akka and Spray

Zach Cox - @zcox - [email protected] - Feb 2014

Page 3: Don't Block - How to Mess Up Akka and Spray

PurposeScala provides great concurrency toolsVery easy to mess upDemonstrate how blocking can prevent processingProvide solutions

Page 4: Don't Block - How to Mess Up Akka and Spray

Things We Will BlockThreads (Java)Futures (Scala)Actors (Akka)Spray

Page 5: Don't Block - How to Mess Up Akka and Spray

SpoilerAll about blocking threads

Page 6: Don't Block - How to Mess Up Akka and Spray

What is Blocking?Code that takes a long time to runNetwork I/O

HTTP requestsDatabase access

File I/OReally heavy computationNothing after the blocking function can run on this thread untilit is done

Page 7: Don't Block - How to Mess Up Akka and Spray

Java: Runnable, Executortrait Runnable { def run(): Unit}

trait Executor { def execute(command: Runnable): Unit}

Page 8: Don't Block - How to Mess Up Akka and Spray

ThreadPoolExecutorclass ThreadPoolExecutor extends Executor { val pool: Set[Thread] = Set.empty val tasks: BlockingQueue[Runnable] = ???

def execute(command: Runnable): Unit = { //run command on new thread, or queue it, or reject it, depending on settings... }}

Page 9: Don't Block - How to Mess Up Akka and Spray

How to block ThreadPoolExecutorExecute tasks that block in run() methodAll threads in pool get blockedTasks are queued before execution (until queue is full)How it is supposed to work...Need to be aware of the blocking though

Page 10: Don't Block - How to Mess Up Akka and Spray

ForkJoinPoolExecutorService implementationscala.concurrent.forkjoinjava.util.concurrent (Java7)Sub-dividing tasks, work queue, worker thread pool, work stealing, ...https://www.google.com/search?q=java+fork+join

Page 11: Don't Block - How to Mess Up Akka and Spray

How to block ForkJoinPoolSame as ThreadPoolExecutor

Page 12: Don't Block - How to Mess Up Akka and Spray

Solutions for Blocked Executorval executor = Executors.newFixedThreadPool(moreThreads)

val executor1 = Executors.newFixedThreadPool(pool1Size)val executor2 = Executors.newFixedThreadPool(pool2Size)

Page 13: Don't Block - How to Mess Up Akka and Spray

Java: Callable, Future,ExecutorService

trait Callable[V] { def call(): V}

trait Future[V] { def isDone(): Boolean def get(): V}

trait ExecutorService extends Executor { def submit(task: Runnable): Future[_] def submit[T](task: Callable[T]): Future[T]}

Page 14: Don't Block - How to Mess Up Akka and Spray
Page 15: Don't Block - How to Mess Up Akka and Spray

Future[T]Monad that eventually contains either:

A value of type T (success)A Throwable (failure)

Future[T] is the read-side; Promise[T] is the write-sideValue is computed and placed into promise/future on some otherthread (usually)//TODO compelling example of Future...

Page 16: Don't Block - How to Mess Up Akka and Spray

ExecutionContextRuns code that asynchronously completes futuresScala version of Executor/ExecutorService

Implementations usually wrap oneExecutor => ExecutionContextExecutorService => ExecutionContext

Future.apply runs body function using ExecutionContextWraps the body function in a RunnableExecutes that Runnable on an ExecutionContextThat Runnable completes a Promise

ExecutionContext.globalTries to use ForkJoinPoolFalls back to ThreadPoolExecutor

Page 17: Don't Block - How to Mess Up Akka and Spray

How to block Future/ExecutionContextExecutionContext just wraps an Executor/ExecutorServiceExecutionContext.global usually wraps either aForkJoinPool or a ThreadPoolExecutorWe already know tldr function passed to Future.apply blocks the underlying thread,exhaust the pool

how to block those

Page 18: Don't Block - How to Mess Up Akka and Spray

Solutions for blockedFuture/ExecutionContext

java ... \ -Dscala.concurrent.context.minThreads=8 \ -Dscala.concurrent.context.numThreads=16 \ -Dscala.concurrent.context.maxThreads=24 \ ...

implicit val c = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(123))

implicit val defaultContext = ExecutionContext.globalval databaseContext = ExecutionContext.fromExecutor(null)

Future("default processing")Future("database operations")(databaseContext)

Page 19: Don't Block - How to Mess Up Akka and Spray

Actors and Dispatchersactor ! msgmsg placed in actor''s Mailbox queueMailbox is a RunnableMailbox executed on dispatcher''s ExecutorServiceBy default, all actors use the same default dispatcher

Page 20: Don't Block - How to Mess Up Akka and Spray

How to Block ActorsRemember ? Do that.Block in Actor.receiveEnough blocked actors will exhaust the dispatcher''s thread pool

how to block Executor

Page 21: Don't Block - How to Mess Up Akka and Spray

Solutions for Blocked Actorsblocking2-dispatcher { type = Dispatcher executor = "fork-join-executor"}

val blocking = system.actorOf(Props[BlockingActor] .withRouter(FromConfig()) .withDispatcher("blocking2-dispatcher"), "blocking2")

my-dispatcher { type = Dispatcher executor = "fork-join-executor" fork-join-executor { parallelism-factor = 10.0 parallelism-max = 100 }}

Page 22: Don't Block - How to Mess Up Akka and Spray

Fun FactMessageDispatcher is an ExecutionContext

val jdbcContext = system.dispatchers.lookup("jdbc-dispatcher")Future(useTheDatabase)(jdbcContext)

Page 23: Don't Block - How to Mess Up Akka and Spray

Sprayspray-io/akka-io: Java NIO + Actorsspray-can: HTTP server & client built on spray-iospray-routing: HTTP request/response DSL

Page 24: Don't Block - How to Mess Up Akka and Spray

How to Block SprayBuilt on actorsWe know SimpleRoutingApp uses a single actor to route all requests (!!!)That actor synchronously calls runRoute - easily blocked!That actor uses default Akka dispatcher - easily blocked!

how to block those

Example code

Page 25: Don't Block - How to Mess Up Akka and Spray

Solutions for Blocking SprayDo not call blocking functions directly in routes

Instead detach to Future or ActorSpray can complete a response using a Future

Use separate dispatchersGive Spray its own dispatcher(s)Give your blocking code its own dispatcher(s)

Page 26: Don't Block - How to Mess Up Akka and Spray

Java NIO and spray-clientBlocking I/O: One thread per socketNon-blocking I/O: One thread, many socketsNo network I/O (web service client, database, etc) libraries use it!Except spray-client...Start writing !Other protocols (TCP, SMTP, XMPP, various DBs, etc) can use

Scala web service clients using spray-can

akka-io

Page 27: Don't Block - How to Mess Up Akka and Spray

Banno is HiringScala Developers!

[email protected]