Δομικά στοιχεία κατανεμημένων εφαρμογών. Δεύτερη προσέγγιση

ανακοίνωση

Συνάδελφοι, στα μέσα του καλοκαιριού σκοπεύω να δημοσιεύσω μια άλλη σειρά άρθρων σχετικά με το σχεδιασμό συστημάτων ουράς: "Το πείραμα VTrade" - μια προσπάθεια να γράψω ένα πλαίσιο για συστήματα συναλλαγών. Η σειρά θα εξετάσει τη θεωρία και την πρακτική της δημιουργίας ενός ανταλλακτηρίου, μιας δημοπρασίας και ενός καταστήματος. Στο τέλος του άρθρου, σας προσκαλώ να ψηφίσετε τα θέματα που σας ενδιαφέρουν περισσότερο.

Δομικά στοιχεία κατανεμημένων εφαρμογών. Δεύτερη προσέγγιση

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

Σήμερα θα θίξουμε θέματα ανάπτυξης της βάσης κώδικα και έργων γενικότερα.

Οργάνωση υπηρεσιών

Στην πραγματική ζωή, κατά την ανάπτυξη μιας υπηρεσίας, συχνά πρέπει να συνδυάσετε πολλά μοτίβα αλληλεπίδρασης σε έναν ελεγκτή. Για παράδειγμα, η υπηρεσία χρηστών, η οποία επιλύει το πρόβλημα της διαχείρισης των προφίλ χρηστών του έργου, πρέπει να ανταποκρίνεται σε αιτήματα req-resp και να αναφέρει ενημερώσεις προφίλ μέσω pub-sub. Αυτή η περίπτωση είναι αρκετά απλή: πίσω από την ανταλλαγή μηνυμάτων υπάρχει ένας ελεγκτής που εφαρμόζει τη λογική της υπηρεσίας και δημοσιεύει ενημερώσεις.

Η κατάσταση γίνεται πιο περίπλοκη όταν χρειάζεται να εφαρμόσουμε μια κατανεμημένη υπηρεσία με ανοχή σε σφάλματα. Ας φανταστούμε ότι οι απαιτήσεις για τους χρήστες έχουν αλλάξει:

  1. τώρα η υπηρεσία θα πρέπει να επεξεργάζεται αιτήματα σε 5 κόμβους συμπλέγματος,
  2. να είναι σε θέση να εκτελεί εργασίες επεξεργασίας παρασκηνίου,
  3. και επίσης να μπορείτε να διαχειρίζεστε δυναμικά τις λίστες συνδρομών για ενημερώσεις προφίλ.

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

Η επίσημη περιγραφή της υπηρεσίας των χρηστών έχει γίνει πιο περίπλοκη. Από την πλευρά ενός προγραμματιστή, οι αλλαγές είναι ελάχιστες λόγω της χρήσης μηνυμάτων. Για να ικανοποιήσουμε την πρώτη απαίτηση, πρέπει να διαμορφώσουμε την εξισορρόπηση στο σημείο ανταλλαγής req-resp.

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

Το σημείο 3 απαιτεί την επέκταση προτύπου pub-sub. Και για υλοποίηση, αφού δημιουργήσουμε ένα σημείο ανταλλαγής pub-sub, πρέπει να εκκινήσουμε επιπλέον τον ελεγκτή αυτού του σημείου στην υπηρεσία μας. Έτσι, είναι σαν να μεταφέρουμε τη λογική για την επεξεργασία των συνδρομών και τις διαγραφές από το επίπεδο ανταλλαγής μηνυμάτων στην υλοποίηση των χρηστών.

Ως αποτέλεσμα, η αποσύνθεση του προβλήματος έδειξε ότι για να ανταποκριθούμε στις απαιτήσεις, πρέπει να εκκινήσουμε 5 παρουσίες της υπηρεσίας σε διαφορετικούς κόμβους και να δημιουργήσουμε μια πρόσθετη οντότητα - έναν ελεγκτή pub-sub, υπεύθυνο για τη συνδρομή.
Για να εκτελέσετε 5 χειριστές, δεν χρειάζεται να αλλάξετε τον κωδικό υπηρεσίας. Η μόνη πρόσθετη ενέργεια είναι ο καθορισμός κανόνων εξισορρόπησης στο σημείο ανταλλαγής, για τους οποίους θα μιλήσουμε λίγο αργότερα.
Υπάρχει επίσης μια πρόσθετη πολυπλοκότητα: ο ελεγκτής pub-sub και ο προσαρμοσμένος προγραμματιστής εργασιών πρέπει να λειτουργούν σε ένα μόνο αντίγραφο. Και πάλι, η υπηρεσία ανταλλαγής μηνυμάτων, ως θεμελιώδης, πρέπει να παρέχει έναν μηχανισμό για την επιλογή ενός ηγέτη.

