Celluloid - Atores, concorrência e relevância para o mundo Ruby

62
Celluloid Atores, concorrência e relevância para o mundo Ruby

Transcript of Celluloid - Atores, concorrência e relevância para o mundo Ruby

Page 1: Celluloid - Atores, concorrência e relevância para o mundo Ruby

CelluloidAtores, concorrência e relevância para o mundo Ruby

Page 2: Celluloid - Atores, concorrência e relevância para o mundo Ruby

@dodecaphonic

Sou @dodecaphonic no Twitter. Fico com essa cara quando o computador não faz o que eu mando.

Page 3: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Todo post no Hacker News desde 2005

"Ruby já era. Não tem como uma linguagem com x sobreviver em um mundo precisando de y."

Page 4: Celluloid - Atores, concorrência e relevância para o mundo Ruby

VÁRIOS X E VÁRIOS Y

X - tipos dinâmicos; Y - software complexo X - execução lenta; Y - software complexo/em escala X - metaprogramação em runtime; Y - software complexo X - ausência de concorrência; Y - software complexo

Page 5: Celluloid - Atores, concorrência e relevância para o mundo Ruby

CONCORRÊNCIA

NÃO É PARALELISMO

Concorrência é a possibilidade de executar duas tarefas que se completarão em períodos que se sobrepõem. Isso não significa que as duas tarefas vão executar ao mesmo tempo — isso aí é paralelismo. Você pode ter concorrência até em máquinas single-threaded.

Page 6: Celluloid - Atores, concorrência e relevância para o mundo Ruby

MAS EM RUBY TUDO É LINDO E FOFO!

Não. Esta é uma área em que Ruby está bem defasado em relação a outras linguagens mais modernas/projetadas com isso em mente.

Page 7: Celluloid - Atores, concorrência e relevância para o mundo Ruby

MRI E SUA TRAVA GLOBAL (GIL)

Em Ruby a gente TEM concorrência — tem uma classe Thread, afinal. Mas é justamente aí onde a trava global do interpretador do Matz, a GIL, atrapalha a história de concorrência no Ruby. Você pode ter concorrência, mas não paralelismo (com a exceção do IO).

Page 8: Celluloid - Atores, concorrência e relevância para o mundo Ruby

TER UMA TRAVA GLOBAL É BOM?

Algumas pessoas (inclusive o ruby-core) consideram a GIL uma funcionalidade fofa dessas, que reduz certas classes de bugs clássicos de threading. Mas olha o custo: meu código roda, mas roda lento, não aproveita a máquina.

Page 9: Celluloid - Atores, concorrência e relevância para o mundo Ruby

‒Yukihiro Matsumoto

"I don't consider myself as the threading guy."

E a solução para isso não vai vir da cabeça do Matz. Ele não se considera apto a resolver o problema. MINASWAN - MINCSWCN.

Page 10: Celluloid - Atores, concorrência e relevância para o mundo Ruby

O que é que Threads oferecem, afinal?

- Threads são contextos de execução que ficam subordinados a um processo do SO. Eles dividem o mesmo espaço de memória, ao contrário de processos;

- Cada Thread pode, potencialmente, ser executado em paralelo pelo seu SO - A troca de contexto entre Threads é mais rápida do que em Processos

Page 11: Celluloid - Atores, concorrência e relevância para o mundo Ruby

THREADS

PROCESSOSvs

Processos e Threads podem atingir os mesmos objetivos. Threads têm como vantagens consumir menos recursos e dividir o mesmo espaço de memória. Processos não exigem que você mude seu jeito de programar significativamente.

Page 12: Celluloid - Atores, concorrência e relevância para o mundo Ruby

DESPERDÍCIO É FEIO

Ou desperdiçaremos por não usarmos nenhum tipo de paralelismo, ou desperdiçaremos por usarmos processos e jogarmos fora memória, inserirmos latência na comunicação interna, etc.

Page 13: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Escreveu, não leu, e lá vem alguém dizer que programar com threads é difícil, até perigoso.

Page 14: Celluloid - Atores, concorrência e relevância para o mundo Ruby

MutexMonitor Condition Variable

Thread-safeRace condition

Deadlock

No Ruby, como em outras linguagens, as preocupações são as mesmas. Os problemas principais são os mesmos.

Page 15: Celluloid - Atores, concorrência e relevância para o mundo Ruby

# From: Working With Ruby ThreadsOrder = Struct.new(:amount, :status) do def pending? status == 'pending' end! def collect_payment puts "Collecting payment..." self.status = 'paid' endend!order = Order.new(100.00, 'pending')!5.times.map do Thread.new do if order.pending? order.collect_payment end endend.each(&:join)

