Akka is the platform for the next generation event-driven, scalable and fault-tolerant architectures on the JVM
We believe that writing correct concurrent, fault-tolerant and scalable applications is too hard. Most of the time it's because we are using the wrong tools and the wrong level of abstraction.
Akka is here to change that.
Using the Actor Model together with Software Transactional Memory we raise the abstraction level and provides a better platform to build correct concurrent and scalable applications.
For fault-tolerance we adopt the "Let it crash" / "Embrace failure" model which have been used with great success in the telecom industry to build applications that self-heals, systems that never stop.
Actors also provides the abstraction for transparent distribution and the basis for truly scalable and fault-tolerant applications.
Akka is Open Source and available under the Apache 2 License.
3. The devil is in
the state
3
Wednesday, December 30, 2009
4. Wrong,
let me rephrase
4
Wednesday, December 30, 2009
5. The devil is in
the mutable state
5
Wednesday, December 30, 2009
6. Definitions
&
Philosophy
6
Wednesday, December 30, 2009
7. What is a Value?
A Value is something that
does not change
Discussion based on
http://clojure.org/state
by Rich Hickey
Wednesday, December 30, 2009
8. What is an Identity?
A stable logical entity
associated with a
series of different Values
over time
Wednesday, December 30, 2009
9. What is State?
The Value
an entity with a
specific Identity
has at a particular
point in time
Wednesday, December 30, 2009
10. How do we know if
something has State?
If a function is invoked with
the same arguments at
two different points in time
and returns different values...
...then it has state
Wednesday, December 30, 2009
11. The Problem
The unification Of
Identity & Value
They are not the same
Wednesday, December 30, 2009
12. We need to separate Identity & Value
...add a level of indirection
Software Transactional Memory
Managed References
Message-Passing Concurrency
Actors/Active Objects
Dataflow Concurrency
Dataflow (Single-Assignment) Variables
Wednesday, December 30, 2009
13. The problems with
Shared State
Concurrency
Wednesday, December 30, 2009
14. Shared-State Concurrency
> Concurrent access to shared, mutable state.
> Protect mutable state with locks
> The
Java
C#
C/C++
Ruby
Python
etc.
Wednesday, December 30, 2009
15. Shared-State
Concurrency is
incredibly hard
> Inherentlyvery hard to use reliably
> Even the experts get it wrong
Wednesday, December 30, 2009
16. Example of
Shared-State Concurrency
Transfer funds
between bank accounts
Wednesday, December 30, 2009
17. Account
public class Account {
private int balance;
public void withdraw(int amount) {
balance ‐= amount;
}
public void deposit(int amount) {
balance += amount;
}
}
Not thread-safe
Wednesday, December 30, 2009
18. Let’s make it thread-safe
public class Account {
private int balance;
public synchronized void withdraw(int amount) {
balance ‐= amount;
}
public synchronized void deposit(int amount) {
balance += amount;
}
}
Thread-safe right?
Wednesday, December 30, 2009
19. It’s still broken
Transfers are not atomic
Wednesday, December 30, 2009
20. Let’s write an
atomic transfer method
public class Account {
...
public synchronized void transferTo(
Account to, double amount) {
this.withdraw(amount);
to.deposit(amount);
}
...
}
This will work right?
Wednesday, December 30, 2009
21. Let’s transfer funds
Account alice = ...
Account bob = ...
// in one thread
alice.transferTo(bob, 10.0D);
// in another thread
bob.transferTo(alice, 3.0D);
Wednesday, December 30, 2009
22. Might lead to
DEADLOCK
Darn, this is really hard!!!
Wednesday, December 30, 2009
23. We need to enforce lock ordering
> How?
> Javawon’t help us
> Need to use code convention (names etc.)
> Requires knowledge about the internal state
and implementation of Account
> …runs counter to the principles of
encapsulation in OOP
> Opens up a Can of Worms
Wednesday, December 30, 2009
24. The problem with locks
Locks do not compose
Taking too few locks
Taking too many locks
Taking the wrong locks
Taking locks in the wrong order
Error recovery is hard
Wednesday, December 30, 2009
25. It’s just
too hard
Wednesday, December 30, 2009
26. Java bet on the
wrong horse?
Perhaps,
but we’re not
completely screwed
There are alternatives
Wednesday, December 30, 2009
27. We need better
and more
high-level
abstractions
Wednesday, December 30, 2009
28. Alternative Paradigms
>Software Transactional
Memory (STM)
>Message-Passing Concurrency (Actors)
>Dataflow Concurrency
28
Wednesday, December 30, 2009
30. Actors
• Originates in a 1973 paper by Carl
Hewitt
• Implemented in Erlang, Occam, Oz
• Encapsulates state and behavior
• Closer to the definition of OO than
classes
Wednesday, December 30, 2009
31. Alan Kay
(father of SmallTalk and OOP)
“OOP to me means only messaging,
local retention and protection and
hiding of state-process, and
extreme late-binding of all things.”
“Actually I made up the term
“object-oriented”, and I can tell you
I did not have C++ in mind.”
Replace C++ with Java or C#
Wednesday, December 30, 2009
32. Actors
• Implements Message-Passing Concurrency
• Share NOTHING
• Isolated lightweight processes
• Communicates through messages
• Asynchronous and non-blocking
Wednesday, December 30, 2009
33. Actor Model of
Concurrency
• No shared state
… hence, nothing to synchronize.
• Each actor has a mailbox (message queue)
Wednesday, December 30, 2009
34. Actor Model of
Concurrency
• Non-blocking send
• Blocking receive
• Messages are immutable
• Highly performant and scalable
• SEDA-style (Staged Event-Driven Architecture)
Wednesday, December 30, 2009
35. Actor Model of
Concurrency
• Easier to reason about
• Raised abstraction level
• Easier to avoid
–Race conditions
–Deadlocks
–Starvation
–Live locks
Wednesday, December 30, 2009
37. Two different models
• Thread-based
• Event-based
–Very lightweight
–Can easily create millions on a single
workstation (6.5 million on 4 G RAM)
Wednesday, December 30, 2009
38. Actor libs for the JVM
> Akka (Java/Scala)
> Kilim (Java)
> Jetlang (Java)
> Actor’s Guild (Java)
> ActorFoundry (Java)
> Actorom (Java)
> FunctionalJava (Java)
> GParallelizer (Groovy)
> Fan Actors (Fan)
Wednesday, December 30, 2009
44. Reply
class SomeActor extends Actor {
def receive = {
case User(name) =>
// use implicit sender
sender.get ! (“Hi ” + name)
}
}
Wednesday, December 30, 2009
45. Reply
class SomeActor extends Actor {
def receive = {
case User(name) =>
// use reply
reply(“Hi ” + name)
}
}
Wednesday, December 30, 2009
46. Send: !!
// uses Future with default timeout
val resultOption = actor !! Message
val result =
resultOption getOrElse defaultResult
// uses Future with explicit timeout
(actor !! (Message, 1000)).getOrElse(
throw new Exception(“Timed out”))
Wednesday, December 30, 2009
47. Reply
class SomeActor extends Actor {
def receive = {
case User(name) =>
// use reply
reply(“Hi ” + name)
}
}
Wednesday, December 30, 2009
48. Reply
class SomeActor extends Actor {
def receive = {
case User(name) =>
// store away the sender future
// to resolve later or
// somewhere else
... = senderFuture
}
}
Wednesday, December 30, 2009
49. Start / Stop
actor.start
actor.stop
spawn(classOf[MyActor])
// callback
override def shutdown = {
... // clean up before shutdown
}
Wednesday, December 30, 2009
50. Active Objects: Java
public class Counter {
private int counter = 0;
public void count() {
counter++;
System.out.println(counter);
}
}
Wednesday, December 30, 2009
53. Active Objects
class Counter {
private var counter = 0
def count = {
counter += 1
println(counter)
}
}
Wednesday, December 30, 2009
54. Create: POSO
val counter = ActiveObject.newInstance(
classOf[Counter], 1000)
Wednesday, December 30, 2009
55. Send
counter.count
Wednesday, December 30, 2009
56. @oneway
class Counter {
private var counter = 0
@oneway def count = {
counter += 1
println(counter)
}
}
Wednesday, December 30, 2009
57. Immutable messages
// define the case class
case class Register(user: User)
// create and send a new case class message
actor ! Register(user)
// tuples
actor ! (username, password)
// lists
actor ! List(“bill”, “bob”, “alice”)
Wednesday, December 30, 2009
58. Actors: config
<akka>
version = "0.6"
<actor>
timeout = 5000
serialize‐messages = off
</actor>
</akka>
Wednesday, December 30, 2009
60. Dispatchers
class Dispatchers {
def newThreadBasedDispatcher(actor: Actor)
def newExecutorBasedEventDrivenDispatcher(name:String)
...
}
Wednesday, December 30, 2009
61. Set dispatcher
class MyActor extends Actor {
dispatcher = Dispatchers
.newThreadBasedDispatcher(this)
...
}
actor.dispatcher = dispatcher // before started
Wednesday, December 30, 2009
62. EventBasedDispatcher
Fluent DSL
val dispatcher =
Dispatchers.newExecutorBasedEventDrivenDispatcher
.withNewThreadPoolWithBoundedBlockingQueue(100)
.setCorePoolSize(16)
.setMaxPoolSize(128)
.setKeepAliveTimeInMillis(60000)
.setRejectionPolicy(new CallerRunsPolicy)
.buildThreadPool
Wednesday, December 30, 2009
63. When to use which
dispatcher?
Wednesday, December 30, 2009
64. Thread-based actors
• One thread per Actor
• Good:
• Threads (actors) don’t block each other
• Good for long-running tasks
• Bad:
• Poor scalability
• Bad for short running tasks
• Use for a limited number of Actors
• Use for low frequency of messages
Wednesday, December 30, 2009
65. Event-based actors
• Backed by thread pool
• Lightweight:
• Can create millions of Actors
• 6.5 million on 4 G RAM
• Best scalability and performance
• 2 million messages in 8 seconds
Wednesday, December 30, 2009
66. Single threaded event-based actors
• Best performance
• Millions of Actors
• Bad:
• one actor can block
all other actors
• Does not take
advantage of multicore
Wednesday, December 30, 2009
67. MessageQueues
Unbounded LinkedBlockingQueue
Bounded LinkedBlockingQueue
Bounded ArrayBlockingQueue
Bounded SynchronousQueue
Plus different options per queue
Wednesday, December 30, 2009
76. Supervision
class Supervisor extends Actor {
trapExit = List(classOf[Throwable])
faultHandler =
Some(OneForOneStrategy(5, 5000))
def receive = {
case Register(actor) =>
link(actor)
}
}
Wednesday, December 30, 2009
77. Manage failure
class FaultTolerant extends Actor {
...
override def preRestart(reason: AnyRef) = {
... // clean up before restart
}
override def postRestart(reason: AnyRef) = {
... // init after restart
}
}
Wednesday, December 30, 2009
78. Declarative config
RestartStrategy(
AllForOne, // restart policy
10, // max # of restart retries
5000) // within time in millis
LifeCycle(
// Permanent: always be restarted
// Temporary: restarted if exited through ERR
Permanent)
Wednesday, December 30, 2009
83. Remote Server
// use host & port in config
RemoteNode.start
RemoteNode.start(classLoader)
RemoteNode.start(
"localhost", 9999)
RemoteNode.start(
"localhost", 9999, classLoader)
Wednesday, December 30, 2009
84. Remote Server
// use host & port in config
val server = new RemoteServer
server.start("localhost", 9999)
Wednesday, December 30, 2009
90. STM: overview
> See the memory (heap and stack) as a
transactional dataset
> Similar to a database
begin
commit
abort/rollback
> Transactions are retried automatically upon
collision
> Rolls back the memory on abort
Wednesday, December 30, 2009
95. Managed References
Typical OO - Direct
• Typical OO: direct Mutable Objects objects
references to access to mutable
foo
:a ?
:b ?
:c 42
:d ?
:e 6
Clojure - and value
• Unifies identity Indirect references
• Managed Reference: separates Identity & Value
• Anything can change at any time
• Consistency is a user problem Objects
to Immutable
• Encapsulation doesn’t solve concurrency:a
foo "fred"
problems :b "ethel"
@foo :c 42
:d 17
:e 6
Copyright Rich Hickey 2009
Wednesday, December 30, 2009
• Separates identity and value
96. Managed References
• Separates Identity from Value
- Values are immutable
- Identity (Ref) holds Values
• Change is a function
• Compare-and-swap (CAS)
• Abstraction of time
• Can only be altered within a transaction
Wednesday, December 30, 2009
97. Managed References
val ref = TransactionalState.newRef(
HashTrie[String, User]())
val users = ref.get
val newUsers = users + // creates new HashTrie
(“bill” ‐> new User(“bill”, “secret”)
ref.swap(newUsers)
Wednesday, December 30, 2009
98. Managed References
val usersRef = TransactionalState.newRef(
HashTrie[String, User]())
for (users <‐ usersRef) {
users + (name ‐> user)
}
val user = for (users <‐ usersRef) yield {
users(name)
}
Wednesday, December 30, 2009
99. Managed References
for {
users <‐ usersRef
user <‐ users
roles <‐ rolesRef
role <‐ roles
if user.hasRole(role)
} {
... // do stuff
}
Wednesday, December 30, 2009
100. Managed References
convenience classes
// wraps a Ref with a HashTrie
val users = TransactionalState.newMap[String, User]
// wraps a Ref with a Vector
val users = TransactionalState.newVector[User]
Wednesday, December 30, 2009
101. Persistent
datastructures
• Immutable
• Change is a function
• Old version still available after change
• Very fast with performance guarantees (near
constant time)
• Thread safe
• Iteration safe
Wednesday, December 30, 2009
103. Structural sharing
with path copying
Approach
Path Copying
• Programming with values is critical
HashMap
HashMap
int count 16
int count 15
INode root
INode root
• By eschewing morphing in place, we just
need to manage the succession of values
(states) of an identity
• A timeline coordination problem
• Several semantics possible
• Managed references
• Variable-like cells with coordination
semantics Hickey 2009
Copyright Rich
Wednesday, December 30, 2009
104. Persistent datastructures
ble
HashTrie
oordination
import se.scalablesolutions.akka.collection._
val hashTrie = new HashTrie[K, V]
// API (+ extends Map)
def get(key: K): V
def +[A >: V](pair: (K, A)): HashTrie[K, A]
def ‐(key: K): HashTrie[K, A]
def empty[A]: HashTrie[K, A]
Wednesday, December 30, 2009
105. Persistent datastructures
ble
Vector
oordination
import se.scalablesolutions.akka.collection._
val vector = new Vector[T]
// API (+ extends RandomAccessSeq)
def apply(i: Int): T
def +[A >: T](obj: A): Vector[A]
def pop: HashTrie[K, A] // remove tail
def update[A >: T](i: Int, obj: A): Vector[A]
Wednesday, December 30, 2009
106. Akka STM
• Transactional Memory
- Atomic, Consistent, Isolated (ACI)
- MVCC
- Rolls back in memory and retries
automatically on clash
• Works together with Managed References
• Map, Vector and Ref abstraction
Wednesday, December 30, 2009
107. STM: declarative API
class UserRegistry extends Transactor {
private lazy val storage =
TransactionalState.newMap[String, User]
def receive = {
case NewUser(user) =>
storage + (user.name ‐> user)
...
}
}
Wednesday, December 30, 2009
108. STM: declarative API
class UserRegistry extends Actor {
makeTransactionRequired
private lazy val storage =
TransactionalState.newMap[String, User]
def receive = {
case NewUser(user) =>
storage + (user.name ‐> user)
...
}
}
Wednesday, December 30, 2009
109. STM: declarative Java API
@transactionrequired
class UserRegistry {
}
Wednesday, December 30, 2009
110. STM: high-order fun API
import se.scalablesolutions.akka.stm.Transaction._
atomic {
.. // do something within a transaction
}
atomic(maxNrOfRetries) {
.. // do something within a transaction
}
atomicReadOnly {
.. // do something within a transaction
}
Wednesday, December 30, 2009
111. STM: high-order fun API
import se.scalablesolutions.akka.stm.Transaction._
atomically {
.. // try to do something
} orElse {
.. // if tx clash; try do do something else
}
Wednesday, December 30, 2009
112. STM: monadic API
val userStorage =
TransactionalState.newMap[String, User]
for (tx <‐ Transaction()) {
userStorage.put(user.name, user)
}
Wednesday, December 30, 2009
113. STM: monadic API
val userStorage =
TransactionalState.newMap[String, User]
val users = for {
tx <‐ Transaction()
name <‐ userNames
if userStorage.contains(name)
} yield userStorage.get(name) // transactional
Wednesday, December 30, 2009
114. STM: config
<akka>
<stm>
service = on
distributed = off
</stm>
</akka>
Wednesday, December 30, 2009
115. STM: disable
TransactionManagement.disableTransactions
Wednesday, December 30, 2009
123. Persistence
• Pluggable storage backend
- Cassandra
- MongoDB
- Redis
• Map, Vector and Ref abstraction
• MVCC
• Works together with STM
Wednesday, December 30, 2009
124. Akka Persistence API
// transactional Cassandra‐backed Map
val map = CassandraStorage.newMap
// transactional Redis‐backed Vector
val vector = RedisStorage.newVector
// transactional Mongo‐backed Ref
val ref = MongoStorage.newRef
Wednesday, December 30, 2009
125. Persistence: config
<akka>
<storage>
<cassandra>
hostname = "127.0.0.1"
port = 9160
storage‐format = "protobuf"
consistency‐level = quorum
</cassandra>
<mongodb>
hostname = "127.0.0.1"
port = 27017
dbname = "mydb"
</mongodb>
</storage>
</akka>
Wednesday, December 30, 2009
126. Akka’s Cassandra API
val sessions = new CassandraSessionPool(
keyspace,
StackPool(SocketProvider(host, port)),
Protocol.Binary,
consistencyLevel)
Create a session pool
Wednesday, December 30, 2009
127. Akka Cassandra API
// get a column
val column = sessions.withSession { session =>
session | (key, new ColumnPath(
columnFamily, superColumn, serializer.out(name))
}
val value = if (column.isDefined)
Some(serializer.in(column.get.value, None)) else
None
Automatic connection management
Wednesday, December 30, 2009
128. Akka Cassandra API
// add a column
sessions.withSession { session =>
session ++|
(key,
new ColumnPath(cf, null, serializer.out(name)),
serializer.out(value),
System.currentTimeMillis,
consistencyLevel)
}
Wednesday, December 30, 2009
138. Security: service
class SampleAuthenticationService
extends DigestAuthenticationActor {
// Use an in‐memory nonce‐map as default
override def mkNonceMap = new HashMap[String, Long]
// Change this to whatever you want
override def realm = “sample”
// Username, password and roles for a username
override def userInfo(uname: String): Option[UserInfo] = {
... // get user with password and roles
Some(UserInfo(uname, password, roles))
}
}
Wednesday, December 30, 2009
139. Security: usage
class SecuredActor extends Actor {
@RolesAllowed(Array(“admin”))
def resetSystem =
(this !! Reset).getOrElse(
<error>Could not reset system</error>)
def receive = {
case Reset => ...
}
}
Wednesday, December 30, 2009
140. Security: config
<akka>
<rest>
service = on
hostname = “localhost”
port = 9998
filters = [“AkkaSecurityFilterFactory”]
authenticator = “SimpleAuthenticationService”
</rest>
</akka>
Wednesday, December 30, 2009