94
Scalability in Scala and Java Nadav Wiener

Akka -- Scalability in Scala and Java

Embed Size (px)

DESCRIPTION

Akka, an actor framework written in Scala (that also supports Java) provides you with all the benefits commonly found in actor frameworks, but without the kinks. This presentation will explore actor based concurrency using Akka, and dwell on some of Akka's stregths, culminating in the equation "Transactional Memory + Actors = Transactors".

Citation preview

Page 1: Akka -- Scalability in Scala and Java

Scalability in Scala and Java Nadav Wiener

Page 2: Akka -- Scalability in Scala and Java

• Nadav Wiener (@nadavwr)

• Senior Consultant & Architect @ AlphaCSP

• Following Scala since 2007

About me

Page 3: Akka -- Scalability in Scala and Java

Akka

The problem with locks

Actors & message passing

High availability & remoting

STM & Transactors

Agenda

Page 4: Akka -- Scalability in Scala and Java

• A actor-based concurrency framework

• Provides solutions for non blocking concurrency

• Written in Scala, also works in Java

• Open source (APL)

• Now releasing 1.0 after 2 years in development

• Lead developer and founder: Jonas Boner

• JRockit, AspectWerkz, AspectJ, Terracotta

What is Akka?

Page 5: Akka -- Scalability in Scala and Java

“Queen of Lapland”

5

Page 6: Akka -- Scalability in Scala and Java

You may also remember…

6

Page 7: Akka -- Scalability in Scala and Java

The Problem

Page 8: Akka -- Scalability in Scala and Java

8

Ignorance Is a Bliss

If (account1.getBalance() > amount) {

account1.withdraw(amount)

account2.deposit(amount)

}

Sewing on a button © Dvortygirl, 17 February 2008.

Page 9: Akka -- Scalability in Scala and Java

Not an option if you plan on having business

Page 10: Akka -- Scalability in Scala and Java

10

So You Use Threads

Coloured Thread © Philippa Willitts, 24 April 2008.

Page 11: Akka -- Scalability in Scala and Java

11

But Without Synchronization You Get Unpredictable Behavior

Page 12: Akka -- Scalability in Scala and Java

12

Lock based concurrency requires us to constantly second guess our code

Page 13: Akka -- Scalability in Scala and Java

13

“The definition of insanity is doing the same thing over and over again and expecting different results. “ – Albert Einstein

Page 14: Akka -- Scalability in Scala and Java

People Are Bad At Thinking Parallel

14

Page 15: Akka -- Scalability in Scala and Java

15

So you synchronize

With locks?

Page 16: Akka -- Scalability in Scala and Java

16

locking

Page 17: Akka -- Scalability in Scala and Java

17

locking B

Page 18: Akka -- Scalability in Scala and Java

18

Lock too much Lock too little

Page 19: Akka -- Scalability in Scala and Java

Lock recklessly?

19

Page 20: Akka -- Scalability in Scala and Java

Using locks recklessly

20

Page 21: Akka -- Scalability in Scala and Java

22

Must Think Globally:

Lock ordering

Contention

Everybody’s code

Page 22: Akka -- Scalability in Scala and Java

Knowing shared-state concurrency != confidence

23

Page 23: Akka -- Scalability in Scala and Java

Keep those cores busy! Cores aren’t getting faster Default thread stack size on AMD64 = 1MB! Context switching hurts throughput

Page 24: Akka -- Scalability in Scala and Java

25

Page 25: Akka -- Scalability in Scala and Java

Actors

Page 26: Akka -- Scalability in Scala and Java

Shakespeare Programming Language

Page 27: Akka -- Scalability in Scala and Java

Romeo, a young man with a remarkable patience.

Juliet, a likewise young woman of remarkable grace.

Scene II: The praising of Juliet.

[Enter Juliet]

Romeo:

Thou art as sweet as the sum of the sum

of Hamlet and his horse and his black

cat! Speak thy mind!

[Exit Juliet]

Excerpt from "Hello World" in SPL

Page 28: Akka -- Scalability in Scala and Java

Actors have nothing to do with the Shakespeare Programming Language

Page 29: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 30: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 31: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 32: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 33: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 34: Akka -- Scalability in Scala and Java

• Each actor has a message queue

• Actors accept and choose what to do with messages

• Lightweight & asynchronous

Actors

Page 35: Akka -- Scalability in Scala and Java

• Actors tend to remain bound to a single thread

• Actors rarely block, thread can remain active for a long duration

• Minimizes context switches – throughput

• Akka actors occupy 650 bytes

Actors

4 cores ~4 threads

X millions

Actors

Page 36: Akka -- Scalability in Scala and Java

• Several actor implementations for Scala – Akka is considered the fastest

Benchmark

Page 37: Akka -- Scalability in Scala and Java

// Java

public class GreetingActor extends UntypedActor {

private int counter = 0;

public void onReceive(Object message) throws Exception {

counter++;

// 1) Hello, Juliet log.info( counter + ") Hello, " + message);

}

}

Akka Actors

// Scala

