Пользователь
0,0
рейтинг
5 августа 2011 в 18:43

Разработка → Akka для Java разработчика (часть 1)

Java*
В последнее время появилось довольно много языков программирования, которые используют для JVM как платформу для выполнения. Одним из наиболее «горячих» тем для обсуждения в последнее время является Scala. В этой статье я не буду рассматривать заслуженно это или нет, просто хочу рассказать как можно использовать средства этого языка используя Java и не написав ни одной строчки на Scala.

Концепция actor'ов родилась задолго до появления Scala и была реализована на различных языках программирования, в том числе и Java. Если формулировать в двух словах, то actor это объект, который занимается обработкой посылаемых ему сообщений. Важно чтобы сообщения были не изменяемые (immutable) и последовательность их обработки соответствовала порядку их поступления. Собственно это основа концепции, а дальше уже идут особенности реализации — сохраняется ли очередь сообщений для объекта, есть ли возможность поменять код обработки сообщений в процессе работы, привязывается ли actor к потоку или пулу потоков, возможность отправки сообщений на actor'ы на других машинах и т.д.

Вообще если кто знаком с JEE, то actor очень похож на Message bean, только очередь сообщений живет внутри JVM.
Автор языка Scala решил реализовать концепцию actor'ов для языка Scala и назвал ее Akka и развивает этот проект как коммерческий с открытыми исходниками. Проект развивается довольно активно, текущая версия 1.1.3 и идет работа на версией 2.0, которую собираются выпустить осенью. Там будет поддержка распределенных очередей и лучшая организация кластеров.

Почему я выбрал именно Akka? Во-первых хотелось пощупать новый способ борьбы со сложностью распределенных вычислений и high-load систем. Второе и очень важное это документация. Про actor'ы сейчас активно только два проекта JetLang и Akka, и Akka подкупает подробной документацией как для Java, так и для Scala, с примерами кода и подробными объяснениями где и чего. И собственно третья причина, это отсутствие необходимости изучать новый язык для того чтобы получить преимущества использования технологии.

Итак, начнем.

Для того, чтобы писать что-то с использованием Akka необходимо скачать дистрибутив akka.io/downloads или подключить в maven следующие библиотеки.

<repository><br> <id>akka.repository</id><br> <name>Akka Maven Repository</name><br> <url>akka.io/repository</url><br></repository><br><br><dependency><br>  <groupId>se.scalablesolutions.akka</groupId><br>  <artifactId>akka-actor</artifactId><br> <version>1.1.3</version><br></dependency><br><br><dependency><br>  <groupId>se.scalablesolutions.akka</groupId><br>  <artifactId>akka-typed-actor</artifactId><br> <version>1.1.3</version><br></dependency><br><br>* This source code was highlighted with Source Code Highlighter.


В Akka реализованы два типа actor'ов — UntypedActor и TypedActor. UntypedActor предоставляет стандартный интерфейс для обработки сообщений через метод onReceive

import akka.actor.UntypedActor;

public class TestUntypedActor extends UntypedActor {

public void onReceive(Object message) throws Exception {
  if (message instanceof MySuperMessage) {
  System.out.println("We've got super message - " + message);
 } else if (message instance of String) {
  System.out.println("Process string - " + message);
}
}


* This source code was highlighted with Source Code Highlighter.


Таким образом мы получим множество классов с которыми можно взаимодействовать через отправку сообщений.
Для TypedActor необходимо сначала описать интерфейс взаимодействия, а потом его реализовать с помощью actor'а.

interface RegistrationService {
void register(User user, Credentials cred);
User getUserFor(String username);
}

import akka.actor.TypedActor;

public class RegistrationServiceImpl extends TypedActor implements RegistrationService {
public void register(User user, Credentials cred) {
  ... // Регистрация пользователя
}

public User getUserFor(String username) {
  ... // Реализация поиска пользователя
 return user;
}
}


* This source code was highlighted with Source Code Highlighter.


В этом случае у нас будет множество классов с интерфейсами для бизнес-логики и реализацией с помощью actor'ов. И если вы в вашем приложении вы используете Spring, то интегрирование actor'ов в существующие приложение будет на уровне конфигурации и пройдет практически безболезненно (в следующих статьях я покажу как).

Итак у нас есть описания actor'ов и теперь необходимо создать как-то экземпляры классов, потому что простой new тут не работает. Для того чтобы создавать UntypedActor используется фабрика Actors

ActorRef testActor = Actors.actorOf(TestUntypedActor.class);

* This source code was highlighted with Source Code Highlighter.


После создания фабрика возвращает ссылку (ActorRef) на конкретный экземпляр actor'а, которую можно сериализовать или отправить на другую машину для отправки сообщения конкретному экземпляру (в случае remote actor).

Для создания TypedActor используется следующий код

// Первый параметр - интерфейс, второй - реализация
UserService service =
(UserService) TypedActor.newInstance(UserService.class, UserServiceImpl.class);

// Или если у TypedActor нет конструктора без параметров

UserService service = TypedActor.newInstance(UserService.class, new TypedActorFactory() {
public TypedActor create() {
  return new UserServiceImpl("default user storage"));
});


* This source code was highlighted with Source Code Highlighter.


Который возвращает ссылку на интерфейс для общения с actor'ом.

В Akka реализовано три типа отправки сообщений actor'ам — fire-and-forget («отправил и забыл», асинхронная отправка сообщения без ожидания результата), request-reply («вопрос-ответ», отправка сообщения и ожидание ответа, синхронный режим), request-reply-with-future («отправить и получить ответ в будущем», отправка сообщения и получения ответа дальше по коду с помощью специального объекта).

Для UntypedActor все три способа выглядят вот так

actorRef.sendOneWay("Hello"); // Посылает в качестве сообщения строчку

actorRef.sendOneWay(new MySuperObject(), сontext()); // Отправить объект в качестве сообщения. Кроме того будет отправлен context текущего actor'а (из которого происходит вызов), для того чтобы можно было отправить ответ с помощью context().reply()

Object result = actorRef.sendRequestReply("get result"); // Отправить строчку как сообщение и ждать ответ. В методе можно также передать context() и таймаут для ожидания ответа (при истечении таймаута будет ActorTimeoutException)

Future<MyResult> future = actorRef.sendRequestReplyFuture("Hello"); // Отправить сообщение и получить объект для извлечения результатов. Можно передать context() и таймаут. У самого объекта Future есть методы await (ожидание результата), result (получить результат), exception (получить исключение если было)

* This source code was highlighted with Source Code Highlighter.


Для TypedActor способы вызова зависят от сигнатуры метода. Есть метод не возвращает значений значений (void), то неявно используется вызов fire-and-forget. Если возвращается какой-либо объект, то используется request-reply. Если метод возвращает значение Future, то используется request-reply-with-future.

Ну и последнее на сегодня — не забывайте стартовать UntypedActor'ы и останавливать для освобождения ресурсов. Если посылать сообщения не стартовавшему actor'у, то они будут складываться в его очередь и не обрабатываться. Поэтому понять что что-то не работает будет тяжело.
aib @aib
карма
53,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (29)