Επιλογή αρχηγού

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

Σε συστήματα που δεν είναι επιρρεπή σε συγκεντρωτισμό, χρησιμοποιούνται καθολικοί και βασισμένοι στη συναίνεση αλγόριθμοι, όπως τα paxos ή τα raft.
Δεδομένου ότι τα μηνύματα είναι μεσίτης και κεντρικό στοιχείο, γνωρίζει για όλους τους ελεγκτές υπηρεσιών - υποψήφιους ηγέτες. Η ανταλλαγή μηνυμάτων μπορεί να διορίσει έναν ηγέτη χωρίς ψήφο.

Μετά την εκκίνηση και τη σύνδεση στο σημείο ανταλλαγής, όλες οι υπηρεσίες λαμβάνουν ένα μήνυμα συστήματος #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Αν LeaderPid συμπίπτει με pid τρέχουσα διαδικασία, ορίζεται ως αρχηγός και η λίστα Servers περιλαμβάνει όλους τους κόμβους και τις παραμέτρους τους.
Τη στιγμή που εμφανίζεται ένας νέος και αποσυνδεθεί ένας λειτουργικός κόμβος συμπλέγματος, λαμβάνουν όλοι οι ελεγκτές υπηρεσίας #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} αντιστοίχως.

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

Διαμεσολαβητές

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

Ένα κλασικό παράδειγμα βελτιστοποίησης pub-sub είναι μια κατανεμημένη εφαρμογή με επιχειρηματικό πυρήνα που δημιουργεί συμβάντα ενημέρωσης, όπως αλλαγές τιμών στην αγορά, και ένα επίπεδο πρόσβασης - N διακομιστές που παρέχουν ένα API websocket για πελάτες ιστού.
Εάν αποφασίσετε κατά μέτωπο, τότε η εξυπηρέτηση πελατών μοιάζει με αυτό:

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

Ας φανταστούμε ότι έχουμε 50000 συνδρομητές στο θέμα «ειδήσεις». Οι συνδρομητές κατανέμονται ομοιόμορφα σε 5 διακομιστές. Ως αποτέλεσμα, κάθε ενημέρωση, που φτάνει στο σημείο ανταλλαγής, θα αναπαράγεται 50000 φορές: 10000 φορές σε κάθε διακομιστή, ανάλογα με τον αριθμό των συνδρομητών σε αυτόν. Δεν είναι πολύ αποτελεσματικό σχέδιο, σωστά;
Για να βελτιώσουμε την κατάσταση, ας εισαγάγουμε έναν διακομιστή μεσολάβησης που έχει το ίδιο όνομα με το σημείο ανταλλαγής. Ο παγκόσμιος καταχωρητής ονομάτων πρέπει να μπορεί να επιστρέψει την πλησιέστερη διαδικασία με όνομα, αυτό είναι σημαντικό.

Ας εκκινήσουμε αυτόν τον διακομιστή μεσολάβησης στους διακομιστές επιπέδου πρόσβασης και όλες οι διεργασίες μας που εξυπηρετούν το api websocket θα εγγραφούν σε αυτό και όχι στο αρχικό σημείο ανταλλαγής pub-sub στον πυρήνα. Το Proxy εγγράφεται στον πυρήνα μόνο στην περίπτωση μιας μοναδικής συνδρομής και αναπαράγει το εισερχόμενο μήνυμα σε όλους τους συνδρομητές του.
Ως αποτέλεσμα, θα σταλούν 5 μηνύματα μεταξύ του πυρήνα και των διακομιστών πρόσβασης, αντί για 50000.

Δρομολόγηση και εξισορρόπηση

Req-Resp

