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.
- 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.
- 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"
- 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") }
- Comunicación entre Actores
Envío de Mensajes
Los actores se comunican enviando mensajes. Puedes enviar un mensaje a un actor utilizando el operador !
.
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 }
- Ciclo de Vida de los Actores
Creación y Terminación
Los actores pueden crear otros actores y también pueden ser terminados.
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)
- 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.
Curso de Programación en Scala
Módulo 1: Introducción a Scala
- Introducción a Scala
- Configuración del Entorno de Desarrollo
- Conceptos Básicos de Scala: Sintaxis y Estructura
- Variables y Tipos de Datos
- Operaciones Básicas y Expresiones
Módulo 2: Estructuras de Control y Funciones
- Sentencias Condicionales
- Bucles e Iteraciones
- Funciones y Métodos
- Funciones de Orden Superior
- Funciones Anónimas
Módulo 3: Colecciones y Estructuras de Datos
- Introducción a las Colecciones
- Listas y Arreglos
- Conjuntos y Mapas
- Tuplas y Opciones
- Coincidencia de Patrones
Módulo 4: Programación Orientada a Objetos en Scala
- Clases y Objetos
- Herencia y Rasgos
- Clases Abstractas y Clases Caso
- Objetos Compañeros
- Objetos Singleton
Módulo 5: Programación Funcional en Scala
- Inmutabilidad y Funciones Puras
- Estructuras de Datos Funcionales
- Mónadas y Funtores
- Comprensiones For
- Manejo de Errores en Programación Funcional
Módulo 6: Conceptos Avanzados de Scala
- Conversiones y Parámetros Implícitos
- Clases de Tipo y Polimorfismo
- Macros y Reflexión
- Concurrencia en Scala
- Introducción a Akka