Elixir og Phoenix: Skalerbar backendutvikling · leksjon

Dynamiske supervisorer og Registry

Lær å starte og stoppe prosesser dynamisk med `DynamicSupervisor`, og bruk `Registry` til oppslag av prosesser.

Leksjon 3 av 411 trinn

Dynamiske supervisorer og Registry er en gratis leksjon i Elixir og Phoenix: Skalerbar backendutvikling på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Elixir og Phoenix: Skalerbar backendutvikling, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Elixir og Phoenix: Skalerbar backendutvikling inneholder totalt 4 leksjoner.

Dynamisk prosesshåndtering

I tidligere leksjoner lærte De om supervisorer for håndtering av et fast sett med prosesser. Men hva om applikasjonen trenger å opprette eller avslutte prosesser ved behov, for eksempel for hver brukersesjon eller en bestemt oppgave?

Det er her dynamisk prosesshåndtering kommer til nytte. Det gjør at applikasjonen kan tilpasse seg og skalere ved å opprette prosesser bare når de trengs.

Introduksjon til DynamicSupervisor

Elixirs DynamicSupervisor er laget nettopp for dette formålet. I motsetning til Supervisor, som håndterer en forhåndsdefinert liste over underordnede prosesser, starter en DynamicSupervisor uten underordnede prosesser.

Den tilbyr funksjoner for å legge til og fjerne underordnede prosesser dynamisk mens programmet kjører, slik at De får fleksibel kontroll over applikasjonens prosesstre.

Starte en DynamicSupervisor

For å bruke en DynamicSupervisor må De først starte den, vanligvis som en del av applikasjonens supervisjonstre. Her er et grunnleggende oppsett:

defmodule MyApp.DynamicManager do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end

# To run this in an IEx session:
# {:ok, pid} = MyApp.DynamicManager.start_link([])
# IO.puts("DynamicSupervisor started with PID: #{inspect(pid)}")

Legge til underordnede prosesser dynamisk

Når DynamicSupervisor kjører, kan De legge til nye underordnede prosesser ved hjelp av DynamicSupervisor.start_child/2. Hver underordnet prosess trenger en spesifikasjon for underordnet prosess som definerer hvordan den skal startes.

La oss opprette en enkel GenServer som supervisoren kan håndtere.

defmodule MyApp.Worker do
  use GenServer

  def start_link(id) do
    GenServer.start_link(__MODULE__, id, name: via_tuple(id))
  end

  def init(id) do
    IO.puts("Worker #{id} started!")
    {:ok, id}
  end

  defp via_tuple(id), do: {:via, Registry, {MyApp.Registry, id}}
end

defmodule MyApp.DynamicManager do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end

  def start_worker(id) do
    child_spec = %{
      id: id,
      start: {MyApp.Worker, :start_link, [id]}
    }
    DynamicSupervisor.start_child(__MODULE__, child_spec)
  end
end

# To run this in an IEx session:
# {:ok, manager_pid} = MyApp.DynamicManager.start_link([])
# {:ok, worker_pid} = MyApp.DynamicManager.start_worker(:user_123)
# IO.puts("Worker PID: #{inspect(worker_pid)}")

Stoppe dynamiske underordnede prosesser

Akkurat som De kan starte underordnede prosesser dynamisk, kan De også stoppe dem. Bruk DynamicSupervisor.terminate_child/2 sammen med PID-en til den underordnede prosessen.

Supervisoren håndterer den kontrollerte avslutningen av prosessen.

defmodule MyApp.Worker do
  use GenServer

  def start_link(id) do
    GenServer.start_link(__MODULE__, id, name: {:global, id})
  end

  def init(id) do
    IO.puts("Worker #{id} started!")
    {:ok, id}
  end

  def terminate(_reason, id) do
    IO.puts("Worker #{id} stopping!")
  end
end

defmodule MyApp.DynamicManager do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end

  def start_worker(id) do
    child_spec = %{id: id, start: {MyApp.Worker, :start_link, [id]}}
    DynamicSupervisor.start_child(__MODULE__, child_spec)
  end

  def stop_worker(worker_pid) do
    DynamicSupervisor.terminate_child(__MODULE__, worker_pid)
  end
end

# To run this in an IEx session:
# {:ok, manager_pid} = MyApp.DynamicManager.start_link([])
# {:ok, worker_pid} = MyApp.DynamicManager.start_worker(:task_A)
# :timer.sleep(100)
# MyApp.DynamicManager.stop_worker(worker_pid)
# :timer.sleep(100)
# IO.puts("Worker process should now be stopped.")

Behovet for et register

Når De starter prosesser dynamisk, hvordan finner De dem igjen senere? Hvis De bare har PID-ene deres, hva skjer hvis en prosess krasjer og starter på nytt med en ny PID?

Vi trenger en måte å tilordne en varig og meningsfull navn til en prosess på, slik at den kan slås opp på en pålitelig måte uavhengig av hvilken PID den har. Det er her Elixirs Registry kommer inn.

Hva er Registry?

Registry-modulen tilbyr et lokalt, distribuert og feiltolerant nøkkel-verdi-lager for å knytte vilkårlige termer (nøkler) til prosessidentifikatorer (PID-er).

Den egner seg perfekt til å slå opp prosesser ved hjelp av et navn eller en ID, særlig ved arbeid med dynamiske prosesser eller når De vil bruke et egendefinert navn i stedet for globale eller lokale atomer.

Starte og bruke Registry

I likhet med andre OTP-atferder må Registry startes, vanligvis som en del av applikasjonens supervisjonstre. De kan definere flere registre, hvert med sitt eget virkeområde.

Når registeret er startet, kan De registrere prosesser med nøkler og slå dem opp.

