Fork me on GitHub

Poolboy

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

Μπορείτε πολύ εύκολα να εξαντλήσετε τους πόρους τους συστήματός σας αν δεν περιορίσετε τον μέγιστο αριθμό ταυτόχρονων διεργασιών που μπορεί να ξεκινήσει το πρόγραμμά σας. Το Poolboy είναι μια ευρέως διαδεδομένη, ελαφριά βιβλιοθήκη δημιουργίας σετ για την Erlang η οποία αντιμετωπίζει αυτό το ζήτημα.

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

Γιατί να χρησιμοποιήσουμε το Poolboy;

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

Η λύση σε αυτό το πρόβλημα είναι η χρήση ενός σετ εργατών (διεργασίες) για να περιορίσουμε τον αριθμό συνδέσεων αντί να δημιουργούμε μια διεργασία για κάθε εγγραφή χρήστη. Τότε μπορείτε εύκολα να αποτρέψετε την εξάντληση των πόρων του συστήματος σας.

Εδώ έρχεται το Poolboy. Σας επιτρέπει να δημιουργήσετε εύκολα μια λίστα εργατών που διαχειρίζονται από έναν Supervisor χωρίς καμμία ενέργεια από μέρους σας. Για την ακρίβεια υπάρχουν πολλές βιβλιοθήκες που χρησιμοποιούν το Poolboy στο εσωτερικό τους. Για παράδειγμα: η λίστα συνδέσεων της postgrex (η οποία με τη σειρά της αξιοποιείται από την Ecto όταν χρησιμοποιούμε την PostgreSQL) και η redis_poolex (Λίστα συνδέσεων Redis) είναι μερικές από τις πιο δημοφιλείς βιβλιοθήκες που χρησιμοποιούν το Poolboy.

Εγκατάσταση

Η εγκατάσταση είναι πανεύκολη με το mix. Το μόνο που πρέπει να κάνουμε είναι να προσθέσουμε το Poolboy σαν εξάρτηση στο mix.exs μας.

Ας δημιουργήσουμε μια εφαρμογή πρώτα:

$ mix new poolboy_app --sup

Ας προσθέσουμε το Poolboy σαν εξάρτηση στο mix.exs μας.

defp deps do
  [{:poolboy, "~> 1.5.1"}]
end

Τότε ας κατεβάσουμε τις εξαρτήσεις, συμπεριλαμβανομένου του Poolboy:

$ mix deps.get

Οι επιλογές ρύθμισης

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

Ρυθμίζοντας το Poolboy

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

Ας ορίσουμε τις επιλογές ρύθμισης του Poolboy και ας το προσθέσουμε σαν εργάτης παιδί μέρος της εκκίνησης της εφαρμογής μας.

defmodule PoolboyApp.Application do
  @moduledoc false
  
  use Application

  defp poolboy_config do
    [{:name, {:local, :worker}},
      {:worker_module, PoolboyApp.Worker},
      {:size, 5},
      {:max_overflow, 2}]
  end

  def start(_type, _args) do
    children = [
      :poolboy.child_spec(:worker, poolboy_config())
    ]

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

Το πρώτο πράγμα που ορίσαμε είναι οι επιλογές ρυθμίσεων για τη λίστα. Ονομάσαμε τη λίστα μας :worker και ορίσαμε το πεδίο δράσης σε :local. Στη συνέχεια προσδιορίσαμε την ενότητα PoolboyApp.Worker σαν την :worker_module που θα χρησιμοποιεί αυτή η λίστα. Επίσης ορίσαμε το :size της λίστας να είναι 5 εργάτες. Επίσης, σε περίπτωση που οι εργάτες μας είναι απασχολημένοι, ρυθμίζουμε τη δημιουργία δύο επιπλέον εργατών για να βοηθήσουν με το φόρτο, χρησιμοποιώντας την επιλογή :max_overflow. (οι overflow εργάτες παύουν να υπάρχουν μόλις ολοκληρώσουν την εργασία τους.)

