SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
1.
Entendendo o modelo de
atores
programação concorrente e distribuída de
verdade
Paulo “JCranky” Siqueira
jcranky.com / @jcranky
2.
Paulo "JCranky" Siqueira
• desenvolvedor Java e Scala
• consultor independente
• instrutor globalcode
– e criador do curso Core Scala
• um dos fundadores dos Scaladores
7.
1a Opção na JVM: Threads
● Forma “natural” para concorrência
– Um processo, pelo menos uma thread,
possivelmente várias
– Temos que controlar “na mão” essas
threads
8.
Em tempo: o processador!
● Para tarefas intensas, não importa
quantas threads...
● Concorrência real, só mais de um núcleo
– (o que não é difícil hoje em dia)
11.
Assincronia
● para performance
● para melhorar a resposta do sistema
● controlar threads “na mão” começa a
complicar...
12.
Dividir para conquistar
● dividir as tarefas
– Threads diferentes podem executá-las
● temos que pensar diferente
13.
estado compartilhado... ??
val numeros = (1 to 100).toList
def divSequential = {
var div = 1000d
numeros.foreach(x => div = div / x)
println(div)
}
14.
estado compartilhado... ??
// qual o resultado?
1 to 10 foreach(x => divSequential)
15.
estado compartilhado... ??
def divParallel = {
var div = 1000d
numeros.par.foreach(
x => {
div = div / x
Thread.sleep(10)
})
println(div)
}
16.
estado compartilhado... ??
// qual o resultado?
1 to 10 foreach(x => divParallel)
18.
Nossa nova filosofia
● Podem esquecer tudo, menos isso!
● mutabilidade isolada
● imutabilidade compartilhada
19.
Na mão? Mesmo?
● Java 5 / 6 trouxeram diversas melhorias
– controlar Threads não é mais tão difícil
– mas ainda assim, não é trivial
20.
Abstração!
• que tal abstrair programação concorrente?
• porque nos preocupar com Threads?
– e com locks?
– e em fazer isso direito...
21.
Enter the “Actor Model”
• entidades independentes
– estado
– comportamento
– mailbox
• como uma classe em OO...
• ... mas mais "pura" (e com a tal da mailbox)
23.
Actor Model
mensagens mailbox
Comportamento
Estado
24.
Actor Model
• comunicação com troca de mensagens
• "event-driven"
• sempre assíncrono
25.
Akka 2.0
• inspirado em Erlang
• abstrai preocupações baixo nível
• pensamos mais nos problemas de negócio
26.
Akka 2.0
• API Java e Scala
• Actor Model !
• Open Source
27.
Abstração == Overhead?
• overhead do Akka é ínfimo
• extraído da página oficial:
"50 million msg/sec on a single machine."
• obviamente, em um micro-benchmark...
• em uma máquina com 48 núcleos
29.
Onde estão as Threads ??
• Akka cria as threads necessárias
• ator != thread
• atores usam threads disponíveis
• akka fornece uma thread para o ator
• sem locks
30.
Divisão de trabalho
• threads usando os "cores" disponíveis
• fork-join por padrão
– com algumas melhorias que devem estar
no Java 8...
• ... mas já está embutido no Akka
• tudo configurável
31.
config. extraída da documentação:
forkjoinexecutor {
# Min number of threads to cap factorbased parallelism number to
parallelismmin = 8
# Parallelism (threads) ... ceil(available processors * factor)
parallelismfactor = 3.0
# Max number of threads to cap factorbased parallelism number to
parallelismmax = 64
}
33.
Imutabilidade
• não precisamos de locks
• nunca teremos deadlocks
34.
Imutabilidade
• Não sabe trabalhar com objetos imutáveis?
• Sabe sim!
– String? BigDecimal? Integer?
35.
Ou mutabilidade isolada
• estado interno do ator é mutável
– mas apenas ele mesmo tem acesso
• chave para um sistema concorrente correto
36.
Uma mensagem
case class SendToS3(fileName: String)
37.
Um ator
class S3SenderActor extends Actor {
def receive = {
case SendToS3(fileName) =>
// lógica de negócios aqui
}
}
38.
Lidando com alteração de estado
• Atores não devem compartilhar estado
– somente ele mesmo acessa seus dados
• Acesso a BD poderia ser enfileirado
– ou atores com "fatias" de dados
39.
Criando atores
• métodos / classes especiais
• criados abaixo de um nó principal
– ... ou relativos ao contexto atual
• todo ator tem um "pai"
• resultando em uma hierarquia de atores
40.
Criando um ator
val system = ActorSystem("MyActorSystem")
// ator com construtor default
val ator =
system.actorOf(Props[S3SenderActor])
// ator com outro construtor
val ator2 =
system.actorOf(Props(new Ator(<params>)))
41.
Enviando uma mensagem
ator ! SendToS3(“imagem.gif”)
42.
Protegendo o estado
val meuAtor = new Ator(<params>)
// erro em runtime
val ator2 =
system.actorOf(Props(meuAtor))
43.
Expandindo a hierarquia de atores
● todo ator tem um context
● esse context pode criar novos atores
● que serão "filhos" do ator atual
44.
Expandindo a hierarquia de atores
context.actorOf(Props[S3SenderActor])
45.
"let it crash"
• tolerância a falhas
• não evitamos que atores quebrem
• decidimos o que fazer quando falhar
– o supervisor decide o que fazer
• todo ator é supervisionado (2.0+)
50.
Config. de tolerância a falhas
override val supervisorStrategy =
OneForOneStrategy(
MaxNrOfRetries = 10,
withinTimeRange = 1 minute) {
case _: ArithmeticException ⇒ Resume
case _: NullPointerException ⇒ Restart
case _: IllegalArgumentException ⇒ Stop
case _: Exception ⇒ Escalate
}
51.
Dividir para conquistar, outra vez
• quebrar tarefas em pedaços pequenos
• roteadores são uma forma de fazer isso
– definem grupos de atores
– e divide as mensagens
52.
Exemplo de roteador
val s3SenderRouter = system.actorOf(
Props[S3SenderActor].withRouter(
SmallestMailboxRouter(2))
)
53.
Programação distribuída – porque?
● porque sim?
● não faça
54.
Programação distribuída – porque?
● processador local não dá conta sozinho
● aumentar tolerância a falhas
● melhorou =)
58.
Atores remotos
● akka suporta atores remotos
● ajustamos a configuração
● ajustamos o código para usar a config.
59.
Atores remotos
● código cliente de atores continua igual
● para o emitente da mensagem, atores
remotos e locais são iguais
● “Atores locais são como otimizações”
60.
Ator a partir de configuração
context.actorOf(Props[MeuAtor]
.withRouter(FromConfig()),"nomeator")
61.
Tá difícil mesmo assim?
Akka Cluster
●
● atores remotos, com menos configuração
● mais inteligência para suportar nós
entrando e saindo do cluster
● em testes, previsto para Akka 2.1
62.
Atores remotos
● cuidados básicos:
● quantidade de mensagens
● tamanho das mensagens
● delay em relação a atores locais
● tudo afeta o design dos atores
● portanto, o código
63.
bonus: composição async com Futures
● possibilita código totalmente sem block
● usamos callbacks
● executados quando uma resposta chegar
● ou Futuros, e compomos o resultado final
64.
bonus: composição async com Futures
val colorFuture = for {
r < (rPartChooser ? FindColorPart)
.mapTo[ColorPartFound]
g < (gPartChooser ? FindColorPart)
.mapTo[ColorPartFound]
b < (bPartChooser ? FindColorPart)
.mapTo[ColorPartFound]
} yield (r.value, g.value, b.value)
65.
Projeto Open Source: Lojinha
• Scala
• Akka 2.0
• Play Framework 2.0
66.
Projeto Open Source: Lojinha
• um ator para lances de cada produto
• aguentaria milhões de produtos
• da página oficial do Akka:
"Small memory footprint; ~2.7 million actors per
GB of heap."
67.
Atores da Lojinha
Play Akka
System
Image
Master
Thumb
Bid Actor
Router
S3
Sender
Router
Image
Process
Thumb
Bid Actor S3
Actor
Sender
Actor
um para cada produto vários, conforme configurado
vários, conforme configurado
68.
Referências
• Programming Concurrency on the JVM,
Venkat Subramariam
• akka.io