  • 0
    Интересно! Не подскажите ли примеры открытых проектов с использованием Akka? Было бы любопытно посмотреть… Жду продолжения статьи.
  • 0
    Насколько сильна поддержка security в akka? Это всё так же хорошо как и в resteasy или spring или нет?
    • 0
      А что понимается под security? Безопасность для вызова remote actors или что-то другое?
  • 0
    Да, это, наверное первый момент, remote actors as protected resources, что-то такое, поддержка oauth, например.
    Ну и возможно секурные аннотации by Akka?
    • 0
      Похоже я не совсем так объяснил что это и для чего. Акторы это внутренние объекты, из которых строится в основном ядро бизнес логики. Remote actor позволяют размазывать это ядро по нескольким машинам. Но это никак не относится к уровню представления, например rest сервисам.
      Безусловно rest api позволяет дергать actor для выполнения каких-то действий, но oauth и подобное, полностью ортогонально концепции actor и никак с ними не пересекается.
      • 0
        Спасибо, смутила схожесть сообщений actor'ам c другими вещами.
  • –2
    Очень интересно, но High Load как мотивации для ознакомления и ограничение возможности масштабирования из-за использования внутри VM как-то между собой не срастаются. Может все-таки JMS (с MesageBeans) более подходящий инструмент?
    • 0
      Не совсем понял где я написал что ограничена возможность масштабирования из-за использования jvm? Jms это как вариант, но если вы заставите все классы вашего приложения для каждого вызова метода дергать jms, то ваше приложение умрет смертью храбрых. Кроме того насколько я помню надо будет быть очень аккуратным с выбором сервера и драйвера к нему, потому что очень многие драйвера на каждый listener создают новый поток для обработки, что черевато.
      • 0
        Не использование jvm, а изспользования внутри jvm. Вы же написали, что очередь сообщений живет внутри JVM. Разве это не накладывает естественное ограничение одной машины?

        Каждый listener в своем потоке — есть такое, по крайней мере ActiveMQ так делает. А чем черевато? Я просто только что перенес логику одного проекта на jms, пока проблем не вылезло. Может я недосмотрел или недодумал чего-то?
        • 0
          Во-первых авторы могут взаимодействовать между разными jvm — на одной машине надо просто импортировать актор с другой машины.

          Про один поток на листенер это черевато вот чем — что будет когда у вас система будет работать с 1000 листнеров? Или с 10000? Так как акторы работают в пуле то для них это не имеет значения
  • 0
    А не расскажите какие есть варианты использования акторов? Для highload, как мне казалось, наиболее актуальны stateless системы в силу простоты обеспечения отказоустойчивости и масштабируемости (state в таком случае хранится в отдельном от подсистемы БЛ хранилище, где проблемы отказоустойчивости и масштабируемости решаются уже отдельно).