defmodule MyApp.Registry do
  use Registry, keys: :unique, name: __MODULE__
end

defmodule MyApp.WorkerWithRegistry do
  use GenServer

  def start_link(id) do
    GenServer.start_link(__MODULE__, id, name: {:via, Registry, {MyApp.Registry, id}})
  end

  def init(id) do
    IO.puts("Worker #{id} started and registered!")
    {:ok, id}
  end

  def get_state(pid) do
    GenServer.call(pid, :get_state)
  end

  @impl true
  def handle_call(:get_state, _from, state) do
    {:reply, state, state}
  end
end

# To run this in an IEx session:
# {:ok, _} = MyApp.Registry.start_link([]) # Start the registry
# {:ok, worker_pid} = MyApp.WorkerWithRegistry.start_link(:session_1)
# IO.puts("Registered worker PID: #{inspect(worker_pid)}")
#
# {:ok, [pid_found]} = Registry.lookup(MyApp.Registry, :session_1)
# IO.puts("Looked up PID: #{inspect(pid_found)}")
# IO.puts("Worker state: #{MyApp.WorkerWithRegistry.get_state(pid_found)}")

DynamicSupervisor med Registry

Å kombinere DynamicSupervisor med Registry er et kraftig mønster. De kan starte arbeidsprosesser dynamisk, og hver arbeidsprosess registrerer seg selv med en unik nøkkel i Registry.

Dette gjør at andre deler av applikasjonen kan finne og samhandle med bestemte arbeidsprosesser uten å måtte kjenne PID-ene deres direkte.

defmodule MyApp.UserSession do
  use GenServer

  def start_link(user_id) do
    # Register with Registry using the user_id as key
    GenServer.start_link(__MODULE__, user_id, name: {:via, Registry, {MyApp.SessionsRegistry, user_id}})
  end

  def init(user_id) do
    IO.puts("Session for user #{user_id} started.")
    {:ok, user_id}
  end

  def get_user_id(pid) do
    GenServer.call(pid, :get_user_id)
  end

  @impl true
  def handle_call(:get_user_id, _from, user_id) do
    {:reply, user_id, user_id}
  end
end

defmodule MyApp.SessionsRegistry do
  use Registry, keys: :unique, name: __MODULE__
end

defmodule MyApp.SessionSupervisor do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end

  def start_session(user_id) do
    child_spec = %{
      id: user_id,
      start: {MyApp.UserSession, :start_link, [user_id]}
    }
    DynamicSupervisor.start_child(__MODULE__, child_spec)
  end
end

# --- Application Entry Point --- (Simulated)
{:ok, _registry_pid} = MyApp.SessionsRegistry.start_link([])
{:ok, _supervisor_pid} = MyApp.SessionSupervisor.start_link([])

# Start a session for user_101
{:ok, session_pid_1} = MyApp.SessionSupervisor.start_session(:user_101)
IO.puts("Session 1 PID: #{inspect(session_pid_1)}")

# Start a session for user_102
{:ok, session_pid_2} = MyApp.SessionSupervisor.start_session(:user_102)
IO.puts("Session 2 PID: #{inspect(session_pid_2)}")

# Look up user_101's session via Registry
{:ok, [pid_from_registry]} = Registry.lookup(MyApp.SessionsRegistry, :user_101)
IO.puts("Found user_101 session PID via Registry: #{inspect(pid_from_registry)}")
IO.puts("User ID from session: #{MyApp.UserSession.get_user_id(pid_from_registry)}")

Quiz om dynamiske prosesser og Registry

Tenk på en applikasjon som bruker DynamicSupervisor til å håndtere brukersesjoner og Registry til å slå opp sesjoner med bruker-ID. Hva er den viktigste fordelen ved å bruke Registry i dette scenarioet?

Oppsummering: Dynamiske prosesser og Registry

De har nå fått full kontroll på dynamisk prosesshåndtering!

  • DynamicSupervisor gjør det mulig å starte og stoppe underordnede prosesser ved behov, slik at applikasjonen blir svært fleksibel og tilpasningsdyktig.
  • Registry-modulen tilbyr et kraftig nøkkel-verdi-lager for å knytte prosesser til meningsfulle navn eller ID-er, slik at dynamisk startede prosesser kan slås opp på en pålitelig måte.

Ved å kombinere disse to kan De bygge robuste og skalerbare applikasjoner som håndterer ressursene sine effektivt.

Gratis å komme i gang

Lær deg Elixir med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
12
Leksjoner
48

Ofte stilte spørsmål

Er leksjonen «Dynamiske supervisorer og Registry» gratis?

Ja – hele teksten i «Dynamiske supervisorer og Registry» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Elixir og Phoenix: Skalerbar backendutvikling-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Elixir og Phoenix: Skalerbar backendutvikling inneholder totalt 4 leksjoner.

Hva lærer jeg i «Dynamiske supervisorer og Registry»?

Lær å starte og stoppe prosesser dynamisk med `DynamicSupervisor`, og bruk `Registry` til oppslag av prosesser. Du øver på Elixir og Phoenix: Skalerbar backendutvikling med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Elixir og Phoenix: Skalerbar backendutvikling?

Ingen tidligere erfaring er nødvendig. Elixir og Phoenix: Skalerbar backendutvikling på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Dynamiske supervisorer og Registry»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Elixir og Phoenix: Skalerbar backendutvikling-leksjonen?

Ja. Alle Elixir og Phoenix: Skalerbar backendutvikling-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Distribuert Elixir og klyngedannelse
  2. Avanserte supervisorstrategier
  3. Dynamiske supervisorer og Registry
  4. GenStage- og backpressure-pipelines
← Tilbake til Elixir og Phoenix: Skalerbar backendutvikling