Download - Введение в Akka
Ресурсы
• Оф. сайт – http://akka.io• Документация – http://doc.akka.io/docs/akka/snapshot/• ScalaDoc – http://doc.akka.io/api/akka/snapshot/• Введение (30 минут) – Typesafe Activator template “Hello
Akka!”• Курс (8 часов) – Principles of Reactive Programming (Weeks
5,6,7)• Реактивный манифест – http://habrahabr.ru/post/195562/• Книга (не введение) – Effective Akka: Patterns and Best
Practices by Jamie Allen
Модули Akka
akka-actor – Classic Actors, Typed Actors, IO Actor etc.akka-agent – Agents, integrated with Scala STMakka-camel – Apache Camel integrationakka-cluster – Cluster membership management, elastic routers.akka-kernel – Akka microkernel for running a bare-bones mini application serverakka-osgi – base bundle for using Akka in OSGi containers, containing the akka-actor classesakka-osgi-aries – Aries blueprint for provisioning actor systemsakka-remote – Remote Actorsakka-slf4j – SLF4J Logger (event bus listener)akka-testkit – Toolkit for testing Actor systemsakka-zeromq – ZeroMQ integration
Примеры
• spray – веб-сервер и фреймворк для написания HTTP/REST приложений
• xitrum – веб-фреймворк• Klout – сервис анализ социальных связей• Amazon• Blizzard• Autodesk• ...
ПроблемаБлокирующие вызовы:• Blocking IO• Примитивы синхронизации (мьютексы, семафоры, CountDownLatch, …)• Thread.sleep()• Future.get()
Итог:• Простаивание ресурсов• Дедлоки• Сильная связанность
компонентов• Ухудшается
отзывчивость системы• Теряется возможность
масштабирования
РешениеРешение – акторы, взаимодействующие между собой асинхронно посредством сообщений (и только сообщений).
Акторы похожи на людей в комнате, говорящих друг с другом.
РешениеРешение – акторы, взаимодействующие между собой асинхронно посредством сообщений (и только сообщений).
Акторы похожи на людей в комнате, говорящих друг с другом.
Actor trait
type Receive = PartialFunction[Any, Unit]
trait Actor { def receive: Receive = ??? ...}
Пример Actor’а
class Time extends Actor { def receive = { case "What is the time?" => sender ! "12:43" }}
Пример stateful Actor’а
class Counter extends Actor { var count = 0 def receive = { case "incr" => count += 1 case ("get", customer: ActorRef) => customer ! count }}
Демонстрация
Parent
BankAccount
Демонстрация
Parent
BankAccount
akka://Main/user/app#-1327529947
akka://Main/user/app/acc1#1803679474
Stopping Actors
context.stop(actor)
actor ! PoisonPill
или
В обоих случаях посылается сообщение.В первом случае все сообщения в очереди выбрасываются.Во втором – сообщение кладется в конец очереди.
Закрепим знания
• Все акторы инкапсулированы и независимы (больше инкапсуляции, чем в традиционном ООП). Нету способа взаимодействия с акторами кроме отправки сообщений.
• Сообщения должны быть иммутабельными.• Избегать блокировок в акторах. Нужно использовать
асинхронное API для работы с файлами, БД, …• Отправка сообщений не является надёжной.• Порядок отправки и принятия сообщений неопределён
(кроме случая, когда актор посылает другому актору подряд сообщения).
Иерархии акторов
Большая корпорация
Отдел маркетинга Бухгалтерия
Отдел разработки
Проект1
БД UI
Напоминают иерархию большой корпорации
Supervision
Большая корпорация
Отдел маркетинга Бухгалтерия
Отдел разработки
Проект1
БД UI
Напоминают иерархию большой корпорации
Supervision
Большая корпорация
Отдел маркетинга Бухгалтерия
Отдел разработки
Проект1
БД UI
Напоминают иерархию большой корпорации
Supervision
Большая корпорация
Отдел маркетинга Бухгалтерия
Отдел разработки
Проект1
БД UI
Напоминают иерархию большой корпорации
Принимает решение
Supervision
class Manager extends Actor { override val supervisorStrategy = OneForOneStrategy() { case _: DBException => Restart // reconnect to DB case _: NullPointerException => Stop case _: ServiceDownException => Escalate } def receive = ???}
Жизненный цикл Actor’а
• start• (restart)*• stop
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
new Actor
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
new Actor
postRestart
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
new Actor
postRestart stop
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
new Actor
postRestart stop
StoppostStop
Жизненный цикл Actor’а
• start• (restart)*• stop
context.actorOf(…)new Actor
preStart
Restart
preRestart
new Actor
postRestart stop
StoppostStop
Может произойти [0, ∞) раз
Закрепим знания
• Акторы организуются в иерархии.• Акторы обязаны обрабатывать ошибки своих дочерних
акторов. Тем самым достигается отказоустойчивость системы.• Рестарты акторов не наблюдаемы из внешнего мира.• При рестарте актора рестартуется всё его поддерево.• Рестарты в листьях дерева иерархии происходят чаще.• Рискованные задачи желательно делегировать дочерним
акторам, если родительский актор имеет важное состояние.
Поиск акторовclass MyActor extends Actor { val path = "/user/app/b" context.actorSelection(path) ! Identify(42) def receive = { case ActorIdentity(42, Some(actorRef)) => { println(s"Actor with $path found: $actorRef") } case ActorIdentity(42, None) => { println(s"Actor with $path not found") } }}
Поиск акторов
context.actorSelection("/user/app/b")
context.actorSelection("child")
context.actorSelection("../sibling")
context.actorSelection("akka.tcp://Main@host:port/user/app/b")
context.actorSelection("/user/app/*")
Поиск по абсолютному пути:
Поиск дочернего актора:
Поиск соседнего актора:
Поиск удалённого актора:
Поиск по wildcard’ам:
Маршрутизация
Router
Routee1 Routee2 Routee3
? ? ?
Round Robin
Router
Routee1 Routee2 Routee3
Round Robin
Router
Routee1 Routee2 Routee3
Round Robin
Router
Routee1 Routee2 Routee3
Round Robin
Router
Routee1 Routee2 Routee3
Round Robin
Router
Routee1 Routee2 Routee3
Round Robin
• Сообщения должны быть равнозначными.• Воркеры должны быть равнозначными.
Random
Router
Routee1 Routee2 Routee3
Random
Router
Routee1 Routee2 Routee3
Random
Router
Routee1 Routee2 Routee3
Random
Router
Routee1 Routee2 Routee3
Random
Router
Routee1 Routee2 Routee3
Random
• Может вызвать разбалансировку.• Применимо, когда bottleneck в самом маршрутизаторе.• Применимо, когда есть несколько маршрутизаторов.• Воркеры должны быть равнозначными.• Сообщения должны быть равнозначными.
Smallest mailbox
Router
Routee1 Routee2 Routee3
Smallest mailbox
Router
Routee1 Routee2 Routee3
Smallest mailbox
• Равномерная балансировка.• Сообщения должны быть равнозначными.• Неприменимо для удалённых воркеров.• Относительно высокая цена маршрутизации – нужно каждый
раз считать размер очереди.
Broadcast
Router
Routee1 Routee2 Routee3
Broadcast
Router
Routee1 Routee2 Routee3
Broadcast
Router
Routee1 Routee2 Routee3
Broadcast
• Можно назначать различные задачи.• Повышается надежность (некоторые воркеры могут упасть
при выполнении задач).• Требуется в n раз больше ресурсов, где n – количество
воркеров.
ScatterGatherFirstCompletedOf
Router
Routee1 Routee2 Routee3
ScatterGatherFirstCompletedOf
Router
Routee1 Routee2 Routee3
Самый быстрый ответ(остальные отвергаются)
ScatterGatherFirstCompletedOf
• Применимо, когда нужно получить ответ как можно скорее.• Требуется в n раз больше ресурсов, где n – количество
воркеров.
Consistent Hash
Router
Routee1 Routee2 Routee3
Consistent Hash
Router
Routee1 Routee2 Routee3
Consistent Hash
Router
Routee1 Routee2 Routee3
Consistent Hash
Router
Routee1 Routee2 Routee3
Consistent Hash
Router
Routee1 Routee2 Routee3
Consistent Hash
• Задачи одного и того же типа отправляются одним и тем же воркерам.
• Позволяет избежать разделяемого состояния между воркерами. Например, сообщения, относящиеся к одному и тому же пользователю, всегда будут обработаны одним и тем же актором.
• Не гарантирует равномерность нагрузки.
Закрепим знания
• Асинхронная передача сообщений обеспечивает вертикальное масштабирование: обрабатывая сообщение, актор не блокирует его, а уступает поток для обработки следующего сообщения.
• Прозрачность местонахождения акторов (location transparency) обеспечивает горизонтальное масштабирование.
Нетронутые темы
• Кластеризация• Персистентность• Мониторинг жизненного цикла акторов• Конечные автоматы (FSM)• Spray – фреймворк для разработки HTTP/REST приложений• Тестирование акторов• Работа с TCP/UDP• Агенты• Интеграция с Apache Camel• ...
Спасибо за внимание!