class GreetingActor extends Actor {

private var counter = 0

def receive = {

case message => {

counter += 1

// 1) Hello, Juliet log.info( counter + ") Hello, " + message)

} } }

Page 38: Akka -- Scalability in Scala and Java

// Java

ActorRef Romeo =

actorOf(GreetingActor.class).start();

greetingActor.sendOneWay("Juliet");

// 1) Hello, Juliet

Akka Actors

// Scala

val greetingActor =

actorOf[GreetingActor].start

greetingActor ! "Juliet“

// 1) Hello, Juliet

Page 39: Akka -- Scalability in Scala and Java

Akka Actors

• Once instantiated, actors can be retrieved by id or uuid

• uuid - generated by runtime, immutable, unique

• id - user assigned, by default that's the class name

class Romeo extend GreetingActor {

self.id = "Romeo"

}

actorOf[Romeo].start

val romeo = actorsFor("Romeo").head

romeo ! "Juliet"

Page 40: Akka -- Scalability in Scala and Java

Message Passing

Page 41: Akka -- Scalability in Scala and Java

• Let's build a bank with one actor per account, We’ll be able to :

• Check our balance

• Deposit money

• Withdraw money, but only if we have it (balance remains >= 0)

• We'll start by defining immutable message types:

case class CheckBalance()

case class Deposit(amount: Int)

case class Withdraw(amount: Int)

Message Passing

Page 42: Akka -- Scalability in Scala and Java

class BankAccount(private var balance: Int = 0) extends Actor {

def receive = {

// …

case CheckBalance =>

self.reply_?(balance)

// …

} }

Message Passing

Page 43: Akka -- Scalability in Scala and Java

class BankAccount(private var balance: Int = 0) extends Actor {

def receive = {

// …

case Deposit(amount) =>

balance += amount

// …

} }

Message Passing

Page 44: Akka -- Scalability in Scala and Java

class BankAccount(private var balance: Int = 0) extends Actor {

def receive = {

// …

case Withdraw(amount) =>

balance = (balance - amount) ensuring (_ >= 0)

// …

} }

Message Passing

Page 45: Akka -- Scalability in Scala and Java

• Now let’s make a deposit:

val account = actorOf[BankAccount].start

account ! Deposit(100)

• But how do we check the account balance?

Message Passing

Page 46: Akka -- Scalability in Scala and Java

• actorRef ! message - fire and forget

• actorRef !! message - send and block (optional timeout) for response

• actorRef !!! message - send, reply using future

...Jones Boner promised to stop at "!!!"

Java equivalents:

• ! = sendOneWay

• !! = sendRequestReply

• !!! = sendRequestReplyFuture

Bang! Bang Bang!

Page 47: Akka -- Scalability in Scala and Java

val balance = (account !! CheckBalance) match {

// do stuff with result

}

// or:

val balanceFuture = account !!! CheckBalance // does not block

// ... go do some other stuff ... later:

val balance = balanceFuture.await.result match {

// do stuff with result

}

Getting Account Balance

Page 48: Akka -- Scalability in Scala and Java

Fault Tolerance

Page 49: Akka -- Scalability in Scala and Java

Too Big to Fail

51

Page 50: Akka -- Scalability in Scala and Java

Jenga Architecture

52

Page 51: Akka -- Scalability in Scala and Java

The harder they fall

53

Page 52: Akka -- Scalability in Scala and Java

54

Page 53: Akka -- Scalability in Scala and Java

Self Healing,

Graceful Recovery

55

Page 54: Akka -- Scalability in Scala and Java

Supervisors: Kind of actors

Fault Handling Strategy: One for One or All for One

Lifecycle: Permanent or Temporary

Supervision Hierarchies

Page 55: Akka -- Scalability in Scala and Java

Fault Handling Strategy:

One For One

Page 56: Akka -- Scalability in Scala and Java

One for One

All for One, Permanent

One for One, Temporary

Romeo,

Permanent

Juliet,

Permanent

Chat Room, Permanent

59

Page 57: Akka -- Scalability in Scala and Java

One for One

All for One, Permanent

One for One, Temporary

Romeo,

Permanent

Juliet,

Permanent

Chat Room, Permanent

60

Page 58: Akka -- Scalability in Scala and Java

One for One

All for One, Permanent

One for One, Temporary

Romeo,

Permanent

Juliet,

Permanent

Chat Room, Permanent

61

Page 59: Akka -- Scalability in Scala and Java

One for One

All for One, Permanent

One for One, Temporary

Romeo,

Permanent

Juliet,

Permanent

Chat Room, Permanent

62

Page 60: Akka -- Scalability in Scala and Java

One for One

All for One, Permanent

One for One, Temporary

Romeo,

Permanent

Juliet,

Permanent

Chat Room, Permanent

63

Page 61: Akka -- Scalability in Scala and Java

Fault Handling Strategy:

All For One

Page 62: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

67

Page 63: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

68

Page 64: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

69

Page 65: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

70

Page 66: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

71

Page 67: Akka -- Scalability in Scala and Java

All for One

All For One, Permanent

One for One, Permanent

Romeo,

Temporary

Juliet,

Temporary

