Akka es un toolkit y runtime para construir aplicaciones concurrentes, distribuidas y resilientes en la JVM (Java Virtual Machine). Akka se basa en el modelo de actores, que proporciona una abstracción de alto nivel para la concurrencia y la paralelización.

Objetivos de Aprendizaje

Al final de esta sección, deberías ser capaz de:

  • Comprender los conceptos básicos del modelo de actores.
  • Crear y gestionar actores en Scala utilizando Akka.
  • Enviar y recibir mensajes entre actores.
  • Manejar el ciclo de vida de los actores.
  • Implementar patrones comunes de concurrencia y paralelización con Akka.

  1. Conceptos Básicos del Modelo de Actores

¿Qué es un Actor?

Un actor es una entidad que:

  • Tiene un estado privado.
  • Puede recibir mensajes.
  • Puede enviar mensajes a otros actores.
  • Puede crear nuevos actores.

Ventajas del Modelo de Actores

  • Encapsulamiento de Estado: Cada actor tiene su propio estado privado, lo que evita problemas de concurrencia.
  • Escalabilidad: Los actores pueden ser distribuidos en múltiples nodos.
  • Resiliencia: Los actores pueden supervisar a otros actores y reiniciarlos en caso de fallo.

  1. Configuración del Proyecto

Para empezar a trabajar con Akka en Scala, necesitas configurar tu proyecto. Aquí te mostramos cómo hacerlo utilizando SBT (Scala Build Tool).

build.sbt

name := "AkkaIntroduction"

version := "0.1"

scalaVersion := "2.13.6"

libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.6.16"

  1. Creación de Actores

Definición de un Actor

Para definir un actor en Akka, necesitas extender la clase AbstractBehavior y definir cómo manejar los mensajes.

import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors

object HelloWorldActor {
  final case class Greet(whom: String)

  def apply(): Behavior[Greet] = Behaviors.receive { (context, message) =>
    context.log.info("Hello, {}!", message.whom)
    Behaviors.same
  }
}

Creación y Ejecución de un Actor

Para crear y ejecutar un actor, necesitas un ActorSystem.

import akka.actor.typed.ActorSystem

object HelloWorldApp extends App {
  val system: ActorSystem[HelloWorldActor.Greet] = ActorSystem(HelloWorldActor(), "hello")

  system ! HelloWorldActor.Greet("World")
}

  1. Comunicación entre Actores

Envío de Mensajes

Los actores se comunican enviando mensajes. Puedes enviar un mensaje a un actor utilizando el operador !.

val greeter: ActorRef[HelloWorldActor.Greet] = system
greeter ! HelloWorldActor.Greet("Akka")

Recepción de Mensajes

La recepción de mensajes se maneja en el método Behaviors.receive.

def apply(): Behavior[Greet] = Behaviors.receive { (context, message) =>
  context.log.info("Hello, {}!", message.whom)
  Behaviors.same
}

  1. Ciclo de Vida de los Actores

Creación y Terminación

Los actores pueden crear otros actores y también pueden ser terminados.

val childActor = context.spawn(HelloWorldActor(), "childActor")
context.stop(childActor)

Supervisión

Los actores pueden supervisar a otros actores y tomar acciones en caso de fallo.

val supervisor = Behaviors.supervise(HelloWorldActor()).onFailure[Exception](SupervisorStrategy.restart)

  1. Patrones Comunes

Patrón de Mensajería

Un patrón común es el uso de mensajes para coordinar tareas entre actores.

object Counter {
  sealed trait Command
  case object Increment extends Command
  case object Decrement extends Command
  case class GetCount(replyTo: ActorRef[Count]) extends Command
  case class Count(value: Int)

  def apply(): Behavior[Command] = counter(0)

  private def counter(count: Int): Behavior[Command] = Behaviors.receiveMessage {
    case Increment =>
      counter(count + 1)
    case Decrement =>
      counter(count - 1)
    case GetCount(replyTo) =>
      replyTo ! Count(count)
      Behaviors.same
  }
}

Ejemplo de Uso

val counter: ActorRef[Counter.Command] = system
counter ! Counter.Increment
counter ! Counter.GetCount(replyTo = system)

Ejercicio Práctico

Ejercicio

Crea un sistema de actores que simule un sistema de chat simple. Debe haber un actor ChatRoom que maneje múltiples actores User. Los usuarios pueden enviar mensajes al ChatRoom, y el ChatRoom debe reenviar estos mensajes a todos los usuarios conectados.

Solución

object ChatRoom {
  sealed trait Command
  case class Join(user: ActorRef[User.Command]) extends Command
  case class Leave(user: ActorRef[User.Command]) extends Command
  case class SendMessage(message: String) extends Command

  def apply(): Behavior[Command] = Behaviors.setup { context =>
    var users = Set.empty[ActorRef[User.Command]]

    Behaviors.receiveMessage {
      case Join(user) =>
        users += user
        Behaviors.same
      case Leave(user) =>
        users -= user
        Behaviors.same
      case SendMessage(message) =>
        users.foreach(_ ! User.ReceiveMessage(message))
        Behaviors.same
    }
  }
}

object User {
  sealed trait Command
  case class ReceiveMessage(message: String) extends Command

  def apply(name: String): Behavior[Command] = Behaviors.receive { (context, message) =>
    message match {
      case ReceiveMessage(msg) =>
        context.log.info(s"[$name] received message: $msg")
        Behaviors.same
    }
  }
}

object ChatApp extends App {
  val system: ActorSystem[ChatRoom.Command] = ActorSystem(ChatRoom(), "chatRoom")

  val user1 = system.systemActorOf(User("Alice"), "user1")
  val user2 = system.systemActorOf(User("Bob"), "user2")

  system ! ChatRoom.Join(user1)
  system ! ChatRoom.Join(user2)

  system ! ChatRoom.SendMessage("Hello everyone!")
}

Conclusión

En esta sección, hemos cubierto los conceptos básicos de Akka y el modelo de actores. Aprendiste a crear y gestionar actores, enviar y recibir mensajes, y manejar el ciclo de vida de los actores. También implementaste un patrón común de mensajería y un ejercicio práctico para reforzar los conceptos aprendidos. Con estos conocimientos, estás preparado para explorar más a fondo las capacidades de Akka y construir aplicaciones concurrentes y distribuidas robustas.

© Copyright 2024. Todos los derechos reservados