Demo 3: Sistemas distribuídos com Elixir
Mix.install(
[
{:kino, "~> 0.14.1"},
{:kino_flame, github: "hugobarauna/kino_flame"}
]
)
Processo
Processo é a unida mínima de concorrência
self()
pid = self()
IO.puts("Esse código está sendo executado dentro do processo: #{inspect(pid)}")
Criando novos processos com spawn
current_process_pid = self()
child_process_pid = spawn(fn ->
"pong"
end)
IO.inspect(current_process_pid, label: "PID do processo corrente")
IO.inspect(child_process_pid, label: "PID do processo filho")
# :ok
Processos se comunicam por passagem de mensagem
Envia mensagem message
para processo com identificador pid
send(pid, message)
Recebe mensagem message
de um outro processo e faz alguma coisa
receive do
message -> # do something
end
Kino.Process.render_seq_trace(fn ->
# inicia um novo processo
child_process_pid =
spawn(fn ->
receive do
{:ping, caller_pid} -> send(caller_pid, :pong)
end
end)
# pega o pid do processo rodando
parent_process_pid = self()
# envia uma mensagem para o processo child_process_pid
send(child_process_pid, {:ping, parent_process_pid})
receive do
:pong -> :it_worked!
end
end)
Location transparency
Processos podem se comunicar de modo transparente em relação a que nó (instância máquina virtual / máquina) eles estão rodando.
Mesma API para se comunicar com um processo rodando na máquina local ou em qualquer máquina do cluster:
send(pid, :message_name)
Vamos iniciar um uma instância da máquina virtual do Erlang em outra máquina
Kino.start_child(
{FLAME.Pool,
name: :runner,
code_sync: [
start_apps: true,
sync_beams: Kino.beam_paths(),
compress: false,
copy_paths: [],
verbose: true
],
min: 1,
max: 1,
max_concurrency: 10,
boot_timeout: :timer.minutes(3),
idle_shutdown_after: :timer.minutes(1),
timeout: :infinity,
track_resources: true,
log: :info,
backend:
{FLAME.FlyBackend,
cpu_kind: "shared",
cpus: 1,
memory_mb: 2048,
env: %{"LIVEBOOK_COOKIE" => Node.get_cookie()}}}
)
remote_node =
Node.list(:hidden)
|> Enum.filter(&String.contains?(Atom.to_string(&1), "flame"))
|> List.first()
remote_process_pid =
Node.spawn(remote_node, fn ->
receive do
:node_and_pid_info ->
IO.puts("Remote process #{inspect(self())} \n\trunning on node #{node()}\n")
end
end)
Kino.nothing()
local_process_pid =
spawn(fn ->
receive do
:node_and_pid_info ->
IO.puts("Local process #{inspect(self())} \n\trunning on node #{node()}\n")
end
end)
Mesma API para se comunicar com um processo rodando na máquina local ou em qualquer máquina do cluster:
send(pid, :message_name)
send(remote_process_pid, :node_and_pid_info)
send(local_process_pid, :node_and_pid_info)
Kino.nothing()
IO.puts("Local process #{inspect(self())}\n\trunning on node #{node()}")
Kino.nothing()
FLAME: elastic scale by wrapping a code inside a function
defmodule Echo do
def hello(message) do
IO.puts("ECHO FROM\n\tNODE: #{node()}\n\tMESSAGE: #{message}")
end
end
message = "Sorry, Dave"
Echo.hello(message)
Kino.start_child(
{FLAME.Pool,
name: :elastic_pool,
code_sync: [
start_apps: true,
sync_beams: Kino.beam_paths(),
compress: false,
copy_paths: [],
verbose: true
],
min: 0,
max: 4,
max_concurrency: 1,
boot_timeout: :timer.minutes(3),
idle_shutdown_after: :timer.minutes(1),
timeout: :infinity,
track_resources: true,
log: :info,
backend:
{FLAME.FlyBackend,
cpu_kind: "shared",
cpus: 2,
memory_mb: 1024,
env: %{"LIVEBOOK_COOKIE" => Node.get_cookie()}}}
)
FLAME.call(:elastic_pool, fn ->
message = "Sorry, Dave"
Echo.hello(message)
end)
FLAME.call(
:elastic_pool,
fn ->
message = "Sorry, Dave"
Echo.hello(message)
end
)