Στην τρέχουσα εφαρμογή ανταλλαγής μηνυμάτων, υπάρχουν 7 στρατηγικές διανομής αιτημάτων:

  • default. Το αίτημα αποστέλλεται σε όλους τους ελεγκτές.
  • round-robin. Τα αιτήματα απαριθμούνται και κατανέμονται κυκλικά μεταξύ των ελεγκτών.
  • consensus. Οι ελεγκτές που υπηρετούν την υπηρεσία χωρίζονται σε αρχηγούς και σκλάβους. Τα αιτήματα αποστέλλονται μόνο στον αρχηγό.
  • consensus & round-robin. Η ομάδα έχει έναν αρχηγό, αλλά τα αιτήματα διανέμονται σε όλα τα μέλη.
  • sticky. Η συνάρτηση κατακερματισμού υπολογίζεται και εκχωρείται σε έναν συγκεκριμένο χειριστή. Τα επόμενα αιτήματα με αυτήν την υπογραφή πηγαίνουν στον ίδιο χειριστή.
  • sticky-fun. Κατά την προετοιμασία του σημείου ανταλλαγής, η συνάρτηση υπολογισμού κατακερματισμού για sticky εξισορρόπηση.
  • fun. Παρόμοια με το sticky-fun, μόνο εσείς μπορείτε επιπλέον να το ανακατευθύνετε, να το απορρίψετε ή να το επεξεργαστείτε εκ των προτέρων.

Η στρατηγική διανομής ορίζεται όταν αρχικοποιείται το σημείο ανταλλαγής.

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

  • Ετικέτα σύνδεσης. Σας επιτρέπει να καταλάβετε μέσω ποιας σύνδεσης προήλθαν τα γεγονότα. Χρησιμοποιείται όταν μια διεργασία ελεγκτή συνδέεται στο ίδιο σημείο ανταλλαγής, αλλά με διαφορετικά κλειδιά δρομολόγησης.
  • Ετικέτα υπηρεσίας. Σας επιτρέπει να συνδυάζετε χειριστές σε ομάδες για μία υπηρεσία και να επεκτείνετε τις δυνατότητες δρομολόγησης και εξισορρόπησης. Για το μοτίβο req-resp, η δρομολόγηση είναι γραμμική. Στέλνουμε ένα αίτημα στο σημείο ανταλλαγής και στη συνέχεια το διαβιβάζει στην υπηρεσία. Αν όμως χρειαστεί να χωρίσουμε τους χειριστές σε λογικές ομάδες, τότε ο διαχωρισμός γίνεται χρησιμοποιώντας ετικέτες. Κατά τον καθορισμό μιας ετικέτας, το αίτημα θα σταλεί σε μια συγκεκριμένη ομάδα ελεγκτών.
  • Ετικέτα αιτήματος. Σας επιτρέπει να διακρίνετε τις απαντήσεις. Δεδομένου ότι το σύστημά μας είναι ασύγχρονο, για να επεξεργαστούμε τις απαντήσεις της υπηρεσίας πρέπει να μπορούμε να καθορίσουμε ένα RequestTag κατά την αποστολή ενός αιτήματος. Από αυτό θα μπορέσουμε να καταλάβουμε την απάντηση σε ποιο αίτημα μας ήρθε.

Pub-sub

Για pub-sub όλα είναι λίγο πιο απλά. Έχουμε ένα σημείο ανταλλαγής στο οποίο δημοσιεύονται τα μηνύματα. Το σημείο ανταλλαγής διανέμει μηνύματα μεταξύ των συνδρομητών που έχουν εγγραφεί στα κλειδιά δρομολόγησης που χρειάζονται (μπορούμε να πούμε ότι αυτό είναι ανάλογο με τα θέματα).

Επεκτασιμότητα και ανοχή σφαλμάτων

Η επεκτασιμότητα του συστήματος στο σύνολό του εξαρτάται από τον βαθμό επεκτασιμότητας των στρωμάτων και των στοιχείων του συστήματος:

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

Η επιτυχία ενός έργου εξαρτάται συχνά από την απλότητα και την ταχύτητα της κλιμάκωσης. Η ανταλλαγή μηνυμάτων στην τρέχουσα έκδοσή της αυξάνεται παράλληλα με την εφαρμογή. Ακόμα κι αν μας λείπει ένα cluster 50-60 μηχανών, μπορούμε να καταφύγουμε στην ομοσπονδία. Δυστυχώς, το θέμα της ομοσπονδίας ξεφεύγει από το πεδίο εφαρμογής αυτού του άρθρου.

Rezervirovanye

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

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

Παραγωγικότητα