Vamos usar um programinha que coleta pagamentos de pedidos.

Page 16: Celluloid - Atores, concorrência e relevância para o mundo Ruby

$ ruby race.rbCollecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...Collecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...Collecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...

Page 17: Celluloid - Atores, concorrência e relevância para o mundo Ruby

$ ruby race.rbCollecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...Collecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...Collecting payment...$ ruby race.rbCollecting payment...Collecting payment...Collecting payment...

WTF?

Page 18: Celluloid - Atores, concorrência e relevância para o mundo Ruby

RACE CONDITION

Page 19: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 20: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 21: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 22: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 23: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 24: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 25: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 26: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 27: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 28: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1 2 3 4 5

SO

Page 29: Celluloid - Atores, concorrência e relevância para o mundo Ruby

MUTEXES

Mutex - Mutual Exclusion, exclusão múltipla

Page 30: Celluloid - Atores, concorrência e relevância para o mundo Ruby

shared_data = []!10.times.map do Thread.new do 1000.times do shared_data << nil end endend.map(&:join)!puts shared_data.size

require "thread"!shared_data = []mutex = Mutex.new!10.times.map do Thread.new do 1000.times do mutex.synchronize { shared_data << nil } end endend.map(&:join)!puts shared_data.size

Sem Mutex Com Mutex

Page 31: Celluloid - Atores, concorrência e relevância para o mundo Ruby

$ ruby array_append_no_mutex.rb10000$ chruby jruby$ ruby array_append_no_mutex.rbConcurrencyError: Detected invalid array contents due to unsynchronized modifications with concurrent users << at org/jruby/RubyArray.java:1147 (root) at array_append_no_mutex.rb:6 times at org/jruby/RubyFixnum.java:275 (root) at array_append_no_mutex.rb:5

Sem Mutex

O MRI dá a resposta que a gente espera, por conta da trava global do interpretador. A JVM nem deixa isso rodar, esse despautério.

Page 32: Celluloid - Atores, concorrência e relevância para o mundo Ruby

$ ruby array_append_mutex.rb10000rubyonrio_mar14_celluloid/examples$ chruby jrubyrubyonrio_mar14_celluloid/examples$ ruby array_append_mutex.rb10000

Com Mutex

Com o Mutex no lugar, tudo certo.

Page 33: Celluloid - Atores, concorrência e relevância para o mundo Ruby

DEADLOCKS

Page 34: Celluloid - Atores, concorrência e relevância para o mundo Ruby

2

1 A

B

Threads Locks (Mutexes)

Page 35: Celluloid - Atores, concorrência e relevância para o mundo Ruby

2

1 A

B

Threads Locks (Mutexes)

Page 36: Celluloid - Atores, concorrência e relevância para o mundo Ruby

2

1 A

B

Threads Locks (Mutexes)

Page 37: Celluloid - Atores, concorrência e relevância para o mundo Ruby

LIVELOCKS

Page 38: Celluloid - Atores, concorrência e relevância para o mundo Ruby

A gente não vai ficar muito mais inteligente. O jeito é pensar em outras maneiras de resolver o problema.

Page 39: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Software Transactional Memory (Clojure)

Communicating Sequential Processes (go, core.async do Clojure)

Actors (Erlang, Akka, Celluloid)

Três grandes ideias (não as únicas) e linguagens/bibliotecas em que elas estão proeminentemente implementadas.

Page 40: Celluloid - Atores, concorrência e relevância para o mundo Ruby

ACTORS

Mas primeiro, uma breve explicação do Actor Model.

Page 41: Celluloid - Atores, concorrência e relevância para o mundo Ruby

1973

Em 1973, Carl Hewitt et al bolaram uma visão computacional para um mundo em que computadores teriam dezenas, centenas, milhares de unidades de processamento independentes. Há 41 anos eles sabiam claramente daquilo que nós aqui no Ruby tentamos ignorar.

Page 42: Celluloid - Atores, concorrência e relevância para o mundo Ruby

ERLANG

Graças, em grande parte, ao Erlang, o modelo ficou bastante conhecido e as ideias geradas há quarenta anos passaram a povoar as mentes contemporâneas.

Page 43: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Mr. Red Actor 1973 Memory Ln. Boston, MA

Um ator é uma entidade computacional que se comunica com outras por meio de mensagens. Cada ator é completamente independente do outro.

Page 44: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Mr. Red Actor 1973 Memory Ln. Boston, MA

Eles só podem se comunicar usando o endereço da caixa de mensagens do ator. Não dá pra puxar o outro ator pelo braço e mandar ele fazer alguma coisa.

