Введение в Akka

Post on 25-Jan-2015

912 views 8 download

description

 

Transcript of Введение в 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• ...

Спасибо за внимание!