Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building Stateful Microservices With Akka

769 views

Published on

Although most microservices are stateless - they delegate things like persistence and consistency to a database or external storage. But sometimes you benefit when you keep the state inside the application. In this talk I’m going to discuss why you want to build stateful microservices and design choices to make. I’ll use Akka framework and explain tools like Akka Clustering and Akka Persistence in depth and show a few practical examples.

Published in: Software
  • Be the first to comment

Building Stateful Microservices With Akka

  1. 1. Building Stateful Microservices With Akka Yaroslav Tkachenko Senior Software Engineer at Demonware (Activision) 1 / 40
  2. 2. Java, Scala, Python, Node Microservices Event-driven Systems Distributed Systems DevOps ... and more About me Yaroslav (Slava) Tkachenko, Vancouver, Canada Demonware (Activision), 2017 Senior Software Engineer [Data Pipeline] Mobify, 2016 - 2017 Senior Software Engineer, Lead [Platform] Bench Accounting, 2011 - 2016 Director of Engineering [Platform] Engineering Lead Software Engineer Freelance, 2007 - 2011 Web Developer 2 / 40
  3. 3. https://sap1ens.com/slides/stateful-services/ 3 / 40
  4. 4. Agenda Microservices: stateless vs stateful Actor systems Akka Akka Cluster and Persistence Real-world applications 4 / 40
  5. 5. Microservices: stateless vs stateful 5 / 40
  6. 6. Microservices: stateless vs stateful Stateless application: application that doesn't keep any state in memory / runtime, but uses external services instead. External service: database, cache, API, etc. Examples: most of the web apps are stateless or designed to be stateless (Spring, Django, Rails, Express, etc.). Stateful application: application that keeps internal state in memory / runtime, instead of relying on external services. Examples: actors can be stateful, so Akka and other actor-based systems (Erlang/OTP, Orleans) can be stateful. But it's also possible to create stateful applications in Node.js or Python, for example. 6 / 40
  7. 7. Microservices: stateless 7 / 40
  8. 8. Microservices: stateless Benefits: Simple development & deployment Simple to scale out -> just add more nodes Biggest challenges: Low latency -> can use caching, but not when strong consistency is needed Concurrent modifications -> conflict resolution with optimistic / pessimistic locking 8 / 40
  9. 9. Microservices: stateful 9 / 40
  10. 10. Microservices: stateful 10 / 40
  11. 11. Microservices: stateful Benefits: Data locality -> low latency, fast processing Sticky consistency -> "simple" and "cheap" consistency without using consensus protocols Biggest challenges: High availability Scaling out 11 / 40
  12. 12. Actor systems 12 / 40
  13. 13. Actor systems An actor is a computational entity that, in response to a message it receives, can concurrently: send a finite number of messages to other actors; create a finite number of new actors; designate the behavior to be used for the next message it receives. There is no assumed sequence to the above actions and they could be carried out in parallel. Every actor has: A mailbox A supervisor Some state [optionally] 13 / 40
  14. 14. Rachel Alex Actor systems - Examples           Fred 14 / 40
  15. 15. Actor systems - Examples Akka Concurrency by Derek Wyatt, Artima 15 / 40
  16. 16. Actor systems - Examples Akka Concurrency by Derek Wyatt, Artima 16 / 40
  17. 17. Actor systems - Examples Akka Concurrency by Derek Wyatt, Artima 17 / 40
  18. 18. Akka 18 / 40
  19. 19. Akka Akka is an open-source toolkit and runtime simplifying the construction of concurrent and distributed applications on the JVM. Akka supports multiple programming models for concurrency, but it emphasizes actor-based concurrency, with inspiration drawn from Erlang. 19 / 40
  20. 20. Akka - Actors case class Greeting(who: String) class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter") greeter ! Greeting("Charlie Parker")   Messages are handled one by one Immutability of messages 20 / 40
  21. 21. Akka - Communication class HelloActor extends Actor with ActorLogging { def receive = { case who => sender() ! "Hello, " + who } } object ConversationActor { def props(fellowActor: ActorRef): Props = Props(classOf[ConversationActor], fellowActor) } class ConversationActor(fellowActor: ActorRef) extends Actor with ActorLogging { def receive = { case "start" => fellowActor ! "it's me!" case message => log.info(message) } } val system = ActorSystem("MySystem") val helloActor = system.actorOf(Props[HelloActor]) val conversationActor = ConversationActor.props(helloActor) conversationActor ! "start" 21 / 40
  22. 22. Actor systems and Akka - Why? So, why actors? Simple concurrency Clean asynchronous programming model Great fit for event-driven systems Resilience Scalability 22 / 40
  23. 23. Akka Persistence 23 / 40
  24. 24. Akka Persistence - Overview 24 / 40
  25. 25. Akka Persistence - Overview Event Sourcing Persistent Actor Journal Snapshot Has plugins for JDBC (MySQL, Postgres, ...), MongoDB, Cassandra, Kafka, Redis and more. 25 / 40
  26. 26. Akka Persistence - Example case class Cmd(data: String) case class Evt(data: String) case class ExampleState(events: List[String] = Nil) { def updated(evt: Evt): ExampleState = copy(evt.data :: events) override def toString: String = events.reverse.toString } class ExamplePersistentActor extends PersistentActor { override def persistenceId = "sample-id-1" var state = ExampleState() def updateState(event: Evt): Unit = state = state.updated(event) val receiveRecover: Receive = { case evt: Evt => updateState(evt) case SnapshotOffer(_, snapshot: ExampleState) => state = snapshot } val receiveCommand: Receive = { case Cmd(data) => persist(Evt(data))(updateState) case "snap" => saveSnapshot(state) case "print" => println(state) } } 26 / 40
  27. 27. Akka Cluster 27 / 40
  28. 28. Cluster Node Gossip protocol Failure Detector Akka Cluster - Overview 28 / 40
  29. 29. Akka Cluster - Sharding Features: One of the most powerful Akka features! Allows to route messages across nodes in a cluster using a sharding function (actually two) You don't need to know the physical location of an actor - cluster will forward message to a remote node if needed Uses Akka Persistence internally (or brand-new Distributed Data) Concepts: Coordinator Shard Region Shard Entity Entities (actors) are "activated" by receiving a first message and can be "passivated" using context.setReceiveTimeout. 29 / 40
  30. 30. Akka Cluster - Sharding Counter interface: case object Increment case object Decrement final case class Get(counterId: Long) final case class EntityEnvelope(id: Long, payload: Any) case object Stop final case class CounterChanged(delta: Int) 30 / 40
  31. 31. Akka Cluster - Sharding Counter implementation: class Counter extends PersistentActor { context.setReceiveTimeout(120.seconds) override def persistenceId: String = "Counter-" + self.path.name var count = 0 def updateState(event: CounterChanged): Unit = count += event.delta override def receiveRecover: Receive = { case evt: CounterChanged ⇒ updateState(evt) } override def receiveCommand: Receive = { case Increment ⇒ persist(CounterChanged(+1))(updateState) case Decrement ⇒ persist(CounterChanged(-1))(updateState) case Get(_) ⇒ sender() ! count case ReceiveTimeout ⇒ context.parent ! Passivate(stopMessage = Stop) case Stop ⇒ context.stop(self) } } 31 / 40
  32. 32. Akka Cluster - Sharding Create a region on every node: val counterRegion: ActorRef = ClusterSharding(system).start( typeName = "Counter", entityProps = Props[Counter], settings = ClusterShardingSettings(system), extractEntityId = extractEntityId, extractShardId = extractShardId) Sharding functions: val extractEntityId: ShardRegion.ExtractEntityId = { case EntityEnvelope(id, payload) ⇒ (id.toString, payload) case msg @ Get(id) ⇒ (id.toString, msg) } val numberOfShards = 100 val extractShardId: ShardRegion.ExtractShardId = { case EntityEnvelope(id, _) ⇒ (id % numberOfShards).toString case Get(id) ⇒ (id % numberOfShards).toString } 32 / 40
  33. 33. Akka Cluster Sharding + Persistence = ❤ Akka Cluster Sharding: Consistent hashing for all requests based on user-defined function Automatic forwarding (from local to remote and vice versa) Akka Persistence: Keeping internal state Easy and fast recovery (journal + snapshots) Event-sourcing built-in 33 / 40
  34. 34. Real-world applications 34 / 40
  35. 35. Real-world applications Complex event-driven state machine with low latency API (aka The Tracker) More (online gaming, data aggregation, trading, complex domains, ...) 35 / 40
  36. 36. Real-world applications - The Tracker Complex event-driven state machine: Consuming: Domain Events via messaging queue (Akka Camel) Interface for querying: HTTP API (Akka HTTP) Websockets (Akka HTTP) Every entity has a clientId and they never intersect - it's a perfect use-case for sharding (clientId as a sharding key). 36 / 40
  37. 37. Real-world applications - The Tracker object TrackerService { case class TrackerData( accounts: Map[String, BankAccount] = Map[String, BankAccount]() ) } class TrackerService extends PersistentActor { private var state = TrackerData() private def handleMessage(message: EventMessage) { val maybeUpdatedState = message match { case b: BankAccountMessage => handleBankMessage(b) case c: ClientMessage => handleClientMessage(c) case _ => None } maybeUpdatedState.foreach { updatedState => updateState(updatedState) } } private def updateState(updatedState: TrackerData) = { state = state.copy( accounts = (state.accounts ++ updatedState.accounts).filterNot(_._2.deleted) ) } } 37 / 40
  38. 38. Real-world applications 38 / 40
  39. 39. Summary Actor-based programming simplifies building highly scalable and reliable systems It's not easy to build & maintain a stateful application, but you never know when it's going to be needed Don't try to write abstractions for distributed programming from scratch (unless you're an expert) Akka has a few great abstractions already, use them! It's easier to build a stateful application as a microservice - smaller state size, more flexibility and great separation of concerns 39 / 40
  40. 40. Questions? @sap1ens 40 / 40

×