Page 45: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Mr. Red Actor 1973 Memory Ln. Boston, MA

Da mesma maneira, o recipiente não sabe nada sobre quem mandou a mensagem: só interessa a mensagem em si. Esse desacoplamento entre as partes é uma ideia extremamente poderosa.

Page 46: Celluloid - Atores, concorrência e relevância para o mundo Ruby

No modelo, as mensagens são enviadas de maneira assíncrona, em um esquema fire-and-forget. Não há garantia da ordem em que serão processadas.

Page 47: Celluloid - Atores, concorrência e relevância para o mundo Ruby

Celluloid

Page 48: Celluloid - Atores, concorrência e relevância para o mundo Ruby

‒Tony Arcieri, criador do Celluloid

“… at the very least Celluloid puts threads on Rails. !

It makes it a lot easier to get right prototypes and get them done quickly and not spend a lot of time debugging all this stuff in the standard library.”

Em uma entrevista com o rubista Jesse Storimer, o criador do Celluloid, Tony Arcieri, disse que gosta de pensar que o Celluloid é uma espécie de Threads on Rails.

Page 49: Celluloid - Atores, concorrência e relevância para o mundo Ruby

ATORES QUE SÃO OBJETOS, OBJETOS QUE SÃO ATORES

O Celluloid dá uma cara Ruby ao modelo de atores. Isso quer dizer que seu programa não vai ficar subitamente com outra cara, seguindo outros paradigmas, como quando se usa o EventMachine.

Page 50: Celluloid - Atores, concorrência e relevância para o mundo Ruby

class ChocolateCake include Celluloid!

def lose_slice sleep 2 puts "Lost slice" endend

Esta é a cara de um ator no Celluloid. A única coisa que precisa ser colocada é esse “include Celluloid”. A partir desse momento seu objeto é concorrente e está em seu próprio Thread.

Page 51: Celluloid - Atores, concorrência e relevância para o mundo Ruby

BEIJINHO NO OMBRO PROS DEADLOCKS

Celluloid uses a concurrent object model which combines method dispatch and thread synchronization. Each actor is a concurrent object running in its own thread, and every method invocation is wrapped in a fiber that can be suspended whenever it calls out to other actors, and resumed when the response is available. This means methods which are waiting for responses from other actors, external messages, or other system events (including I/O with Celluloid::IO) .

Page 52: Celluloid - Atores, concorrência e relevância para o mundo Ruby

TOLERÂNCIA A FALHAS

Inspirado pelo Erlang, o Celluloid segue a filosofia “let it crash”, permitindo que você estabeleça relações entre atores que determinem como o sistema deve se portar em caso de falhas.

Page 53: Celluloid - Atores, concorrência e relevância para o mundo Ruby

TOLERÂNCIA A FALHAS

Links

O primeiro método é o link. Um ator se liga a outro, e seus destinos estão selados. (demo no pry)

Page 54: Celluloid - Atores, concorrência e relevância para o mundo Ruby

TOLERÂNCIA A FALHAS

Links Supervisores

Inspirado pelo Erlang, o Celluloid segue a filosofia “let it crash”, permitindo que você estabeleça relações entre atores que determinem como o sistema deve se portar em caso de falhas.

Page 55: Celluloid - Atores, concorrência e relevância para o mundo Ruby

COMPUTAÇÃO DISTRIBUÍDA

(via DCell)

A partir do momento que atores não estão ligados diretamente e só têm os endereços uns dos outros, um ator pode estar na mesma máquina ou não. O modelo de programação não muda.

Page 56: Celluloid - Atores, concorrência e relevância para o mundo Ruby

IO ASSÍNCRONO COM ATORES

(via Celluloid::IO)

TOMA ESSA, NODE!

Page 57: Celluloid - Atores, concorrência e relevância para o mundo Ruby

"MAS SÓ ME INTERESSA A

WEB!"

Page 58: Celluloid - Atores, concorrência e relevância para o mundo Ruby

A MORTE DO REQUEST—RESPONSE

Page 59: Celluloid - Atores, concorrência e relevância para o mundo Ruby

BALLADINA

Page 60: Celluloid - Atores, concorrência e relevância para o mundo Ruby

FIM

Page 61: Celluloid - Atores, concorrência e relevância para o mundo Ruby

• http://github.com/celluloid/celluloid

• http://github.com/dodecaphonic/balladina

Page 62: Celluloid - Atores, concorrência e relevância para o mundo Ruby

• http://www.flickr.com/photos/

zigazou76/3702501888/in/photostream/ (Perigo)