+ All Categories
Home > Software > Scaling modern JVM applications with Akka toolkit

Scaling modern JVM applications with Akka toolkit

Date post: 22-Jan-2017
Category:
Upload: bojan-babic
View: 43 times
Download: 3 times
Share this document with a friend
28
Scaling modern JVM applications with Akka toolkit Bojan Babić
Transcript

Scaling modern JVM applications with Akka toolkit

Bojan Babić

About me

• Full stack developer, focused on JVM

• Currently focused on scaling in messaging

@tenzki github.com/tenzki

Basic

• Unit of computation

• Motivated by relatable concepts

• Application as human organization

Mailbox

Actor

Isolated State

21 3 54 6

S1

S0

S2

Supervisor Actor 0

Supervisor Actor 1

Subordinate Actors

Akka

• Toolset with actor model as its foundation

• Written in Scala

• JVM with Java and Scala API

Example

• User in organization

• Described by name

• Can read and change it

// 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

// 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

// 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

Persisting state

• Event sourcing

• Supported with persistence module

• Cassandra, with support for relational databases, mongo…

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}" }

// 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

// 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

// 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

// 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

// 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

Running on multiple machines

• Cluster module

• Gossip protocol

• Cluster sharding

3

21

USERS

A B C Shards

User Actors

Cluster

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}" }

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}" }

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}" }

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 }

}

Optimize for scaling

• Command query responsibility segregation

• Different databases, eventually consistent

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

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 }

}

Linking write and read

• Persistence query, API for streaming events

• Akka streams and reactive streams

• Streaming events from journal to any read database

Thanks!


Recommended