Στη συνέχεια, προσθέσαμε τη συνάρτηση poolboy.child_spec/2 στον πίνακα των παιδιών ώστε η λίστα εργατών να ξεκινάει με την εκκίνηση της εφαρμογή. Δέχεται δύο ορίσματα: το όνομα της λίστας και τις ρυθμίσεις της.

Δημιουργία Εργάτη

Η ενότητα εργάτη θα είναι ένας απλός GenServer υπολογισμού της τετραγωνικής ρίζας ενός αριθμού, που κοιμάται για ένα δευτερόλεπτο και τυπώνει το pid του εργάτη:

defmodule Worker do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, nil, [])
  end

  def init(_) do
    {:ok, nil}
  end

  def handle_call({:square_root, x}, _from, state) do
    IO.puts "process #{inspect self} calculating square root of #{x}"
    :timer.sleep(1000)
    {:reply, :math.sqrt(x), state}
  end
end

Χρήση του Poolboy

Τώρα που έχουμε τον PoolboyApp.Worker μας, ας δοκιμάσουμε το Poolboy. Ας γράψουμε μια απλή ενότητα που δημιουργεί ταυτόχρονες διεργασίες χρησιμοποιώντας το Poolboy. Η :poolboy.transaction/3 είναι η συνάρτηση που μπορείτε να χρησιμοποιείτε για τη διεπαφή με τη λίστα εργατών. Γράψτε το lib/poolboy_app/test.ex:

defmodule PoolboyApp.Test do
  @timeout 60000

  def start do
    1..20
    |> Enum.map(fn(i) -> async_call_square_root(i) end)
    |> Enum.each(fn(task) -> await_and_inspect(task) end)
  end
  
  defp async_call_square_root(i) do
    Task.async(fn ->
      :poolboy.transaction(:worker, fn(pid) -> GenServer.call(pid, {:square_root, i}) end, @timeout)
    end)
  end
  
  defp await_and_inspect(task), do: task |> Task.await(@timeout) |> IO.inspect()
end

Τρέξτε τη δοκιμαστική συνάρτηση για να δείτε το αποτέλεσμα.

$ iex -S mix
iex> PoolboyApp.Test.start()
process #PID<0.182.0> calculating square root of 7
process #PID<0.181.0> calculating square root of 6
process #PID<0.157.0> calculating square root of 2
process #PID<0.155.0> calculating square root of 4
process #PID<0.154.0> calculating square root of 5
process #PID<0.158.0> calculating square root of 1
process #PID<0.156.0> calculating square root of 3
...

Αν δεν έχετε διαθέσιμους εργάτες στη λίστα σας, το Poolboy θα έχει μια λήξη ορίου χρόνου μετά το τέλος της περιόδου λήξης ορίου χρόνου (πέντε δευτερόλεπτα) και δεν θα δέχεται νέες αιτήσεις. Στο παράδειγμά μας, αυξήσαμε το προκαθορισμένο όριο λήξης χρόνου στο ένα λεπτό ώστε να επιδείξουμε πως μπορούμε να αλλάξουμε την προκαθορισμένη τιμή λήξης ορίου χρόνου. Σε αυτή την εφαρμογή, μπορείτε να δείτε το σφάλμα αλλάζοντας την τιμή της @timeout σε λιγότερο από 1000.

Παρόλο που προσπαθούμε να δημιουργήσουμε πολλαπλές διεργασίες (είκοσι στο σύνολό τους στο παραπάνω παράδειγμα), η συνάρτηση :poolboy.transaction/2 θα περιορίσει το σύνολο των δημιουργημένων διεργασιών σε πέντε (συν δύο εργάτες υπερχείλισης αν απαιτούνται) όπως το ορίσαμε στις ρυθμίσεις μας. Όλες οι αιτήσεις θα χειριστούν από τη λίστα εργατών μας αντί να δημιουργείται μια νέα διεργασία για κάθε μια αίτηση.


Contributors

loading...



Share This Page