Επιτηρητές OTP

Αυτή η μετάφραση είναι πλήρως ενημερωμένη.

Οι Επιτηρητές είναι εξειδικευμένες διεργασίες με ένα σκοπό: την επισκόπηση άλλων διεργασιών. Αυτοί οι επιτηρητές μας επιτρέπουν να δημιουργούμε ανεκτικές στα σφάλματα εφαρμογές με το να επανεκινούν τις διεργασίες παιδιά όταν αποτυγχάνουν.

Πίνακας περιεχομένων

Ρύθμιση

Η μαγεία των επιτηρητών είναι στη συνάρτηση Supervisor.start_link/2. Επιπρόσθετα με την εκκίνηση του επιτηρητή και των παιδιών, μας επιτρέπει να ορίσουμε τη στρατηγική που χρησιμοποιεί ο επιτηρητής για τη διαχείριση των διεργασιών παιδιών.

Ας αρχίσουμε χρησιμοποιώντας την ενότητα SimpleQueue από το μάθημα Συγχρονισμός OTP:

Δημιουργήστε ένα νέο project χρησιμοποιόντας την εντολή mix new simple_queue --sup για να δημιουργήσετε ένα project με δέντρο επιτήρησης. Ο κώδικας για την ενότητα SimpleQueue θα πρέπει να μπει στο lib/simple_queue.ex και ο κώδικας επιτήρησης που θα προσθέσουμε θα μπει στο lib/simple_queue/application.ex

Οι διεργασίες παιδιά ορίζονται με τη χρήση μιας λίστας, ειδάλως με μια λίστα ονομάτων ενοτήτων:

defmodule SimpleQueue.Application do
  use Application

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

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

ή μια λίστα από τούπλες αν θέλετε να συμπεριλάβετε επιλογές διαμόρφωσης:

defmodule SimpleQueue.Application do
  use Application

  def start(_type, _args) do
    children = [
      {SimpleQueue, [1, 2, 3]}
    ]

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

Αν τρέξουμε την εντολή iex -S mix θα δούμε ότι η ενότητα SimpleQueue εκκινείται αυτόματα:

iex> SimpleQueue.queue
[1, 2, 3]

Αν η διεργασία της SimpleQueue κράσαρε ή τερματιζόταν ο επιτηρητής μας θα την επανεκκινούσε αύτοματα σαν να μην είχε συμβεί τίποτε.

Στρατηγικές

Αυτή τη στιγμή οι επιτηρητές μας έχουν διαθέσιμες τρείς διαφορετικές στρατηγικές επανεκκίνησης:

  • :one_for_one - Επανεκκίνηση μόνο της αποτυχημένης διεργασίας παιδί.

  • :one_for_all - Επανεκκίνηση όλων των διεργασιών παιδί σε περίπτωση αποτυχίας.

  • :rest_for_one - Επανεκκίνηση της αποτυχημένης διεργασίας και κάθε διεργασίας που ξεκίνησε μετά από αυτή.

Προδιαγραφή Παιδιού

Ο επιτηρητής μετά την εκκίνηση του θα πρέπει να γνωρίζει πως να εκκινήσει/σταματήσει/επανεκκινήσει τις διεργασίες παιδιά του. Κάθε ενότητα παιδιού θα πρέπει να έχει μια συνάρτηση child_spec/1 η οποία θα ορίζει αυτές τις συμπεριφορές. Οι μακροεντολές use GenServer, use Supervisor και use Agent προσδιορίζουν αυτόματα αυτή την μέθοδο για εμάς ( Η SimpleQueue έχει use Genserver, οπότε δεν χρειάζεται να τροποποιήσουμε την ενότητα), αλλά αν θέλετε να την καθορίσετε μόνοι σας, η συνάρτηση child_spec/1 θα πρέπει να επιστρέφει έναν χάρτη επιλογών:

def child_spec(opts) do
  %{
    id: SimpleQueue,
    start: {__MODULE__, :start_link, [opts]},
    shutdown: 5_000,
    restart: :permanent,
    type: :worker
  }
