Slide 1

Slide 1 text

Никита Соболев github.com/sobolevn

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Процессы. ОТP. Elixir

Slide 4

Slide 4 text

План доклада • Концепция • OTP • Elixir specific штуки

Slide 5

Slide 5 text

Для кого мой доклад? • Вы слышали про Erlang/Elixir • Вы читали туториал • Вы знакомы с другими функциональными языками

Slide 6

Slide 6 text

Actor Model

Slide 7

Slide 7 text

OTP

Slide 8

Slide 8 text

Supervisor

Slide 9

Slide 9 text

defmodule FpconfElixir do use Application def start(_type, _args) do import Supervisor.Spec, warn: false children = [ worker(FpconfElixir.AgentType, []), worker(FpconfElixir.GenStageType.Producer, [0]), worker(FpconfElixir.GenStageType.ProducerConsumer, []), worker(FpconfElixir.GenStageType.Consumer, []) ] opts = [strategy: :one_for_one, name: FpconfElixir.Supervisor] Supervisor.start_link(children, opts) end end

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

:permanent :temporary :transient

Slide 14

Slide 14 text

spawn

Slide 15

Slide 15 text

spawn fn -> IO.puts("I am a process!") end

Slide 16

Slide 16 text

defmodule FpconfElixir.SpawnType do def start_link do accepting_messages(0) end def accepting_messages(state) do receive do {:hello, message} -> IO.puts "Hello, #{message}" # Will accept only once without this: accepting_messages(state) {:counter} -> new_state = state + 1 IO.puts "New state is #{new_state}" accepting_messages(new_state) _ -> IO.puts "What?" accepting_messages(state) end end end

Slide 17

Slide 17 text

pid = spawn fn -> FpconfElixir.SpawnType.start_link end send pid, {:hello, "world"} # => Hello, world send pid, {:counter} # => New state is 1 send pid, {:counter} # => New state is 2 send pid, {:other} # => What?

Slide 18

Slide 18 text

spawn_link

Slide 19

Slide 19 text

spawn_link fn -> raise "Oops" end spawn fn -> raise "Oops" end

Slide 20

Slide 20 text

GenServer

Slide 21

Slide 21 text

defmodule FpconfElixir.TodoList do use GenServer def start_link do GenServer.start_link(__MODULE__, [], name: __MODULE__) end def add_task(task) when is_binary(task) do GenServer.cast(__MODULE__, {:add, task}) end def list_tasks, do: GenServer.call(__MODULE__, :list) # Private def handle_cast({:add, task}, tasks), do: {:noreply, [task | tasks]} def handle_call(:list, _from, tasks), do: {:reply, tasks, tasks} end

Slide 22

Slide 22 text

FpconfElixir.TodoList.add_task("One") # => :ok FpconfElixir.TodoList.add_task("Two") # => :ok FpconfElixir.TodoList.list_tasks # => ["One", "Two"]

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Agent

Slide 26

Slide 26 text

defmodule FpconfElixir.AgentType do use Agent @doc """ Starts a new Agent process with an empty Map. """ def start_link do Agent.start_link(fn -> %{} end, name: __MODULE__) # this process is a singleton end def get(key) do Agent.get(__MODULE__, &Map.get(&1, key)) end def put(key, value) do Agent.update(__MODULE__, fn (state) -> Map.put(state, key, value) # happens on server end) end end

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

alias FpconfElixir.AgentType AgentType.put("name", "Nikita") # =>:ok AgentType.get("Name") # => "Nikita"

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

ets

Slide 31

Slide 31 text

table = :ets.new(:personal_table, [ :set, :protected, :named_table ]) :ets.insert(table, {"surname", "Sobolev"}) :ets.lookup(table, "surname")

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

ets => 0.40 µs agent => 6 µs redis => 210 µs

Slide 35

Slide 35 text

Task

Slide 36

Slide 36 text

task = Task.async(fn -> do_some_work() end) res = do_some_other_work() res + Task.await(task)

Slide 37

Slide 37 text

Task.Supervisor.async({ FpconfElixir.TaskSupervisor, :remote@local }, HttpParser, :parse, ["head", "body"] )

Slide 38

Slide 38 text

GenStage

Slide 39

Slide 39 text

[A] -> [B] -> [C]

Slide 40

Slide 40 text

defmodule FpconfElixir.GenStageType.Producer do use GenStage def start_link(initial \\ 0) do GenStage.start_link(__MODULE__, initial, name: __MODULE__) end def init(counter), do: {:producer, counter} def handle_demand(demand, state) do events = Enum.to_list(state..(state + demand - 1)) {:noreply, events, state + demand} end end

Slide 41

Slide 41 text

defmodule FpconfElixir.GenStageType.ProducerConsumer do use GenStage def start_link do GenStage.start_link(__MODULE__, :state_doesnt_matter, name: __MODULE__) end def init(state) do {:producer_consumer, state, subscribe_to: [ FpconfElixir.GenStageType.Producer]} end def handle_events(events, _from, state) do numbers = events |> Enum.filter(&(rem(&1, 2) == 0)) {:noreply, numbers, state} end end

Slide 42

Slide 42 text

defmodule FpconfElixir.GenStageType.Consumer do use GenStage def start_link do GenStage.start_link(__MODULE__, :state_doesnt_matter) end def init(state) do {:consumer, state, subscribe_to: [ FpconfElixir.GenStageType.ProducerConsumer]} end def handle_events(events, _from, state) do for event <- events do IO.inspect({self(), event, state}) end # As a consumer we never emit events {:noreply, [], state} end end

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

Real world example A. Берем список репозиториев пользователя с Github B. Для каждого репозитория получим всех, кто ставил звездочку C. Сохраним их себе в базу для опытов

Slide 46

Slide 46 text

Flow

Slide 47

Slide 47 text

File.stream!("source.txt", [], :line) |> Flow.from_enumerable() |> Flow.flat_map(&String.split/1) |> Flow.partition() # the magic happens here |> Flow.reduce(fn -> %{} end, fn(word, map) -> Map.update(map, word, 1, &(&1 + 1)) end) |> Enum.into(%{})

Slide 48

Slide 48 text

Стоит упомянуть • OTP application • gen_fsm, GenEvent • gen_metrics • mnesia

Slide 49

Slide 49 text

Полезные материалы • https://github.com/sasa1977/exactor • https://github.com/onetapbeyond/ gen_metrics • https://github.com/saleyn/erlexec • https://github.com/PragTob/benchee

Slide 50

Slide 50 text

http://elixir-lang.moscow

Slide 51

Slide 51 text

http://wunsh.ru

Slide 52

Slide 52 text

Вопросы? github.com/sobolevn