Escolar Documentos
Profissional Documentos
Cultura Documentos
A complexidade aumenta quando estas threads precisam acessar a mesma informao (estado
compartilhado).
Para resolver este problema usamos blocos synchronized, que acabam gerando um gargalo na aplicao,
pois as instrues protegidas por synchronized so executadas apenas por uma thread por vez. Com o
crescimento da aplicao, consequentemente a quantidade de blocos synchronized cresce, eventualmente
causando lentido e casualmente um dead-lock.
Derek Wyatt, engenheiro de software atuante na comunidade Akka/Scala, no seu livro Akka Concurrency
[4], nos brinda com uma definio perfeita sobre este modelo: Em concorrncia com estado compartilhado
tendemos a criar os problemas primeiro, ento resolv-los usando primitivas de sincronizao.
. A Figura 1 ilustra o modelo de estado compartilhado e seus gargalos.
Criado com o foco em aplicaes distribudas e otimizado para operar de maneira standalone, o Akka prov:
Atores: conforme explicado anteriormente, a base do Akka sua implementao do modelo de atores;
Futures: apesar de podermos utilizar atores para resolver praticamente todos os problemas, s vezes pode
ser mais conveniente utilizar Futures. Os futures do Akka so muito parecidos com a implementao padro
do java.util.concurrent, porm possui integrao com atores;
Scheduler: integrado ao contexto de atores, o Akka permite o agendamento de tarefas usando vrios tipos
de schedulers.
Devido a estas caractersticas e recursos, o Akka vem sendo adotado por vrias empresas de porte. Dentre
elas, podemos citar a Amazon, Autodesk, Blizzard, VMware, dentre outros.
Hello World Primeiros passos
Uma forma de desenvolver aplicaes com o Akka fazer com que o framework seja uma dependncia do
projeto e usar a prpria aplicao para iniciar o sistema de atores. Alm dessa, existe outra opo para
desenvolver tais aplicaes: usando o Typesafe Activator, que um software que depois de instalado pode
ser utilizado para criar e executar aplicaes diversas baseadas em templates.
O Activator na verdade oferece muito mais, como por exemplo, uma IDE totalmente web para manuteno
dos projetos gerados por ele. Tanto o Activator quanto as dependncias do Akka podem ser obtidos na
pgina de downloads do site do prprio Akka [6]. Neste artigo, adotaremos a primeira opo, pois esta
permite uma compreenso melhor do funcionamento do framework.
Para criar uma aplicao usando o Akka podemos usar uma ferramenta de build como o Maven ou o SBT.
Aqui optaremos pelo Maven, por ele ser mais empregado pela comunidade. Antes de criar a aplicao, voc
deve ter o Eclipse com o plugin do Maven (m2e) instalado, ou apenas o Maven, caso voc prefira realizar o
build pela linha de comando.
Dito isso, crie um diretrio para hospedar o cdigo da aplicao, e depois crie o pom.xml com a dependncia
do Akka, conforme indica a Listagem 1.
Listagem 1. pom.xml base de uma aplicao com Akka.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.devmedia.akka</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.10</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
</project>
Em seguida, crie uma classe no diretrio src/main/java chamada Start. Esta classe ser responsvel por
iniciar a aplicao e o sistema de atores do Akka. Inicialmente ela deve estar vazia, conforme a Listagem 2.
Listagem 2. Classe que ir iniciar o sistema de atores do Akka.
public class Start {
public static void main(String[] args) {
// aqui inicializaremos o Akka
}
}
Agora, antes de pormos as mos na massa, vamos analisar o conceito de sistema de atores do Akka.
Actor System O container de atores
Como principal caracterstica, o Akka implementa um modelo de atores. De modo simples, este modelo diz
que tudo so atores. Sendo assim, a lgica da aplicao que normalmente programada em classes que
costumamos chamar de o modelo da aplicao ou os componentes de negcio, deve ser estruturada em
um ou vrios atores que se comunicam de maneira assncrona atravs da troca de mensagens, todos sob a
gesto do actor system.
Veremos adiante que para criar um ator basta estender uma determinada classe do Akka e usar a sua API
para solicitar a instanciao. Contudo, para dar vida aos atores, precisamos antes inicializar o container
Akka, ou seja, iniciar o actor system.
O actor system o ponto de partida de toda aplicao, por isso, deve ser criado assim que ela iniciar. Em
relao ao nmero de actor systems, uma aplicao pode iniciar vrios deles, porm este tipo de design no
indicado por questes de organizao.
Para criar um actor system devemos usar a classe akka.actor.ActorSystem, como demonstra a Listagem 3.
Listagem 3. Criao do actor system.
import akka.actor.ActorSystem;
public class Start {
public static void main(String[] args) {
// Criao de um Actor System, container Akka.
ActorSystem system = ActorSystem.create("HelloSystem");
}
}
Podemos observar que o construtor do actor system recebe um texto como parmetro para definir o seu
nome. Este nome deve ser nico por aplicao e tambm deve ser nico por container, caso a aplicao seja
instalada em um microcontainer Akka. Como veremos adiante, o nome do actor system tambm ir compor
o path de localizao do ator.
6
Deixando os detalhes de criao do logger para depois, podemos reparar que a classe de um ator simples.
importante notar que a mensagem pode ser qualquer objeto Java, porm razovel que este implemente
Serializable (apesar do Akka no obrigar) para no termos problemas em um ambiente distribudo, onde
mensagens so serializadas para trafegar pela rede.
Voltando para a classe Start, agora que temos um ator implementado, podemos usar o actor system para
efetivamente dar vida a este ator. Assim, usamos o mtodo actorOf() de actor system para criar o ator. Este
mtodo recebe como parmetro uma instncia de akka.actor.Props e uma String com o nome que o ator
ter no actor system. Props uma classe que define um objeto de configurao, usado apenas para criar
atores.
Para a construo do objeto Props, usamos o mtodo esttico Props.create(), passando como parmetro a
classe do ator. Existem muitas maneiras de construir Props, mas para o nosso exemplo, usaremos a forma
mais simples. A Listagem 5 mostra a classe Start alterada, com a criao do ator.
Listagem 5. Criando o ator EcoActor.
import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import akka.actor.Props;
public class Start {
public static void main(String[] args) {
// Criao de um Actor System, que o container Akka.
ActorSystem system = ActorSystem.create("HelloSystem");
// Criando o ator EcoActor
ActorRef actor = system.actorOf(Props.create(EcoActor.class), "eco");
}
}
importante perceber que o que obtemos ao criar um ator um ActorRef e no um objeto com a classe do
ator propriamente dita (EcoActor). Nesta informao reside um grande conceito do Akka e do modelo de
atores: os detalhes do ator no so expostos para quem envia a mensagem, pois o estado interno deste deve
ser visvel apenas para ele mesmo. Isso fica bem claro na implementao do ator.
Basta percebermos que o cdigo processado por ele (sua lgica de negcio) se restringe ao mtodo
onReceive(), que chamado pelo Akka para processar as mensagens. Como as mensagens so processadas
uma por vez (ou seja, apenas uma thread por vez), no existe nenhuma necessidade de blocos synchronized.
Com isso, locks e deadlocks no existem neste modelo.
ActorRef a interface para comunicao com o ator criado e o mtodo ActorRef.tell() utilizado para
enviar uma mensagem para este ator. Este mtodo recebe como parmetro a mensagem e o ator que a est
enviando. Como estamos enviando a mensagem do mtodo main() da casse Start (isto , no um ator que
est enviando a mensagem), usaremos ActorRef.noSender(), que um mtodo auxiliar que retorna uma
constante para identificar ao actor system que no existe nenhum ator origem. A Listagem 6 mostra o envio
dessa mensagem ao ator.
Listagem 6. Enviando uma mensagem para EcoActor.
import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import akka.actor.Props;
public class Start {
public static void main(String[] args) {
// Criao de um Actor System, que o container Akka.
ActorSystem system = ActorSystem.create("HelloSystem");
// Criando o ator EcoActor
ActorRef ecoActor = system.actorOf(Props.create(EcoActor.class), "eco");
// Enviando a mensagem ao ator
ecoActor.tell("Al Mundo com Atores", ActorRef.noSender());
}
}
Com essas duas classes j possvel ver o Akka em ao, mas antes, precisamos compilar a aplicao. Para
isso, usaremos o Maven atravs do comando mvn clean install. Em seguida, executaremos a aplicao
tambm pelo Maven, com o plugin exec:java, como indica a Listagem 7.
Listagem 7. Executando a aplicao.
$ mvn clean install exec:java -Dexec.mainClass=Start
.
. (detalhes do build omitidos)
.
[INFO]
[01/01/2014
19:00:00.384]
[HelloSystem-akka.actor.default-dispatcher-3]
[akka://HelloSystem/user/eco] Mensagem recebida: Al Mundo com Atores
Note que a mensagem enviada (Al Mundo com Atores) foi processada pelo ator e impressa no console
conforme esperado. O log do actor system ainda informa o timestamp, o nome da thread e o nome do ator
que est logando.
Falaremos destes detalhes adiante. Para interromper a execuo, digite Ctrl+c, caso contrrio o actor system
continuar executando.
8
Antes de partir para a modelagem de uma aplicao mais prxima do real, vamos apresentar algumas
funcionalidades do Akka usando o tradicional Hello, World!.
Atores criando atores
Conforme descrito anteriormente, um ator pode iniciar (ou criar) outros atores, estabelecendo uma
hierarquia entre eles. Esta hierarquia definida de acordo com a modelagem da aplicao. Contudo, nada
impede de modelarmos uma aplicao usando o Akka e criar todos os atores na raiz do actor system, como
fizemos com EcoActor.
Apesar dessa possibilidade, por questes de organizao e de gesto de excees, importante definir uma
estrutura coesa de atores e normalmente isso implica em vrios nveis.
Para criar um ator a partir de outro, indicado faz-lo dentro do mtodo preStart() de UntypedActor,
apesar de nada impedir de fazermos isso sob demanda, ou seja, na primeira vez que o ator for utilizar o ator
filho.
O actor system executa o prestar() (dentre outros que ainda analisaremos do ciclo de vida dos atores) antes
de completar a inicializao de um ator. Sendo assim, este um excelente lugar para inicializar eventuais
estados internos do ator, como por exemplo, dependncia de outros atores ou atributos privados utilizados
por ele.
Tendo em vista estas informaes, vamos alterar o cdigo da aplicao exemplo para que o EcoActor crie
um ator na sua inicializao e repasse toda mensagem recebida por ele para este ator filho.
Para isso, declare uma classe chamada ChildActor com o mesmo cdigo de EcoActor (ver Listagem 4).
Agora, devemos alterar EcoActor, sobrescrever o mtodo preStart() e instanciar o ator filho, do tipo
ChildActor. Assim como a criao de um ator via actor system, para instanciar um ator dentro de outro
usamos o mtodo actorOf() do contexto do ator.
Este contexto obtido atravs do mtodo getContext(). A Listagem 8 mostra a implementao do mtodo
preStart() e a criao do ator filho, bem como o repasse das mensagens recebidas em onReceive().
Listagem 8. Criando um ator filho via getContext().
public class EcoActor extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
//Declaramos o ator filho como atributo de EcoActor
private ActorRef childActor;
@Override
public void preStart() throws Exception {
super.preStart();
//Na inicializao do ator, instanciamos o ator filho
childActor = getContext().actorOf(Props.create(ChildActor.class), "childOfEco");
}
@Override
public void onReceive(Object msg) throws Exception {
log.info("Mensagem recebida: " + msg);
//Repassamos a mensagem recebida para o ator filho
childActor.tell(msg, getSelf());
}
Para ver os resultados destas alteraes, devemos repetir os comandos de compilao e execuo via Maven,
como mostra a Listagem 9.
Listagem 9. Executando a aplicao.
$ mvn clean install exec:java -Dexec.mainClass=Start
.
. (detalhes do build omitidos)
.
[INFO]
[01/01/2014
10:29:26.722]
[HelloSystem-akka.actor.default-dispatcher-2]
[akka://HelloSystem/user/eco] Mensagem recebida: Al Mundo com Atores
[INFO]
[01/01/2014
10:29:26.722]
[HelloSystem-akka.actor.default-dispatcher-3]
[akka://HelloSystem/user/eco/childOfEco] Mensagem recebida: Al Mundo com Atores
O log exibido no resultado mostra claramente a hierarquia dos atores, exposta pelo path do ator. Repare que
a mensagem foi recebida por EcoActor, que tem o path akka://HelloSystem/user/eco, e repassada ao ator
filho, que tem o path akka://HelloSystem/user/eco/childOfEco.
O path de um ator uma URI nica que o identifica. Ela composta pelo protocolo (akka://), o identificador
do actor system (HelloSystem), pela raiz dos atores criados pela aplicao (user), o identificador do ator pai
(caso exista) e o identificador do prprio ator. Sendo assim, na estrutura de atores criada pela aplicao
exemplo, temos dois atores identificveis por dois paths:
akka://HelloSystem/user/eco
akka://HelloSystem/user/eco/childOfEco
O identificador do actor system (HelloSystem) definido no start da aplicao, e o identificador de cada ator
definido no momento de sua criao. Apenas o prefixo user algo imposto pelo Akka, pois ele define a
raiz da hierarquia dos atores. Na prtica, todos os atores criados pelas aplicaes desenvolvidas com Akka
ficaro sob o guardio chamado user. Existem guardies disponveis para diversas finalidades, a saber:
/user: o guardio de todos os atores criados pelo cdigo da aplicao desenvolvida usando Akka;
/system: Este o guardio de atores criados pelo prprio Akka, para manter o funcionamento do
framework;
/deadLetters: um ator que recebe as mensagens enviadas para atores inexistentes, ou atores que no
esto em funcionamento;
/temp: o guardio de atores criados para necessidades temporrias, isto , so atores de vida curta;
/remote: Este um guardio para paths virtuais, que representam atores remotos, ou seja, que estejam
instanciados em outras JVMs.
Mensagens tipadas
muito comum no desenvolvimento de aplicaes Akka um determinado ator ser programado para receber
vrios tipos de mensagens e realizar processamentos distintos de acordo com a mensagem. Neste caso o
padro adotado utilizar classes diferentes para representar os diferentes tipos de mensagens.
A vantagem de usar este padro que ele deixa claro e visvel os tipos de mensagens que um ator processa e
principalmente os que ele no processa. Para exemplificar, vamos refatorar nosso cdigo para enviar uma
10
mensagem tipada para nossos atores e refutar qualquer outra mensagem. Para isto, criaremos uma classe
chamada HelloMessage, conforme a Listagem 10.
Listagem 10. Classe que representar a mensagem.
public class HelloMessage {
}
Como pode ser observado, esta classe no contm nenhum estado interno, pois o seu propsito apenas
identificar um tipo de mensagem. Dito isso, depois de cri-la, devemos alterar os nossos atores, para deixar
claro que eles recebem e processam apenas esse tipo de mensagem. Sendo assim, o mtodo receive() de
EcoActor deve ficar como exposto na Listagem 11.
Listagem 11. Alterao do mtodo onReceive() de EcoActor.
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof HelloMessage) {
log.info("Mensagem recebida: " + msg);
// repassamos a mensagem recebida para o ator filho
childActor.tell(msg, getSelf());
}
else {
// informa ao actor system que este ator no processa esta mensagem
unhandled(msg);
}
}
Repare que usamos instanceof para filtrar as mensagens processadas de acordo com o tipo destas. Neste
cdigo importante ressaltar a chamada ao mtodo unhandled(), que deixa claro ao actor system que esta
mensagem no foi processada pelo ator. Deste modo, ela ser considerada pelo actor system para eventuais
tratamentos futuros.
Se o mtodo unhandled() no for chamado, o actor system no tem como saber que a mensagem no foi
processada. Para dar continuidade ao exemplo, devemos alterar tambm ChildActor, para receber apenas
mensagens do tipo HelloMessage. Veja a Listagem 12.
Listagem 12. Alterao do mtodo onReceive() de ChildActor.
@Override
public void onReceive(Object msg) throws Exception {
if (msg instanceof HelloMessage) {
log.info("Mensagem recebida: " + msg);
}
else {
unhandled(msg);
}
}
Ao executarmos a aplicao (Listagem 9) aps essas alteraes, ser possvel notar que nenhum log ser
exibido. Isso facilmente explicado pelo fato de nossos atores agora apenas processarem mensagens do tipo
HelloMessage e a mensagem enviada pela classe Start ser do tipo String. Para corrigir isso, devemos
alterar a classe Start de acordo com a Listagem 13.
Listagem 13. Alterando o envio da mensagem em Start.
11
Com essa simples mudana, ao reexecutar a aplicao teremos o resultado apresentado na Listagem 14.
Listagem 14. Resultado da reexecuo aps as alteraes.
$ mvn clean install exec:java -Dexec.mainClass=Start
.
. (detalhes do build omitidos)
.
[INFO]
[02/02/2014
11:22:00.668]
[HelloSystem-akka.actor.default-dispatcher-3]
[akka://HelloSystem/user/eco] Mensagem recebida: HelloMessage@7ee14288
[INFO]
[02/02/2014
11:22:00.668]
[HelloSystem-akka.actor.default-dispatcher-4]
[akka://HelloSystem/user/eco/childOfEco] Mensagem recebida: HelloMessage@7ee14288
a. Continuar. Simplesmente ignorar que a exceo aconteceu e continuar com a vida do ator, processando
as mensagens normalmente;
b. Reiniciar o ator. O mtodo preRestart() executado na instncia em execuo, uma nova instncia
criada e o mtodo postRestart() executado na nova instncia. O path do ator se mantm, assim como o
UID, caracterizando uma mesma encarnao;
c. Encerrar o ator (morte). O ator pode ser terminado pelo seu supervisor ou pelo envio de uma mensagem
que o finalize (existe uma mensagem no Akka do tipo akka.actor.PoisonPill, que ao ser consumida por um
ator causa sua interrupo). Neste ponto, o mtodo postStop() executado. Caso o ator seja iniciado
novamente, uma nova encarnao ser criada, tendo o mesmo path, porm com um novo UID.
new
Function<Throwable,
Para implementar uma estratgia de recuperao em um supervisor (um ator com filhos), devemos
sobrescrever o mtodo supervisorStrategy(), que por sua vez, deve retornar uma instncia de
SupervisorStrategy. No caso do cdigo exemplo, foi escolhida a opo akka.actor.OneForOneStrategy.
Isso significa que a estratgia de recuperao ser aplicada apenas no ator que lanou a exceo.
Para construir um objeto do tipo OneForOneStrategy devemos informar trs parmetros no construtor:
maxNrOfRetries (nmero mximo de tentativas de reincio do ator), withinTimeRange (intervalo entre a
primeira e a ltima tentativa de reincio) e decider.
Deixando de lado os dois primeiros parmetros, o decider, ou decisor, deve ser uma implementao da
interface akka.japi.Function e possuir apenas o mtodo chamado apply(), que recebe como parmetro um
Throwable e retorna um akka.actor.SupervisorStrategy.Directive (explicado adiante).
Quando a aplicao estiver em execuo e um determinado ator lanar uma exceo no processamento de
uma mensagem, o Akka ir executar o mtodo apply() do objeto decider definido na estratgia do ator pai
passando como parmetro a exceo lanada e obtendo como retorno uma diretiva a ser utilizada para tomar
a devida ao.
As diretivas podem ser do tipo:
resume: Ignora a exceo atirada pelo ator supervisionado (filho). A mensagem simplesmente deixa de ser
processada e o ator continua processando normalmente outras mensagens;
restart: Reinicia o ator, conforme explicado anteriormente, na descrio do ciclo de vida do ator;
escalate: Repassa a deciso do que deve ser feito para o supervisor diretamente acima;
stop: Finaliza (mata) o ator.
Assim como qualquer paradigma de desenvolvimento, o modelo de atores tem as suas peculiaridades. Ao
desenvolver uma aplicao usando este modelo, devemos estruturar a aplicao em atores, considerando que
a comunicao entre eles assncrona e realizada atravs troca de mensagens. Depois de uma vida inteira
desenvolvendo aplicaes com o modelo tradicional de concorrncia, inicialmente este paradigma poder
parecer um pouco confuso.
Para facilitar o aprendizado a respeito, existe uma grande quantidade de material na internet relacionada
modelagem de atores, mas para ficar apenas no universo do Akka, uma boa referncia o site Let It Crash
[7], mantido pela comunidade. Este site possui trechos de cdigo, artigos, discusses e outros contedos
relacionados ao desenvolvimento com Akka.
Prximos passos
Este artigo procurou introduzir os princpios bsicos do paradigma de concorrncia baseado em atores e o
framework Akka. Por esse motivo, ainda existe muito a ser explorado.
Pensando em facilitar os prximos passos, e direcionando nosso foco para o Akka, a seguir listamos os
tpicos mais importantes que o leitor pode se aprofundar:
Mailbox: Apesar do Akka abstrair a existncia deles, cada instncia de ator possui uma caixa de
mensagens, ou mailbox. Existem vrios tipos de customizaes que podem ser feitas com este recurso, como
por exemplo, podemos criar uma caixa de mensagens para vrios atores, balanceando o consumo, realizando
uma espcie de load balancing de processamento de mensagens;
Dispatchers: So implementaes de ExecutionContext que regem a criao das threads de
processamento de mensagens, schedulers e todo o funcionamento do framework. Tambm possui um alto
grau de customizao;
Persistncia: possvel configurar o Akka para persistir o estado interno de um ator, inclusive o seu
mailbox. Sendo assim, caso a aplicao caia ou seja desligada indevidamente, no restart do container este
estado recuperado. Este mdulo ainda experimental no Akka e foi introduzido na verso 2.3.0 do
framework;
Extenses: O Akka oferece um mecanismo padro para criar extenses. Com ele podemos atuar sobre
elementos centrais do framework, como o Actor system, para modificar o seu comportamento.
O modelo de concorrncia baseado em threads e estado compartilhado tem atendido razoavelmente as
necessidades do mercado, porm, quando nos deparamos com o desafio de construir uma aplicao que
suporte alta concorrncia e alta disponibilidade, importante considerarmos um modelo baseado em
mensagens assncronas.
No ecossistema do Java, o Akka a ferramenta ideal para esta finalidade e tem se tornado mais completo a
cada release. Alm de suas qualidades tcnicas, seguro apostar no Akka no apenas por tratar-se de uma
tecnologia suportada e mantida por uma empresa do porte da Typesafe, mas tambm por ser um excelente
framework, leve, bem estruturado, modular e muito fcil de escalar.
Links
[1] Pgina da API de concorrncia do Java.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/packagesummary.html
15
16