end
  • id - Απαιτούμενο κλειδί. Χρησιμοποιείται απο τον επιτηρητή ώστε να αναγνωρίσει τις προδιαγραφές της διεργασίας παιδιού.

  • start - Απαιτούμενο κλειδί. Η Ενότητα/Συνάρτηση/Ορίσματα που θα κληθούν όταν γίνει εκκίνηση απο τον επιτηρητή.

  • shutdown - Προεραιτικό κλειδί. Προσδιορίζει τον τρόπο λειτουργίας ενός παιδιού κατά τον τερματισμό. Οι επιλογές είναι:

    • :brutal_kill - Η διεργασία παιδί τερματίζεται αμέσως.

    • οποιοσδήποτε θετικός ακέραιος - ο χρόνος σε χιλιοστά του δευτερολέπτου που θα περιμένει ο επιτηρητής πριν τερματήσει την διεργασία παιδί. Αν η διεργασία είναι τύπου :worker, η προκαθορισμένη τιμή είναι 5000.

    • :infinity - Ο επιτηρητής θα περιμένει επ άπειρον πριν τερματήσει την διεργασία παιδί. Προκαθορισμένο για διεργασίες τύπου :supervisor. Δεν συνιστάται για διεργασίες τύπου :worker.

  • restart - Προαιρετικό κλειδί. Υπάρχουν διάφορες προσεγγίσεις για το χειρισμό κρασαρισμάτων διεργασιών παιδιών:

    • :permanent - Η διεργασία παιδί πάντα επανεκκινείται. Προκαθορισμένο για όλες τις διεργασίες

    • :temporary - Η διεργασία παιδί δεν επανεκκινείται ποτέ.

    • :transient - Η διεργασία παιδί επανεκκινείται μόνο αν τερματιστεί απρόβλεπτα.

  • type - Προαιρετικό κλειδί. Οι διεργασίες μπορεί να είνα είτε τύπου :worker είτε τύπου :supervisor. Ο προκαθορισμένος τύπος είναι :worker.

DynamicSupervisor

Η εκκίνηση των επιτηρητών συνήθως συνοδεύεται από μια λίστα παιδιών που εκκινούν με την εκκίνηση της εφαρμογής. Παρ’όλ’αυτά, μερικές φορές τα επιτηρούμενα παιδιά δεν θα είναι γνωστά με την εκκίνηση της εφαρμογής μας (για παράδειγμα, μπορεί να έχουμε μια εφαρμογή web που εκκινεί μια νέα διεργασία για να χειριστεί την σύνδεση ενός χρήστη στην σελίδα μας. Για αυτές τις περιπτώσεις θα θέλουμε έναν επιτηρητή του οποίου οι διεργασίες παιδιά θα εκκινούνται κατά παραγγελία. Ο DynamicSupervisor χρησιμοποιείται για να χειριστεί αυτήν την περίπτωση.

Από την στιγμή που δεν θα προσδιορίσουμε διεργασίες παιδιά, χρειάζεται μόνο να καθορίσουμε τις επιλογές εκτέλεσης για τον επιτηρητή. Ο DynamicSupervisor υποστηρίζει μόνο στρατηγικές επιτήρησης :one_for_one:

options = [
  name: SimpleQueue.Supervisor,
  strategy: :one_for_one
]

DynamicSupervisor.start_link(options)

Κατόπιν, για να εκκινήσουμε δυναμικά μια νέα SimpleQueue θα χρησιμοποιήσουμε την start_child/2 που παίρνει έναν επιτηρητή και τις προδιαγραφές του παιδιού (επαναλαμβάνουμε, η SimpleQueue χρησιμοποιεί την use GenServer άρα οι προδιαγραφές του παιδιού είναι ήδη ορισμένες):

{:ok, pid} = DynamicSupervisor.start_child(SimpleQueue.Supervisor, SimpleQueue)

Επιτηρητής Εργασίας

Οι εργασίες έχουν τον δικό τους εξειδικευμένο Επιτηρητή, τον Task.Supervisor. Σχεδιασμένος για δυναμικά δημιουργημένες εργασίες, ο επιτηρητής χρησιμοποιεί την :simple_one_for_one.

Εγκατάσταση

Η συμπερίληψη του Task.Supervisor δεν έχει καμμία διαφορά από τους άλλους επιτηρητές:

children = [
  {Task.Supervisor, name: ExampleApp.TaskSupervisor, restart: :transient}
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Η μεγάλη διαφορά μεταξύ Supervisor και Task.Supervisor είναι ότι η προκαθορισμένη του στρατηγική επανεκίνησης είναι η :temporary (οι εργασίες δεν θα επανεκινούνται ποτέ).

Επιτηρούμενες Εργασίες

Με τον επιτηρητή να έχει ξεκινήσει, μπορούμε να χρησιμοποιήσουμε τη συνάρτηση start_child/2 για να δημιουργήσουμε μια επιτηρούμενη εργασία:

{:ok, pid} = Task.Supervisor.start_child(ExampleApp.TaskSupervisor, fn -> background_work end)

Αν η εργασία μας κρασάρει πρόωρα, θα επανεκκινηθεί. Αυτό μπορεί να αποδειχθεί ιδιαίτερα χρήσιμο όταν δουλεύουμε με εισερχόμενες συνδέσεις ή επεξεργαζόμαστε δεδομένα στο παρασκήνιο.

Caught a mistake or want to contribute to the lesson? Edit this page on GitHub!