    Наверное, stateful архитектура актуальна для систем в которых происходит активное взаимодействие между пользователями. В «классических» stateless системах это приводит к огромной нагрузке на запись на БД. Примером могу служить многопользовательские игры. В таком случае применение акторов было бы, как мне кажется, более уместным, чем обмен сообщениями через сервисы-посредники. Я прав? :)
    • +1
      Акторы сами по себе могут быть stateful или stateless, это как вы захотите и как вам будет удобно. Создать новый актор это легкая операция и создавать их можно по мере необходимости из других акторов.
      У самих акторов есть методы postStart preStop чтобы сохранять-восстанавливать состояние по необходимости.
      А игры наиболее интересный с точки зрения пример, где любые другие подходы не тянут. Например, если у нас есть ферма, то для каждого объекта можно создать актор для обработки событий для него. Т.е. для каждого деревца, каждого кустика будет свой concurrent обработчик, который реагирует на действия разных игроков, и при этот система не умрет от огромного количества потоков.
      • 0
        >и при этот система не умрет от огромного количества потоков
        Вот с этого момента можете по подробнее? Как акторы связаны с потоками? На каждый актор создается свой поток или как? Что будет если таких акторов надо 100к создать?
        • +1
          Концепция акторов она как бы надпоточна, т.е. сообщения к одному актору должны обрабатываться последовательно и независимо. Поэтому по умолчанию создаются пулы процессов, которые обрабатывают сообщения. Есть возможность настроить akka так чтобы под какие-то акторы выделялся отдельный поток для обработки.
      • 0
        А евент ориентированные библиотеки типа Drools или Esper для этой цели не подойдут?
    • 0
      Я думаю актор это примитив чуть более низкого уровня. Акторы используются для построения многопоточных систем, как например для этого используются блокировки и STM.
  • 0
    Т.н. «актеры» — основа API в Windows и существуют еще с незапямятных времен (кто помнит обмен сообщениями WM_).

    Каждый актер — это single-threaded объект, доступ к которому осуществляется через очередь сообщений (и различные враперы типа TypedActors). Т.о. не болит голова о синхронизации и блокировках. Все, что внутри объекта используется им эксклюзивно.

    У архитектуры, построенной на актерах есть одно преимущество в то же время недостаток — она асинхронная. То есть в заданный момент времени нельзя определить четко состояние всей системы, что становится проблемой например при ее остановке. Не совсем ясно в какой последовательности останавливать актеров, т.к. они могут зависеть друг от друга. И если актер уже остановлен, а другой пытается послать ему сообщение, он вылетит с ошибкой. Поэтому у асинхронной архитектуры очереди сообщений обычно делают persistent и выносят в отдельный леер (JMS,MQ), чтобы при рестарте компонентов сообщения не терялись.

    Также у асинхронной архитектуры есть пробемы для реализации сложной атомарной операции между актерами (транзакция), поскольку их состояние ни в какой момент несинхронизровано. В Akka для этой цели используют механизм транзакторов, которые могут координировать работу нескольких актеров. Другое решение — использовать Software Transaction Memory, реализация которой в Akka очень примитивна.

    P.S. Еще замечание: использование синхронного request-reply в асинхронной архитектуре, равно как и «request-reply-with-future» — это 99% DEADLOCK. Единственное safe-решение — это request-with-callback.
  • 0
    мне кажется или все эти чудесные концепции появились очень и очень давно?
    мне показалось что абсолютно аналогичный концепт я видел в CORBA, когда логика завязана на EventService.
    • 0
      Ну так это постмодернизм — ничего нового, только обсасывание старых концепций :)
      • 0
        что то в последнее время я начинаю охреневать, от того что концептуальные и прорывные вещи оказываются таковыми только на словах, а на деле, это просто хорошо забытое старое.

        всегда жутко интересно слушать самозабвенные рассуждения об очередной супернавороченой архитектуре, а потом обнаружить полную копию одного из тех 9000+ сервисов CORBA, которые были описаны еще в конце 90-х. может просто у меня старость подкрадывается :)

        опять же, с экторами была чудесная ситуация, когда в самом начале очередной лекции о новомодности экторов, весь полет мысли апологета, был сбит фразой о том что это все было еще в Simula-67 :)
        • 0
          Ну я и не претендую, что я открыл какое-то сокровенное знание, которое никто не знает. И чем больше и больше я погружаюсь в akka тем больше мне начинает нравиться apache camel. Просто изучаю альтернативы и решил поделиться.
          • 0
            та не, к тебе претензий никаких быть и не может.
            уверен, для многих статья будет полезна, за что большая тебе спасиба! все что я написал ранее относится к ситуации в целом и моей реальной жизни. что то на филосовствование просто потянуло, вот и решил поделиться своим наблюдениями.
  • 0
    Где же продолжение? :)
    • 0
      Это первый, который уже не так актуален + я в самой akka разочаровался :)
      • 0
        В чем причина разочарования?
        • 0
          Сложность разработки не компенсируется возможностям или масштабированием. Это подходит для систем, которые не надо останавливать, потому тогда нет проблемы с потерями сообщений. А если решать проблему потери сообщений при остановке (хранение или внешние транзакции), то от акторов остается только фасад, а все кишки обычным синхроном.
          • 0
            У акторов нет встроенной возможности персистить сообщения?
            • 0
              Ну вот во второй вроде появилось, но я не тестировал её.
              • 0
                Понятно, все равно спасибо за статью. Буду искать информацию дальше :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.