Dynamiske supervisorer og Registry
Lær å starte og stoppe prosesser dynamisk med `DynamicSupervisor`, og bruk `Registry` til oppslag av prosesser.
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!
DynamicSupervisorgjø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.
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
- Distribuert Elixir og klyngedannelse
- Avanserte supervisorstrategier
- Dynamiske supervisorer og Registry
- GenStage- og backpressure-pipelines