Ας προσπαθήσουμε να συγκρίνουμε τουλάχιστον κατά προσέγγιση την απόδοση του rabbitmq και των προσαρμοσμένων μηνυμάτων μας.
βρήκα επίσημα αποτελέσματα Δοκιμές rabbitmq από την ομάδα του openstack.

Στην παράγραφο 6.14.1.2.1.2.2. Το αρχικό έγγραφο δείχνει το αποτέλεσμα του RPC CAST:
Δομικά στοιχεία κατανεμημένων εφαρμογών. Δεύτερη προσέγγιση

Δεν θα κάνουμε καμία πρόσθετη ρύθμιση στον πυρήνα του λειτουργικού συστήματος ή στο erlang VM εκ των προτέρων. Προϋποθέσεις για τη δοκιμή:

  • erl επιλέγει: +A1 +sbtu.
  • Η δοκιμή σε έναν κόμβο erlang εκτελείται σε φορητό υπολογιστή με παλιό i7 σε έκδοση για κινητά.
  • Οι δοκιμές συμπλέγματος πραγματοποιούνται σε διακομιστές με δίκτυο 10G.
  • Ο κώδικας εκτελείται σε κοντέινερ docker. Δίκτυο σε λειτουργία NAT.

Κωδικός δοκιμής:

req_resp_bench(_) ->
  W = perftest:comprehensive(10000,
    fun() ->
      messaging:request(?EXCHANGE, default, ping, self()),
      receive
        #'$msg'{message = pong} -> ok
      after 5000 ->
        throw(timeout)
      end
    end
  ),
  true = lists:any(fun(E) -> E >= 30000 end, W),
  ok.

Σενάριο 1: Η δοκιμή εκτελείται σε φορητό υπολογιστή με παλιά έκδοση i7 για κινητά. Η δοκιμή, τα μηνύματα και η υπηρεσία εκτελούνται σε έναν κόμβο σε ένα κοντέινερ Docker:

Sequential 10000 cycles in ~0 seconds (26987 cycles/s)
Sequential 20000 cycles in ~1 seconds (26915 cycles/s)
Sequential 100000 cycles in ~4 seconds (26957 cycles/s)
Parallel 2 100000 cycles in ~2 seconds (44240 cycles/s)
Parallel 4 100000 cycles in ~2 seconds (53459 cycles/s)
Parallel 10 100000 cycles in ~2 seconds (52283 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (49317 cycles/s)

Σενάριο 2: 3 κόμβοι που λειτουργούν σε διαφορετικά μηχανήματα υπό docker (NAT).

Sequential 10000 cycles in ~1 seconds (8684 cycles/s)
Sequential 20000 cycles in ~2 seconds (8424 cycles/s)
Sequential 100000 cycles in ~12 seconds (8655 cycles/s)
Parallel 2 100000 cycles in ~7 seconds (15160 cycles/s)
Parallel 4 100000 cycles in ~5 seconds (19133 cycles/s)
Parallel 10 100000 cycles in ~4 seconds (24399 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (34517 cycles/s)

Σε όλες τις περιπτώσεις, η χρήση της CPU δεν ξεπέρασε το 250%

Αποτελέσματα της

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

Φωτογραφία @chuttersnap

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

Ποια θέματα πρέπει να καλύψω με περισσότερες λεπτομέρειες ως μέρος της σειράς VTrade Experiment;

  • Θεωρία: Αγορές, παραγγελίες και ο χρόνος τους: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Βιβλίο παραγγελιών. Θεωρία και πρακτική υλοποίησης βιβλίου με ομαδοποιήσεις

  • Οπτικοποίηση συναλλαγών: τσιμπούρια, μπάρες, αναλύσεις. Πώς να αποθηκεύσετε και πώς να κολλήσετε

  • Backoffice. Σχεδιασμός και ανάπτυξη. Παρακολούθηση εργαζομένων και διερεύνηση περιστατικών

  • API. Ας δούμε ποιες διεπαφές χρειάζονται και πώς να τις εφαρμόσουμε

  • Αποθήκευση πληροφοριών: PostgreSQL, Timescale, Tarantool σε συστήματα συναλλαγών

  • Αντιδραστικότητα στα συστήματα συναλλαγών

  • Αλλα. Θα γράψω στα σχόλια

Ψήφισαν 6 χρήστες. 4 χρήστες απείχαν.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο