Download - Akka -- Scalability in Scala and Java

Transcript
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