Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen · Les

Dynamisch procesbeheer

Beheers het dynamisch starten en stoppen van childprocessen binnen supervisors voor aanpasbare en resource-efficiënte systemen.

Les 2 van 411 stappen

Dynamisch procesbeheer is een gratis Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen bevat in totaal 4 lessen.

Overzicht van dynamische processen

In Erlang hoeven niet alle processen te worden gestart wanneer je toepassing voor het eerst wordt opgestart. Soms heb je processen nodig die op aanvraag worden gemaakt en vernietigd.

Deze worden dynamische processen genoemd en zijn cruciaal voor het bouwen van aanpasbare systemen die efficiënt met bronnen omgaan. Je kunt ze zien als tijdelijke werkers die supervisors naar behoefte kunnen aannemen en ontslaan.

Waarom dynamisch beheer?

Dynamisch procesbeheer biedt verschillende belangrijke voordelen:

  • Efficiënt gebruik van resources: Start processen alleen wanneer ze echt nodig zijn, zodat je geheugen en CPU bespaart.
  • Aanpasbaarheid: Reageer op wisselende belastingen door het aantal werkprocessen op of af te schalen.
  • Taken op aanvraag: Ideaal voor het afhandelen van tijdelijke taken, zoals een nieuwe clientverbinding, een verzoek om een bestand te converteren of één databasequery.

Dit verschilt van statische kinderen, die altijd deel uitmaken van de oorspronkelijke configuratie van de supervisor.

De strategie `simple_one_for_one`

Om dynamische kinderen te beheren, gebruiken supervisors vaak de herstartstrategie simple_one_for_one. Deze strategie is speciaal ontworpen voor supervisors die na het opstarten dynamisch kinderen toevoegen.

  • Je kunt kinderen toevoegen zonder ze vooraf te definiëren in de functie init/1 van de supervisor.
  • Elk dynamisch toegevoegd kind wordt behandeld als een unieke entiteit, zelfs als ze dezelfde onderliggende module gebruiken.
  • Als een dynamisch kind crasht, herstart simple_one_for_one alleen dat kind, niet alle kinderen.

Dynamische kinderen starten

Je start een dynamisch kindproces met de functie supervisor:start_child/2. Deze functie accepteert twee argumenten:

  • SupRef: De naam of PID van de supervisor.
  • ChildSpec: Een map (of in oudere versies van Erlang een lijst met tuples) die het kindproces beschrijft.

Deze functie retourneert {ok, ChildPid} of een fout als het kind niet kon worden gestart.

Specificatie van een dynamisch kind

Een ChildSpec voor een dynamisch kind lijkt op een statische specificatie, maar het veld id is essentieel om instanties van elkaar te onderscheiden. Dit is een gebruikelijke structuur:

  • id: Een unieke atom of term om deze specifieke instantie van het kind te identificeren (bijvoorbeeld client_123).
  • start: Een tuple {Module, Function, Args} die wordt aangeroepen om het kind te starten (bijvoorbeeld {gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]}).
  • type: worker of supervisor.
  • restart: Vaak transient voor dynamische werkprocessen. Dit betekent dat ze alleen worden herstart als ze onverwacht crashen, niet als ze normaal afsluiten.
  • shutdown: Time-out voor gecontroleerd afsluiten.

Code: dynamische werkprocessen maken

Dit voorbeeld laat zien hoe een supervisor dynamisch twee unieke processen voor 'taken' start. Elke taak houdt zijn eigen teller bij.

Voer het uit om te zien hoe nieuwe processen worden gestart en hoe je afzonderlijk met ze kunt communiceren.

-module(dynamic_start_example).
-behaviour(supervisor).

-export([start_link/0, init/1, start_task/1, call_task/2, run/0]).
-export([task_init/1, task_handle_call/3, task_terminate/2]).

% --- Supervisor part ---
start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    Strategy = {simple_one_for_one, 0, 1},
    Children = [],
    {ok, {Strategy, Children}}.

start_task(TaskId) ->
    io:format("Supervisor: Starting task ~p~n", [TaskId]),
    ChildSpec = #{
        id => TaskId,
        start => {gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]},
        type => worker,
        restart => transient,
        shutdown => 5000
    },
    supervisor:start_child(?MODULE, ChildSpec).

call_task(TaskId, Message) ->
    gen_server:call(TaskId, Message).