Chat Room, Permanent

72

Page 68: Akka -- Scalability in Scala and Java

val supervisor = Supervisor(SupervisorConfig(

AllForOneStrategy(

List(classOf[Exception]), 3, 1000),

List(Supervise(actorOf[ChatServer], Permanent),

Supervise(actorOf[ChatServer], Permanent,

RemoteAddess("host1", 9000))

)

))

Supervision, Remoting & HA

Page 69: Akka -- Scalability in Scala and Java

• You can’t have a highly available system on a single computer

• Luckily Akka supports near-seamless remote actors

High Availability

Page 70: Akka -- Scalability in Scala and Java

• Server managed remote actors:

// on host1

RemoteNode.start("host1", 9000)

// register an actor

RemoteNode.register(“romeo", actorOf[GreetingActor])

// on host2

val romeo = RemoteClient.actorFor(“romeo", "host1", 9000)

romero ! “juliet"

• RemoteClient handles the connection lifecycle for us

• Clients can also manage server actors, but enabling this might pose a security risk

High Availability

Page 71: Akka -- Scalability in Scala and Java

Actor model – a life choice?

Page 72: Akka -- Scalability in Scala and Java

High scalability Assumes state travels

Hgh availability along the message

Fast flow

Etc… Hostile towards shared state.

Minds not easily rewired for this!

Page 73: Akka -- Scalability in Scala and Java

Brain Transplant

Page 74: Akka -- Scalability in Scala and Java

Software Transactional Memory

Page 75: Akka -- Scalability in Scala and Java

Persistent Data Structures

Rich Hickey (Clojure)

Page 76: Akka -- Scalability in Scala and Java

• Share common immutable structure and data

• Copy-on-write semantics:

• When “modified”, minimal changes to structure are made to accommodate new data

Persistent Data Structures

© Rich Hickey 2009

val prices = TransactionalMap[String, Double]

atomic { prices += ("hamburger" -> 20.0) }

Page 77: Akka -- Scalability in Scala and Java

How many people seated in the audience?

Page 78: Akka -- Scalability in Scala and Java

If I started counting, by the time I finished…

1, 2, 3, 4, 5, …

Page 79: Akka -- Scalability in Scala and Java

…the room would be empty

Page 80: Akka -- Scalability in Scala and Java

Jonas Boner

Page 81: Akka -- Scalability in Scala and Java

Transactors

class BankAccount extends Transactor {

private val balanceRef = Ref(0)

def atomically = { // ...

case CheckBalance => self reply_? balance.get

// ...

}

}

Page 82: Akka -- Scalability in Scala and Java

Transactors

class BankAccount extends Transactor {

private val balanceRef = Ref(0)

def atomically = { // ...

case Deposit(amount) =>

balance alter (_ + amount) // ...

}

}

Page 83: Akka -- Scalability in Scala and Java

Transactors

class BankAccount extends Transactor {

private val balanceRef = Ref(0)

def atomically = { // ...

case Withdraw(amount) =>

balance alter (_ - amount) ensuring (_.get >= 0) // ...

}

}

Page 84: Akka -- Scalability in Scala and Java

Performing a money transfer transactionally:

val tx = Coordinated()

val fromBalance = (from !! tx(CheckBalance())) match {

balance: Int => balance

}

if (fromBalance >= 50) {

from ! tx(Withdraw(50))

to ! tx(Deposit(50)) }

Transactors

Page 85: Akka -- Scalability in Scala and Java

Coordinated Transactions

class Bank extends Actor { private val accounts =

TransactionalVector[BankAccount]

def receive = { // ...

case tx @ Coordinated(Join) => {

tx atomic {

accounts += self.sender.get

}

} // ...

}

Page 86: Akka -- Scalability in Scala and Java

Coordinated Transactions

class Bank extends Actor {

private val accounts = TransactionalVector[BankAccount]

def receive = { // ...

case tx @ Coordinated(Sum) => {

val futures = for (account <- accounts) yield

account !!! tx(CheckBalance)

val allTheMoney = futures map (_.await.result) sum

self reply_? allTheMoney

} // ...

} …and then: println (myBank !! Coordinated(Sum))

Page 87: Akka -- Scalability in Scala and Java

Takeaways

Page 88: Akka -- Scalability in Scala and Java

94

Knowing shared-state concurrency != confidence

Page 89: Akka -- Scalability in Scala and Java

References

Page 90: Akka -- Scalability in Scala and Java

http://akkasource.org

96

Page 91: Akka -- Scalability in Scala and Java

http://scalablesolutions.se

97

Page 92: Akka -- Scalability in Scala and Java

http://alphacsp.com

Page 93: Akka -- Scalability in Scala and Java

Questions?

Page 94: Akka -- Scalability in Scala and Java

• In Java we will usually avoid “untyped” actors, and use POJOs instead:

interface BankAccount { Future<Integer> balance(); void deposit(int amount); void withdraw(int amount); } class BankAccountImpl extends TypedActor implements BankAccount {

// Almost a regular POJO public Future<Integer> balance() { return future(balance); }

// ... }

Typed Actors in Java