Elixir og Phoenix: Skalerbar backendutvikling · leksjon

Elixir-prosesser og meldingsutveksling

Lær om lette Elixir-prosesser, hvordan de kommuniserer og prinsippene for «share nothing»-samtidighet.

Leksjon 1 av 411 trinn

Elixir-prosesser og meldingsutveksling er en gratis leksjon i Elixir og Phoenix: Skalerbar backendutvikling på CoddyKit. Dette er leksjon 1 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.

Bli kjent med Elixir-prosesser

Elixir er laget for samtidighet, og prosesser er de grunnleggende byggesteinene. Tenk på dem som små, isolerte programmer som kjører samtidig.

I motsetning til prosesser eller tråder i operativsystemet er Elixir-prosesser svært lette. Du kan ha hundretusener, ja til og med millioner, kjørende på én enkelt maskin!

  • Isolasjon: Hver prosess har sitt eget minne.
  • Kommunikasjon: De kommuniserer med hverandre ved å sende meldinger.
  • Feiltoleranse: Hvis én krasjer, tar den ikke ned de andre.

Opprette nye prosesser

Vi oppretter en ny Elixir-prosess ved hjelp av funksjonen spawn. Den tar en funksjon (ofte en anonym funksjon) som den nye prosessen skal kjøre.

spawn returnerer en prosessidentifikator (PID), som fungerer som en adresse til den nye prosessen.

defmodule MyModule do
  def greet do
    IO.puts "Hello from a new process!"
  end

  def run do
    # Spawn a new process that calls MyModule.greet()
    pid = spawn(MyModule, :greet, [])
    IO.puts "Spawned process with PID: #{inspect(pid)}"
  end
end

MyModule.run()

Forstå prosess-ID-er (PID-er)

En prosessidentifikator, eller PID, er en unik referanse til en Elixir-prosess. Det er slik du adresserer en bestemt prosess for å sende den meldinger.

Tenk på en PID som et telefonnummer til en person. Du ringer nummeret for å snakke med personen, og prosesser bruker PID-er til å kommunisere.

  • Hver prosess har en unik PID.
  • PID-er er avgjørende for meldingsutveksling.
  • Du kan inspisere en PID for å se den interne representasjonen.

Finne din egen PID

Akkurat som du kan hente PID-en til en nyopprettet prosess, kan enhver prosess som kjører finne sin egen PID ved hjelp av funksjonen self().

Dette er avgjørende når en prosess må fortelle andre hvordan de skal sende meldinger tilbake til den.

defmodule PidDemo do
  def show_self_pid do
    IO.puts "My PID is: #{inspect(self())}"
  end

  def run do
    IO.puts "Main process PID: #{inspect(self())}"
    # Spawn a process to show its own PID
    spawn(PidDemo, :show_self_pid, [])
    :timer.sleep(100) # Give the spawned process time to run
  end
end

PidDemo.run()

Kommunisere med meldinger

Elixir-prosesser kommuniserer ved å sende og motta meldinger. Dette er kjernen i samtidighetsmodellen «share nothing».

Meldinger er enkle Elixir-termer (alle datatyper) som sendes fra én prosess til en annen prosess sin «postkasse».

  • send(pid, message): Legger message i postkassen til prosessen som identifiseres av pid.
  • receive do ... end: Venter på meldinger i postkassen til den gjeldende prosessen.

Svare tilbake

La oss se hvordan en «klient»-prosess kan sende en melding til en «server»-prosess og motta et svar.

Serverprosessen bruker en receive-blokk til å vente på og behandle innkommende meldinger. Deretter sender den et svar tilbake til klientens PID.

defmodule EchoServer do
  def loop do
    receive do
      {:echo, client_pid, message} ->
        send(client_pid, {:reply, message})
        loop() # Continue looping to receive more messages
    end
  end
end

defmodule Client do
  def run do
    # Start the server process
    server_pid = spawn(EchoServer, :loop, [])
    IO.puts "Echo server started with PID: #{inspect(server_pid)}"

    # Send a message to the server, including our own PID for reply
    send(server_pid, {:echo, self(), "Hello, server!"})
    IO.puts "Client sent 'Hello, server!' to #{inspect(server_pid)}"

    # Wait for a reply
    receive do
      {:reply, message} ->
        IO.puts "Client received reply: '#{message}'"
    end
  end
end

Client.run()

Hvorfor «share nothing» er viktig

Elixir-prosesser følger prinsippet «share nothing». Det betyr at prosesser ikke deler minne eller tilstand direkte.