% --- Worker part (this module acts as a gen_server for the tasks) ---
task_init([]) ->
    io:format("Task ~p: Initializing with count 0~n", [self()]),
    {ok, 0}. % Initial state for the task

task_handle_call(get_count, _From, State) ->
    io:format("Task ~p: Getting count ~p~n", [self(), State]),
    {reply, State, State}.
task_handle_call({increment, Value}, _From, State) ->
    NewState = State + Value,
    io:format("Task ~p: Incrementing by ~p to ~p~n", [self(), Value, NewState]),
    {reply, NewState, NewState}.

task_terminate(_Reason, State) ->
    io:format("Task ~p: Terminating with final state ~p~n", [self(), State]),
    ok.

% --- Entry point for runnable example ---
run() ->
    io:format("~n--- Starting Dynamic Task Example ---~n"),
    {ok, SupPid} = dynamic_start_example:start_link(),
    io:format("Supervisor started: ~p~n", [SupPid]),

    io:format("~nStarting Task 'task_alpha':~n"),
    dynamic_start_example:start_task(task_alpha),
    timer:sleep(100), % Give it a moment to start

    Count1 = dynamic_start_example:call_task(task_alpha, get_count),
    io:format("Task 'task_alpha' count: ~p~n", [Count1]),

    dynamic_start_example:call_task(task_alpha, {increment, 7}),
    Count2 = dynamic_start_example:call_task(task_alpha, get_count),
    io:format("Task 'task_alpha' count after increment: ~p~n", [Count2]),

    io:format("~nStarting Task 'task_beta':~n"),
    dynamic_start_example:start_task(task_beta),
    timer:sleep(100),

    Count3 = dynamic_start_example:call_task(task_beta, get_count),
    io:format("Task 'task_beta' count: ~p~n", [Count3]),

    dynamic_start_example:call_task(task_beta, {increment, 12}),
    Count4 = dynamic_start_example:call_task(task_beta, get_count),
    io:format("Task 'task_beta' count after increment: ~p~n", [Count4]),

    % In a real app, you'd stop the supervisor or individual tasks here.
    % For this example, we'll let them run.
    io:format("--- Dynamic Task Start Demo Finished ---~n"),
    ok.

Dynamische kinderen beëindigen

Net zoals je processen dynamisch kunt starten, kun je ze ook stoppen. Hiervoor gebruik je de functie supervisor:terminate_child/2.

  • De functie accepteert SupRef en ChildId (de id uit de kindspecificatie) als argumenten.
  • De supervisor stuurt een exitsignaal naar het kind, waarmee een gecontroleerde afsluiting wordt gestart.
  • Na beëindiging wordt het kind uit de lijst van de supervisor verwijderd, waardoor resources vrijkomen.

Dit is cruciaal voor het beheren van resources en om te voorkomen dat processen onnodig blijven bestaan.

Code: dynamische werkprocessen beëindigen

Dit voorbeeld laat zien hoe je een taak start, ermee communiceert en deze vervolgens gecontroleerd stopt met terminate_child/2. Let op de uitvoer wanneer de taak wordt beëindigd.

-module(dynamic_stop_example).
-behaviour(supervisor).

-export([start_link/0, init/1, start_task/1, call_task/2, stop_task/1, run/0]).
-export([task_init/1, task_handle_call/3, task_terminate/2]).

% --- Supervisor part ---
start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    Strategy = {simple_one_for_one, 0, 1},
    Children = [],
    {ok, {Strategy, Children}}.

start_task(TaskId) ->
    io:format("Supervisor: Starting task ~p~n", [TaskId]),
    ChildSpec = #{
        id => TaskId,
        start => {gen_server, start_link, [{local, TaskId}, ?MODULE, [], []]},
        type => worker,
        restart => transient,
        shutdown => 5000
    },
    supervisor:start_child(?MODULE, ChildSpec).

call_task(TaskId, Message) ->
    gen_server:call(TaskId, Message).

stop_task(TaskId) ->
    io:format("Supervisor: Stopping task ~p~n", [TaskId]),
    supervisor:terminate_child(?MODULE, TaskId).

% --- Worker part (this module acts as a gen_server for the tasks) ---
task_init([]) ->
    io:format("Task ~p: Initializing with count 0~n", [self()]),
    {ok, 0}.

