Share
Akka: Simpler Scalability, Fault-Tolerance, Concurrency & Remoting through Actors
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.
Jonas Bonér
Transcript
162 slides
-
Jonas Bonér Akka: Simpler Scalability, Fault-tolerance, Concurrency & Remoting through
Actors http://akkasource.org Scalable Solutions AB Copyright 2009 - Scalable Solutions AB Wednesday, December 30, 2009 -
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 -
What is an Identity? A stable logical entity associated with
a series of different Values over time Wednesday, December 30, 2009 -
What is State? The Value an entity with a specific
Identity has at a particular point in time Wednesday, December 30, 2009 -
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 -
The Problem The unification Of Identity & Value They are
not the same Wednesday, December 30, 2009 -
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 -
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 -
Shared-State Concurrency is incredibly hard >Inherently very hard to use
reliably >Even the experts get it wrong Wednesday, December 30, 2009 -
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 -
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 -
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 -
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 -
Might lead to DEADLOCK Darn, this is really hard!!! Wednesday,
December 30, 2009 -
We need to enforce lock ordering >How? >Java won’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 -
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 -
Java bet on the wrong horse? Perhaps, but we’re not
completely screwed There are alternatives Wednesday, December 30, 2009 -
28 Alternative Paradigms >Software Transactional Memory (STM) >Message-Passing Concurrency (Actors)
>Dataflow Concurrency Wednesday, December 30, 2009 -
•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 Actors Wednesday, December 30, 2009 -
“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# Alan Kay (father of SmallTalk and OOP) Wednesday, December 30, 2009 -
Actors • Implements Message-Passing Concurrency • Share NOTHING • Isolated
lightweight processes • Communicates through messages • Asynchronous and non-blocking Wednesday, December 30, 2009 -
Actor Model of Concurrency • No shared state … hence,
nothing to synchronize. • Each actor has a mailbox (message queue) Wednesday, December 30, 2009 -
• Non-blocking send • Blocking receive • Messages are immutable
• Highly performant and scalable • SEDA-style (Staged Event-Driven Architecture) Actor Model of Concurrency Wednesday, December 30, 2009 -
Actor Model of Concurrency • Easier to reason about •
Raised abstraction level • Easier to avoid –Race conditions –Deadlocks –Starvation –Live locks Wednesday, December 30, 2009 -
Akka Actors • Asynchronous –Fire-and-forget –Futures (Send Receive Reply Eventually)
• Synchronous • Message loop with pattern (message) matching • Erlang-style Wednesday, December 30, 2009 -
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 -
> Akka (Java/Scala) > Kilim (Java) > Jetlang (Java) >
Actor’s Guild (Java) > ActorFoundry (Java) > Actorom (Java) > FunctionalJava (Java) > GParallelizer (Groovy) > Fan Actors (Fan) Actor libs for the JVM Wednesday, December 30, 2009 -
Akka Transactors Supervisor hierarchies STM Persistent Fault tolerance & Scalablility
Distributed RESTful Comet Secure Wednesday, December 30, 2009 -
val dispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher .withNewThreadPoolWithBoundedBlockingQueue(100) .setCorePoolSize(16) .setMaxPoolSize(128) .setKeepAliveTimeInMillis(60000) .setRejectionPolicy(new CallerRunsPolicy) .buildThreadPool
EventBasedDispatcher Fluent DSL Wednesday, December 30, 2009 -
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 -
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 -
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 -
MessageQueues Unbounded LinkedBlockingQueue Bounded LinkedBlockingQueue Bounded ArrayBlockingQueue Bounded SynchronousQueue Plus
different options per queue Wednesday, December 30, 2009 -
object factory extends SupervisorFactory( SupervisorConfig( RestartStrategy(AllForOne, 3, 10000), Supervise( actor1, LifeCycle(Permanent)) :: Supervise( actor2, LifeCycle(Temporary)) :: Nil))
factory.newSupervisor.start Declarative config Wednesday, December 30, 2009 -
override def init = { ... // init the actor } Initialize actor init is called on startup
Wednesday, December 30, 2009 -
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 -
> Transactions can nest > Transactions compose (yipee!!) atomic { ..
atomic { .. } } STM: overview Wednesday, December 30, 2009 -
92 >All operations in scope of a transaction: Need
to be idempotent Can’t have side-effects STM: restrictions Wednesday, December 30, 2009 -
93 Akka STM is based on the ideas of Clojure
STM Wednesday, December 30, 2009 -
• Typical OO: direct access to mutable objects Managed References
Typical OO - Direct references to Mutable Objects • Unifies identity and value • Anything can change at any time • Consistency is a user problem • Encapsulation doesn’t solve concurrency problems ? ? 42 ? 6 :e :d :c :b :a foo • Managed Reference: separates Identity & Value Clojure - Indirect references to Immutable Objects 6 17 "ethel" "fred" 42 :e :d :c :b :a foo @foo • Separates identity and value Copyright Rich Hickey 2009 Wednesday, December 30, 2009 -
• 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 Managed References Wednesday, December 30, 2009 -
for { users <‐ usersRef user <‐ users roles <‐ rolesRef role <‐ roles if user.hasRole(role) } { ... // do stuff } Managed
References Wednesday, December 30, 2009 -
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 -
Bit-partitioned hash trie Bit-partitioned hash tries Copyright Rich Hickey 2009
Wednesday, December 30, 2009 -
Structural sharing with path copying Path Copying int count 15
INode root HashMap int count 16 INode root HashMap Copyright Rich Hickey 2009 Approach • Programming with values is critical • 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 Wednesday, December 30, 2009 -
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] Persistent datastructures HashTrie
ble oordination Wednesday, December 30, 2009 -
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] Persistent datastructures Vector
ble oordination Wednesday, December 30, 2009 -
• 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 Akka STM Wednesday, December 30, 2009 -
class UserRegistry extends Transactor { private lazy val storage = TransactionalState.newMap[String, User] def receive = { case NewUser(user) => storage + (user.name ‐> user) ... } }
STM: declarative API Wednesday, December 30, 2009 -
class UserRegistry extends Actor { makeTransactionRequired private lazy val storage = TransactionalState.newMap[String, User] def receive = { case NewUser(user) => storage + (user.name ‐> user) ... }
} STM: declarative API Wednesday, December 30, 2009 -
import sbinary.DefaultProtocol._ val users = ("user1", "passwd1") :: ("user2", "passwd2") :: ("user3", "passwd3") :: Nil val bytes = Serializer.SBinary.out(users) val usersCopy: List[Tuple2[String, String]]] = Serializer.SBinary.in(bytes) Serializers Scala
2 Binary & Binary 2 Scala Wednesday, December 30, 2009 -
• Pluggable storage backend - Cassandra - MongoDB - Redis
• Map, Vector and Ref abstraction • MVCC • Works together with STM Persistence Wednesday, December 30, 2009 -
<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> Persistence: config Wednesday, December 30, 2009 -
Akka’s Cassandra API val sessions = new CassandraSessionPool( keyspace, StackPool(SocketProvider(host, port)), Protocol.Binary, consistencyLevel) Create a
session pool Wednesday, December 30, 2009 -
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 -
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 -
class Boot { object factory extends SupervisorFactory( SupervisorConfig( RestartStrategy(OneForOne, 3, 100), Supervise( new Counter, LifeCycle(Permanent)) :: Supervise( new Chat, LifeCycle(Permanent)) :: Nil))) val supervisor = factory.newInstance
supervisor.start } Boot classes Wednesday, December 30, 2009 -
Comet actors • Based on Atmosphere project • Portable •
Supports natively: • Tomcat 4, 5, 6 • Jetty 5, 6, 7 • GlassFish 1, 2, 3 • Weblogic 9.x, 10.x • Grizzly 1.9.x • JBossWeb 2.x • Annotation based Wednesday, December 30, 2009 -
@Path("/chat") class Chat extends Actor { case class Chat(who: String, what: String, message: String) @Suspend // receiving endpoint @GET @Produces(Array("text/html")) def suspend = <!‐‐ suspend ‐‐> // sending endpoint @Broadcast(Array(classOf[XSSHtmlFilter], classOf[JsonFilter])) @Consumes(Array("application/x‐www‐form‐urlencoded")) @Produces(Array("text/html"))
@POST def publishMessage(form: MultivaluedMap[String, String]) = (this !! Chat(form.getFirst("name"), form.getFirst("action"), form.getFirst("message"))).getOrElse("System error") def receive = { case Chat(..) => ..} } Comet actors Wednesday, December 30, 2009 -
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))
} } Security: service Wednesday, December 30, 2009 -
class Boot { // Lift’s bootstrap class def boot { LiftRules.httpAuthProtectedResource.prepend { case (ParsePath("akka‐lift" :: Nil, _, _, _)) => Full(AuthRole("admin")) } LiftRules.authentication = HttpBasicAuthentication("lift") { case ("guest", "guest", req) => userRoles(AuthRole("admin")) } object factory extends SupervisorFactory(
SupervisorConfig( RestartStrategy(OneForOne, 3, 100), List(Supervise(new AkkaService, LifeCycle(Permanent)))) factory.newInstance.start } } Lift integration Wednesday, December 30, 2009 -
<web‐app> <servlet> <servlet‐name>AkkaServlet</servlet‐name> <servlet‐class> se.ss.akka.rest.AkkaServlet </servlet‐class> </servlet> <servlet‐mapping> <servlet‐name>AkkaServlet</servlet‐name> <url‐pattern>/*</url‐pattern>
</servlet‐mapping> </web‐app> Lift integration Wednesday, December 30, 2009 -
val producer = AMQP.newProducer( config, hostname, port, exchangeName, serializer, None, None, // listeners 100) producer ! Message(“Hi there”, routingId)
AMQP: producer Wednesday, December 30, 2009 -
val consumer = AMQP.newConsumer( config, hostname, port, exchangeName, ExchangeType.Direct, serializer, None, 100, passive, durable, Map[String, AnyRef()) consumer ! MessageConsumerListener( queueName, routingId, actor { case Message(payload, _, _, _, _) => ... // process message })
AMQP: consumer Wednesday, December 30, 2009 -
Akka as a library Add Persistence module as a Cache
Wednesday, December 30, 2009 -
Akka as a library Add Persistence module as primary SoR
Wednesday, December 30, 2009 -
Akka as a library Add REST and Comet modules Wednesday,
December 30, 2009 -
Monitoring & Management Provisioning and more... Part of commercial license
Wednesday, December 30, 2009