I stedet kommuniserer de utelukkende gjennom meldingsutveksling. Dette designvalget gir betydelige fordeler:

  • Isolasjon: Hindrer én prosess i å ødelegge dataene til en annen.
  • Samtidighet: Gjør det enklere å forstå og skalere systemer på tvers av flere CPU-kjerner.
  • Feiltoleranse: Et krasj i én prosess påvirker ikke de andre, noe som gjør systemene mer robuste.

Smart meldingshåndtering

receive-blokken er svært kraftig fordi den bruker Elixirs mønstergjenkjenning.

Du kan definere ulike klausuler i receive for å matche bestemte meldingsstrukturer, mens andre meldinger ignoreres til en match blir funnet.

defmodule SmartReceiver do
  def loop do
    receive do
      {:greet, name} ->
        IO.puts "Hello, #{name}!"
        loop()
      {:farewell, name} ->
        IO.puts "Goodbye, #{name}!"
        loop()
      :quit ->
        IO.puts "Receiver quitting."
      _ -> # Catch-all for unmatched messages
        IO.puts "Received an unknown message."
        loop()
    end
  end

  def run do
    receiver_pid = spawn(SmartReceiver, :loop, [])
    send(receiver_pid, {:greet, "Alice"})
    send(receiver_pid, "Just a string")
    send(receiver_pid, {:farewell, "Bob"})
    send(receiver_pid, :quit)
    :timer.sleep(100) # Give processes time to finish
  end
end

SmartReceiver.run()

Bevare tilstand med rekursjon

Ettersom prosesser ikke deler minne, hvordan opprettholder de tilstand? Ved hjelp av rekursjon!

En prosess sender den gjeldende tilstanden sin som et argument til seg selv når den kaller løkkefunksjonen på nytt. Dette skaper en uforanderlig, sekvensiell flyt av tilstandsendringer.

defmodule Counter do
  def loop(count) do
    receive do
      :increment ->
        IO.puts "Incrementing to #{count + 1}"
        loop(count + 1)
      :get_count ->
        IO.puts "Current count: #{count}"
        loop(count)
      :stop ->
        IO.puts "Counter stopped at #{count}"
    end
  end

  def run do
    counter_pid = spawn(Counter, :loop, [0])
    send(counter_pid, :increment)
    send(counter_pid, :increment)
    send(counter_pid, :get_count)
    send(counter_pid, :stop)
    :timer.sleep(100)
  end
end

Counter.run()

Test: Prosesser og meldinger

Se på følgende Elixir-kode:

defmodule Quiz do
  def server_loop do
    receive do
      {:ping, client_pid} ->
        send(client_pid, :pong)
        server_loop()
    end
  end

  def client_action(server_pid) do
    send(server_pid, {:ping, self()})
    receive do
      :pong ->
        IO.puts "Received pong!"
      _ ->
        IO.puts "Received something else."
    end
  end

  def run do
    server_pid = spawn(Quiz, :server_loop, [])
    client_action(server_pid)
  end
end

Quiz.run()

Hva blir skrevet ut i konsollen når Quiz.run() kjøres?

Dine første steg innen samtidighet

Gratulerer! Du har tatt et stort steg inn i Elixirs kraftige modell for samtidighet.

Her er en kort oppsummering av det vi har gått gjennom:

  • Elixir-prosesser: Lette, isolerte enheter for kjøring.
  • PID-er: Unike identifikatorer for prosesser, brukt til adressering.
  • spawn og self(): Funksjoner for å opprette prosesser og hente PID-ene deres.
  • Meldingsutveksling: Den viktigste måten prosesser kommuniserer på, ved hjelp av send og receive.
  • «Del ingenting»: Prosesser deler ikke minne, noe som gir robuste og skalerbare systemer.
  • Mønstergjenkjenning: Brukes i receive for å håndtere ulike meldingstyper.
  • Tilstand: Opprettholdes gjennom rekursive funksjonskall.

Disse grunnleggende konseptene danner fundamentet for å bygge svært samtidige og feiltolerante applikasjoner i Elixir!

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 «Elixir-prosesser og meldingsutveksling» gratis?

Ja – hele teksten i «Elixir-prosesser og meldingsutveksling» 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 «Elixir-prosesser og meldingsutveksling»?

Lær om lette Elixir-prosesser, hvordan de kommuniserer og prinsippene for «share nothing»-samtidighet. 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 1 av 4.

Hvor lang tid tar leksjonen «Elixir-prosesser og meldingsutveksling»?

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. Elixir-prosesser og meldingsutveksling
  2. Implementering av GenServer-atferd
  3. Supervisorer og applikasjonsstruktur
  4. Samtidig arbeid med Task og Agent
← Tilbake til Elixir og Phoenix: Skalerbar backendutvikling