Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Processes. OTP. Elixir

Sobolev Nikita
December 02, 2017
70

Processes. OTP. Elixir

Sobolev Nikita

December 02, 2017
Tweet

Transcript

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

    View Slide

  2. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. Actor Model

    View Slide

  7. OTP

    View Slide

  8. Supervisor

    View Slide

  9. 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

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. :permanent
    :temporary
    :transient

    View Slide

  14. spawn

    View Slide

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

    View Slide

  16. 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

    View Slide

  17. 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?

    View Slide

  18. spawn_link

    View Slide

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

    View Slide

  20. GenServer

    View Slide

  21. 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

    View Slide

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

    View Slide

  23. View Slide

  24. View Slide

  25. Agent

    View Slide

  26. 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

    View Slide

  27. View Slide

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

    View Slide

  29. View Slide

  30. ets

    View Slide

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

    View Slide

  32. View Slide

  33. View Slide

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

    View Slide

  35. Task

    View Slide

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

    View Slide

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

    View Slide

  38. GenStage

    View Slide

  39. [A] -> [B] -> [C]

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. View Slide

  44. View Slide

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

    View Slide

  46. Flow

    View Slide

  47. 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(%{})

    View Slide

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

    View Slide

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

    View Slide

  50. http://elixir-lang.moscow

    View Slide

  51. http://wunsh.ru

    View Slide

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

    View Slide