task_handle_call(get_count, _From, State) ->
    io:format("Task ~p: Getting count ~p~n", [self(), State]),
    {reply, State, State}.
task_handle_call({increment, Value}, _From, State) ->
    NewState = State + Value,
    io:format("Task ~p: Incrementing by ~p to ~p~n", [self(), Value, NewState]),
    {reply, NewState, NewState}.

task_terminate(_Reason, State) ->
    io:format("Task ~p: Terminating with final state ~p~n", [self(), State]),
    ok.

% --- Entry point for runnable example ---
run() ->
    io:format("~n--- Terminating Dynamic Task Example ---~n"),
    {ok, SupPid} = dynamic_stop_example:start_link(),
    io:format("Supervisor started: ~p~n", [SupPid]),

    io:format("~nStarting Task 'temp_task':~n"),
    dynamic_stop_example:start_task(temp_task),
    timer:sleep(100),

    dynamic_stop_example:call_task(temp_task, {increment, 100}),
    Count = dynamic_stop_example:call_task(temp_task, get_count),
    io:format("Task 'temp_task' current count: ~p~n", [Count]),

    io:format("~nStopping Task 'temp_task':~n"),
    dynamic_stop_example:stop_task(temp_task),
    timer:sleep(100), % Give it a moment to terminate

    io:format("~nAttempting to call 'temp_task' after termination (will fail):~n"),
    CatchResult = try dynamic_stop_example:call_task(temp_task, get_count) of
                      Result -> Result
                  catch
                      error:Reason -> {error, Reason}
                  end,
    io:format("Call result after stop: ~p~n", [CatchResult]),

    supervisor:stop(SupPid), % Clean up the supervisor
    io:format("Supervisor stopped.~n"),
    io:format("--- Dynamic Task Stop Demo Finished ---~n"),
    ok.

Praktische toepassingsgebieden

Dynamisch procesbeheer is een krachtig hulpmiddel in Erlang. Hier zijn enkele veelvoorkomende situaties waarin het goed van pas komt:

  • Webservers: Een nieuw proces starten voor elke binnenkomende clientverbinding.
  • Verbindingspools: Een pool met databaseverbindingen beheren en indien nodig nieuwe verbindingen maken.
  • Batchverwerkers: Werkprocessen starten om items uit een wachtrij af te handelen.
  • Gebruikerssessies: Afzonderlijke gebruikerssessies beheren in een toepassing voor meerdere gebruikers.

Hiermee kan je systeem zich aanpassen aan wisselende belastingen en tijdelijke resources efficiënt beheren.

Korte controle

Je hebt geleerd hoe je dynamische kindprocessen start en stopt. Laten we controleren of je de stof begrijpt.

Samenvatting: dynamische processen

We hebben dynamisch procesbeheer verkend, een belangrijke techniek voor het bouwen van aanpasbare Erlang-systemen:

  • Dynamische processen worden op aanvraag gemaakt en vernietigd, waardoor resources worden bespaard.
  • De strategie simple_one_for_one wordt doorgaans gebruikt door supervisors die dynamische kinderen beheren.
  • Met supervisor:start_child/2 voeg je nieuwe processen toe, waarbij je voor elk proces een unieke kindspecificatie opgeeft.
  • supervisor:terminate_child/2 zorgt voor een gecontroleerde afsluiting en verwijdering van dynamische processen.

Als je dit beheerst, kunnen je toepassingen efficiënt opschalen en reageren op veranderende behoeften.

Gratis beginnen

Leer Erlang met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
12
Lessen
48

Veelgestelde vragen

Is de les “Dynamisch procesbeheer” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen, waaronder “Dynamisch procesbeheer”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen bevat in totaal 4 lessen.

Wat leer ik in “Dynamisch procesbeheer”?

Beheers het dynamisch starten en stoppen van childprocessen binnen supervisors voor aanpasbare en resource-efficiënte systemen. Je oefent met Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen te beginnen?

Ervaring vooraf is niet nodig. Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Dynamisch procesbeheer”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen?

Ja. Elke les over Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Complexe supervisiebomen
  2. Dynamisch procesbeheer
  3. Geavanceerde herstartstrategieën
  4. Supervisor bridges en gemengde proceshiërarchieën
← Terug naar Erlang OTP: programmeren van gedistribueerde en fouttolerante systemen