Top Banner
Scaling modern JVM applications with Akka toolkit Bojan Babić
28

Scaling modern JVM applications with Akka toolkit

Jan 22, 2017

Download

Software

Bojan Babic
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Scaling modern JVM applications with Akka toolkit

Scaling modern JVM applications with Akka toolkit

Bojan Babić

Page 2: Scaling modern JVM applications with Akka toolkit

About me

• Full stack developer, focused on JVM

• Currently focused on scaling in messaging

@tenzki github.com/tenzki

Page 3: Scaling modern JVM applications with Akka toolkit

Basic

• Unit of computation

• Motivated by relatable concepts

• Application as human organization

Page 4: Scaling modern JVM applications with Akka toolkit

Mailbox

Actor

Isolated State

Page 5: Scaling modern JVM applications with Akka toolkit

21 3 54 6

S1

S0

S2

Supervisor Actor 0

Supervisor Actor 1

Subordinate Actors

Page 6: Scaling modern JVM applications with Akka toolkit

Akka

• Toolset with actor model as its foundation

• Written in Scala

• JVM with Java and Scala API

Page 7: Scaling modern JVM applications with Akka toolkit

Example

• User in organization

• Described by name

• Can read and change it

Page 8: Scaling modern JVM applications with Akka toolkit

// messages case object GetName case class SetName(name: String)

// actor class User(var name: String) extends Actor {

override def receive: Receive = { case GetName => sender() ! name case SetName(newName: String) => name = newName }

}

Plain actor

Page 9: Scaling modern JVM applications with Akka toolkit

// messages case object GetName case class SetName(name: String)

// actor class User(var name: String) extends Actor {

override def receive: Receive = { case GetName => sender() ! name case SetName(newName: String) => name = newName }

}

Plain actor

Page 10: Scaling modern JVM applications with Akka toolkit

// messages case object GetName case class SetName(name: String)

// actor class User(var name: String) extends Actor {

override def receive: Receive = { case GetName => sender() ! name case SetName(newName: String) => name = newName }

}

Plain actor

Page 11: Scaling modern JVM applications with Akka toolkit

Persisting state

• Event sourcing

• Supported with persistence module

• Cassandra, with support for relational databases, mongo…

Page 12: Scaling modern JVM applications with Akka toolkit

Persistent actor

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Page 13: Scaling modern JVM applications with Akka toolkit

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Persistent actor

Page 14: Scaling modern JVM applications with Akka toolkit

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Persistent actor

Page 15: Scaling modern JVM applications with Akka toolkit

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Persistent actor

Page 16: Scaling modern JVM applications with Akka toolkit

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Persistent actor

Page 17: Scaling modern JVM applications with Akka toolkit

// commands case object GetName case class SetName(name: String)

// events case class NameSet(name: String)

class User(id: UUID) extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

override def receiveCommand: Receive = { case GetName => sender() ! name case SetName(newName: String) => persist(NameSet(newName))(e => name = e.name) }

override def persistenceId: String = s"user:${id.toString}" }

Persistent actor

Page 18: Scaling modern JVM applications with Akka toolkit

Running on multiple machines

• Cluster module

• Gossip protocol

• Cluster sharding

Page 19: Scaling modern JVM applications with Akka toolkit

3

21

USERS

A B C Shards

User Actors

Cluster

Page 20: Scaling modern JVM applications with Akka toolkit

Sharded actor

// commands trait UserMsg {val id: UUID} case class SetName(id: UUID, name: String) extends UserMsg case class GetName(id: UUID) extends UserMsg

// events case class NameSet(name: String)

class User extends PersistentActor {

var name: String = null

def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

def receiveCommand: Receive = { case GetName(_) => sender() ! name case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name) }

def persistenceId: String = s"user:persistence:${self.path.name}" }

Page 21: Scaling modern JVM applications with Akka toolkit

Sharded actor

// commands trait UserMsg {val id: UUID} case class SetName(id: UUID, name: String) extends UserMsg case class GetName(id: UUID) extends UserMsg

// events case class NameSet(name: String)

class User extends PersistentActor {

var name: String = null

def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

def receiveCommand: Receive = { case GetName(_) => sender() ! name case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name) }

def persistenceId: String = s"user:persistence:${self.path.name}" }

Page 22: Scaling modern JVM applications with Akka toolkit

Sharded actor

// commands trait UserMsg {val id: UUID} case class SetName(id: UUID, name: String) extends UserMsg case class GetName(id: UUID) extends UserMsg

// events case class NameSet(name: String)

class User extends PersistentActor {

var name: String = null

def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

def receiveCommand: Receive = { case GetName(_) => sender() ! name case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name) }

def persistenceId: String = s"user:persistence:${self.path.name}" }

Page 23: Scaling modern JVM applications with Akka toolkit

Sharded actor

object User { val NAME = "user"

val extractEntityId: ShardRegion.ExtractEntityId = { case command: UserMsg => (command.id.toString, command) }

val numberOfShards = 100

val extractShardId: ShardRegion.ExtractShardId = { case command: UserMsg => (command.id.toString.hashCode % numberOfShards).toString }

}

Page 24: Scaling modern JVM applications with Akka toolkit

Optimize for scaling

• Command query responsibility segregation

• Different databases, eventually consistent

Page 25: Scaling modern JVM applications with Akka toolkit

trait UserMsg {val id: UUID} // commands case class SetName(id: UUID, name: String) extends UserMsg

// events case class NameSet(name: String)

class UserProcessor extends PersistentActor {

var name: String = null

override def receiveRecover: Receive = { case NameSet(changeName: String) => name = changeName }

def receiveCommand: Receive = { case SetName(_, newName: String) => persist(NameSet(newName))(e => name = e.name) }

def persistenceId: String = s"user:cqrs:${self.path.name}"

}

CQRS actors

Page 26: Scaling modern JVM applications with Akka toolkit

CQRS actors

// query case class GetName(id: UUID) extends UserMsg

class UserView extends Actor with Stash {

var name: String = null

def receive: Receive = { case GetName(id: UUID) => stash() case EventEnvelope(_, _, _, NameSet(newName: String)) => name = newName unstashAll() context.become(active) sender() ! Done }

def active: Receive = { case GetName(_) => sender() ! name case EventEnvelope(_, _, _, NameSet(newName: String)) => name = newName sender() ! Done }

}

Page 27: Scaling modern JVM applications with Akka toolkit

Linking write and read

• Persistence query, API for streaming events

• Akka streams and reactive streams

• Streaming events from journal to any read database

Page 28: Scaling modern JVM applications with Akka toolkit

Thanks!