Elixir 태스크 모듈의 힘 - Into Task.Supervisor

태스크 모듈과 더 일반적인 방식으로 작동하는 방법에 대한 소개였습니다.

이제 Task.Supervisor를 사용하여 상황을 조금 복잡하게 만들고 몇 가지 비약 개념을 볼 시간입니다.

계속 진행하기 전에 몇 가지 비약의 기본 개념을 살펴보겠습니다.

Supervisor

A supervisor is a process that supervises other processes, which we refer to as child processes. Supervisors are used to building a hierarchical process structure called a supervision tree. Supervision trees provide fault-tolerance and encapsulate how our applications start and shutdown.



Processes

In Elixir, all code runs inside processes. Processes are isolated from each other, run concurrent to one another, and communicate via message passing.

Elixir’s processes should not be confused with operating system processes. Processes in Elixir are extremely lightweight in terms of memory and CPU (even compared to threads as used in many other programming languages). Because of this, it is not uncommon to have tens or even hundreds of thousands of processes running simultaneously.


Task.Supervisor를 사용하여 관련 자식을 관리하는 프로세스를 생성합니다.

놀이터 만들기



첫 번째 단계는 감독 트리를 사용하여 새로운 엘릭서 애플리케이션을 만드는 것입니다. 혼합 가능성에 대한 자세한 정보는 in the official documentation 에서 찾을 수 있습니다.

mix new newsample --sup


생성된 것을 볼 시간:

  lib
  lib/newsample
  lib/newsample.ex
  lib/newsample/application.ex
test
  test/newsample_test.exs
  test/test_helper.exs
README.md
mix.exs


지금은 lib/newsample.exlib/newsample/application.ex 로 작업하겠습니다.

두 번째 단계는 테스트를 쉽게 실행할 수 있도록 약간의 변경으로 이전 게시물lib/newsample.ex 내부에서 동일한 기능을 만드는 것입니다.

폭발적인 시나리오를 강제하기 위해 "알파"값과 일치하는 패턴이 있습니다.

  def say_hello("alpha" = to), do: raise("Error to say hello to #{to}")

  def say_hello(to) do
    IO.puts("Hello #{to}")
    :ok
  end

  def process() do
    items = ["alpha", "beta", "gama"]

    Enum.map(items, fn item ->
      Task.async(fn ->
        say_hello(item)
      end)
    end)
    |> Enum.map(&Task.await/1)
  end


마지막으로 elixir's interactive shell 을 사용하여 실행합니다.

iex -S mix


그런 다음 코드를 실행합니다.

iex(1)> self
#PID<0.145.0>
iex(2)> Newsample.process
Hello beta
Hello gama

22:01:54.276 [error] Task #PID<0.148.0> started from #PID<0.145.0> terminating
** (RuntimeError) Error to say hello to alpha
    (newsample 0.1.0) lib/newsample.ex:15: anonymous fn/2 in Newsample.process/1
    (elixir 1.11.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
    (elixir 1.11.3) lib/task/supervised.ex:35: Task.Supervised.reply/5
    (stdlib 3.14) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: #Function<1.13017213/0 in Newsample.process/1>
    Args: []
** (EXIT from #PID<0.145.0>) shell process exited with reason: an exception was raised:
    ** (RuntimeError) Error to say hello to alpha
        (newsample 0.1.0) lib/newsample.ex:15: anonymous fn/2 in Newsample.process/1
        (elixir 1.11.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
        (elixir 1.11.3) lib/task/supervised.ex:35: Task.Supervised.reply/5
        (stdlib 3.14) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

Interactive Elixir (1.11.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> self
#PID<0.151.0>


우리가 볼 수 있듯이 발생한 예외는 iex 세션까지 전파되었습니다. 코드를 실행하기 전PID과 실행 후#PID<0.145.0>를 비교하여#PID<0.151.0> 확인할 수 있습니다.

이 폭발적인 오류 전파를 어떻게 피할 수 있습니까?



이제 감독된 작업을 사용하는 단계에 있습니다. 열어봅시다lib/newsample/application.ex.

  def start(_type, _args) do
    children = []

    opts = [strategy: :one_for_one, name: Newsample.Supervisor]
    Supervisor.start_link(children, opts)
  end


우리의 Task.Supervisor를 자식 프로세스로 만들어 봅시다.

  children = [
    {Task.Supervisor, name: Newsample.TaskSupervisor}
  ]


이제 process 메서드에서 이 지도 작업을 사용하고 async_no_link 로 시작합니다. 이것은 이 자식 프로세스와 부모 사이에 링크가 없음을 의미합니다.

  def process() do
    items = ["alpha", "beta", "gama"]

    Enum.map(items, fn item ->
      Task.Supervisor.async_nolink(Newsample.TaskSupervisor, fn ->
        say_hello(item)
      end)
    end)
    |> Enum.map(&Task.await/1)
  end


다시 실행하면 이 오류 전파 없이 모든 것이 올바르게 작동합니다.

iex(1)> self
#PID<0.158.0>
iex(2)> Newsample.process
Hello beta
Hello gama

21:04:48.200 [error] Task #PID<0.161.0> started from #PID<0.158.0> terminating
** (RuntimeError) Error to say hello to alpha
    (newsample 0.1.0) lib/newsample.ex:6: Newsample.say_hello/1
    (elixir 1.11.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
    (elixir 1.11.3) lib/task/supervised.ex:35: Task.Supervised.reply/5
    (stdlib 3.14) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: #Function<1.39266525/0 in Newsample.process/0>
    Args: []
** (exit) exited in: Task.await(%Task{owner: #PID<0.158.0>, pid: #PID<0.161.0>, ref: #Reference<0.1303494724.713818114.156158>}, 5000)
    ** (EXIT) an exception was raised:
        ** (RuntimeError) Error to say hello to alpha
            (newsample 0.1.0) lib/newsample.ex:6: Newsample.say_hello/1
            (elixir 1.11.3) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
            (elixir 1.11.3) lib/task/supervised.ex:35: Task.Supervised.reply/5
            (stdlib 3.14) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
    (elixir 1.11.3) lib/task.ex:639: Task.await/2
    (elixir 1.11.3) lib/enum.ex:1411: Enum."-map/2-lists^map/1-0-"/2
iex(2)> self
#PID<0.158.0>


우리가 볼 수 있듯이 PID는 실행 전후에 동일합니다.

프로세스와 감독자 트리는 탄력성과 내결함성 시스템을 구축하기 위한 엘릭서의 중요한 구성 요소입니다.

좋은 웹페이지 즐겨찾기