This document summarizes steps taken to optimize CPU usage of a JVM running an Akka application using Spray. The application was not utilizing the CPU effectively, with throughput very low. Understanding Akka's asynchronous, actor-based architecture and obtaining thread dumps revealed the logging was blocking threads. The solution was to configure logging to occur asynchronously within actors to avoid blocking and better utilize the CPU.
4. spray is an open-source toolkit for building REST/HTTP-
based integration layers on top of Scala and Akka. Being
asynchronous, actor-based, fast, lightweight, modular and
testable it's a great way to connect your Scala applications
to the world.
13. "default-akka.actor.default-dispatcher-24" prio=5 tid=0x00007fef1e054800 nid=0x9003 waiting for monitor entry
[0x0000000114423000]
java.lang.Thread.State: BLOCKED (on object monitor)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:211)
- waiting to lock <0x0000000701d22d00> (a ch.qos.logback.core.spi.LogbackLock)
at ch.qos.logback.core.rolling.RollingFileAppender.subAppend(RollingFileAppender.java:175)
at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:103)
at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:88)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:48)
at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:272)
at ch.qos.logback.classic.Logger.callAppenders(Logger.java:259)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:441)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:395)
Blockしてる
14. "default-akka.actor.default-dispatcher-24" prio=5 tid=0x00007fef1e054800 nid=0x9003 waiting for monitor entry
[0x0000000114423000]
java.lang.Thread.State: BLOCKED (on object monitor)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:211)
- waiting to lock <0x0000000701d22d00> (a ch.qos.logback.core.spi.LogbackLock)
at ch.qos.logback.core.rolling.RollingFileAppender.subAppend(RollingFileAppender.java:175)
at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:103)
at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:88)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:48)
at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:272)
at ch.qos.logback.classic.Logger.callAppenders(Logger.java:259)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:441)
at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:395)
Lockを取得しよう
として待ってる
"default-akka.actor.default-dispatcher-18" prio=5 tid=0x00007fef1a99f800 nid=0x7f03 waiting for monitor entry
[0x0000000113e11000]
java.lang.Thread.State: BLOCKED (on object monitor)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:211)
- locked <0x0000000701d22d00> (a ch.qos.logback.core.spi.LogbackLock)
at ch.qos.logback.core.rolling.RollingFileAppender.subAppend(RollingFileAppender.java:175)
Lockしてる
17. • Do the blocking call within an actor (or a set of actors managed by a
router [Java, Scala]), making sure to configure a thread pool which is
either dedicated for this purpose or sufficiently sized.
!
• Do the blocking call within a Future, ensuring an upper bound on the
number of such calls at any point in time (submitting an unbounded
number of tasks of this nature will exhaust your memory or thread
limits).
!
• Do the blocking call within a Future, providing a thread pool with an
upper limit on the number of threads which is appropriate for the
hardware on which the application runs.
!
• Dedicate a single thread to manage a set of blocking resources (e.g. a
NIO selector driving multiple channels) and dispatch events as they
occur as actor messages.
The non-exhaustive list of adequate solutions to the
“blocking problem”