Μεταγλωττιζόμενη διαμόρφωση ενός κατανεμημένου συστήματος

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

Μεταγλωττιζόμενη διαμόρφωση ενός κατανεμημένου συστήματος

(на русском)

Εισαγωγή

Η κατασκευή ισχυρών κατανεμημένων συστημάτων απαιτεί τη χρήση σωστής και συνεκτικής διαμόρφωσης σε όλους τους κόμβους. Μια τυπική λύση είναι η χρήση μιας περιγραφής ανάπτυξης κειμένου (terraform, ansible ή κάτι παρόμοιο) και τα αρχεία διαμόρφωσης που δημιουργούνται αυτόματα (συχνά — αφιερωμένα για κάθε κόμβο/ρόλο). Θα θέλαμε επίσης να χρησιμοποιήσουμε τα ίδια πρωτόκολλα των ίδιων εκδόσεων σε κάθε κόμβο επικοινωνίας (διαφορετικά θα αντιμετωπίζαμε προβλήματα ασυμβατότητας). Στον κόσμο του JVM αυτό σημαίνει ότι τουλάχιστον η βιβλιοθήκη μηνυμάτων θα πρέπει να είναι της ίδιας έκδοσης σε όλους τους κόμβους επικοινωνίας.

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

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

Η διαμόρφωση τείνει να εξελίσσεται μαζί με το λογισμικό. Συνήθως χρησιμοποιούμε εκδόσεις για να αναγνωρίσουμε διάφορες
στάδια εξέλιξης λογισμικού. Φαίνεται λογικό να καλύπτεται η διαμόρφωση υπό διαχείριση έκδοσης και να αναγνωρίζονται διαφορετικές διαμορφώσεις με ορισμένες ετικέτες. Εάν υπάρχει μόνο μία διαμόρφωση στην παραγωγή, ενδέχεται να χρησιμοποιήσουμε μία έκδοση ως αναγνωριστικό. Μερικές φορές μπορεί να έχουμε πολλά περιβάλλοντα παραγωγής. Και για κάθε περιβάλλον ίσως χρειαζόμαστε ξεχωριστό κλάδο διαμόρφωσης. Έτσι, οι διαμορφώσεις μπορεί να φέρουν ετικέτα με κλάδο και έκδοση για να αναγνωρίζουν μοναδικά διαφορετικές διαμορφώσεις. Κάθε ετικέτα διακλάδωσης και έκδοση αντιστοιχεί σε έναν ενιαίο συνδυασμό κατανεμημένων κόμβων, θυρών, εξωτερικών πόρων, εκδόσεων βιβλιοθήκης classpath σε κάθε κόμβο. Εδώ θα καλύψουμε μόνο τον μεμονωμένο κλάδο και θα αναγνωρίσουμε τις διαμορφώσεις με μια δεκαδική έκδοση τριών συστατικών (1.2.3), με τον ίδιο τρόπο όπως και άλλα τεχνουργήματα.

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

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

Μεταγλώττιση διαμόρφωσης

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

Ένα τυπικό κατανεμημένο σύστημα αποτελείται από μερικούς κόμβους. Οι κόμβοι μπορούν να αναγνωριστούν χρησιμοποιώντας κάποιους τύπους:

sealed trait NodeId
case object Backend extends NodeId
case object Frontend extends NodeId

ή απλά

case class NodeId(hostName: String)

ή ακόμη

object Singleton
type NodeId = Singleton.type

Αυτοί οι κόμβοι εκτελούν διάφορους ρόλους, εκτελούν ορισμένες υπηρεσίες και θα πρέπει να μπορούν να επικοινωνούν με τους άλλους κόμβους μέσω συνδέσεων TCP/HTTP.

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

case class TcpEndPoint[Protocol](node: NodeId, port: Port[Protocol])

όπου Port είναι απλώς ένα Int εντός του επιτρεπόμενου εύρους:

type PortNumber = Refined[Int, Closed[_0, W.`65535`.T]]

Εκλεπτυσμένοι τύποι

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

Για το HTTP (REST) ​​μπορεί επίσης να χρειαστούμε μια διαδρομή της υπηρεσίας:

type UrlPathPrefix = Refined[String, MatchesRegex[W.`"[a-zA-Z_0-9/]*"`.T]]
case class PortWithPrefix[Protocol](portNumber: PortNumber, pathPrefix: UrlPathPrefix)

Τύπος φάντασμα

Προκειμένου να προσδιορίσουμε το πρωτόκολλο κατά τη μεταγλώττιση, χρησιμοποιούμε τη δυνατότητα Scala για τη δήλωση του ορίσματος τύπου Protocol που δεν χρησιμοποιείται στην τάξη. Είναι ένα λεγόμενο τύπου φάντασμα. Κατά τον χρόνο εκτέλεσης σπάνια χρειαζόμαστε μια παρουσία του αναγνωριστικού πρωτοκόλλου, γι' αυτό δεν το αποθηκεύουμε. Κατά τη διάρκεια της σύνθεσης, αυτός ο τύπος φάντασμα παρέχει πρόσθετη ασφάλεια τύπου. Δεν μπορούμε να περάσουμε τη θύρα με εσφαλμένο πρωτόκολλο.

Ένα από τα πιο ευρέως χρησιμοποιούμενα πρωτόκολλα είναι το REST API με σειριοποίηση Json:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

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

Για τους σκοπούς αυτής της ανάρτησης θα χρησιμοποιήσουμε μια απλούστερη έκδοση του πρωτοκόλλου:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

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

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

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

  type EchoProtocol[A] = SimpleHttpGetRest[A, A]

  trait EchoConfig[A] extends ServiceConfig {
    def portNumber: PortNumber = 8081
    def echoPort: PortWithPrefix[EchoProtocol[A]] = PortWithPrefix[EchoProtocol[A]](portNumber, "echo")
    def echoService: HttpSimpleGetEndPoint[NodeId, EchoProtocol[A]] = providedSimpleService(echoPort)
  }

Η υπηρεσία Echo χρειάζεται μόνο ρυθμισμένη θύρα. Και δηλώνουμε ότι αυτή η θύρα υποστηρίζει πρωτόκολλο echo. Σημειώστε ότι δεν χρειάζεται να προσδιορίσουμε μια συγκεκριμένη θύρα αυτή τη στιγμή, επειδή το χαρακτηριστικό επιτρέπει δηλώσεις αφηρημένων μεθόδων. Εάν χρησιμοποιούμε αφηρημένες μεθόδους, ο μεταγλωττιστής θα απαιτήσει μια υλοποίηση σε μια παρουσία διαμόρφωσης. Εδώ παρέχουμε την υλοποίηση (8081) και θα χρησιμοποιηθεί ως η προεπιλεγμένη τιμή εάν την παραλείψουμε σε συγκεκριμένη διαμόρφωση.

Μπορούμε να δηλώσουμε μια εξάρτηση στη διαμόρφωση του προγράμματος-πελάτη της υπηρεσίας echo:

  trait EchoClientConfig[A] {
    def testMessage: String = "test"
    def pollInterval: FiniteDuration
    def echoServiceDependency: HttpSimpleGetEndPoint[_, EchoProtocol[A]]
  }

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

Υλοποίηση υπηρεσιών

Μια υπηρεσία χρειάζεται μια λειτουργία για να ξεκινήσει και να τερματιστεί με χάρη. (Η δυνατότητα τερματισμού μιας υπηρεσίας είναι κρίσιμη για τη δοκιμή.) Και πάλι υπάρχουν μερικές επιλογές για τον καθορισμό μιας τέτοιας λειτουργίας για μια δεδομένη διαμόρφωση (για παράδειγμα, θα μπορούσαμε να χρησιμοποιήσουμε κατηγορίες τύπου). Για αυτήν την ανάρτηση θα χρησιμοποιήσουμε ξανά το Cake Pattern. Μπορούμε να αντιπροσωπεύσουμε μια υπηρεσία χρησιμοποιώντας cats.Resource το οποίο ήδη παρέχει bracketing και απελευθέρωση πόρων. Για να αποκτήσουμε έναν πόρο θα πρέπει να παρέχουμε μια διαμόρφωση και κάποιο πλαίσιο χρόνου εκτέλεσης. Έτσι, η λειτουργία εκκίνησης υπηρεσίας μπορεί να μοιάζει με:

  type ResourceReader[F[_], Config, A] = Reader[Config, Resource[F, A]]

  trait ServiceImpl[F[_]] {
    type Config
    def resource(
      implicit
      resolver: AddressResolver[F],
      timer: Timer[F],
      contextShift: ContextShift[F],
      ec: ExecutionContext,
      applicative: Applicative[F]
    ): ResourceReader[F, Config, Unit]
  }

όπου

  • Config — τύπος διαμόρφωσης που απαιτείται από αυτόν τον εκκινητή υπηρεσίας
  • AddressResolver — ένα αντικείμενο χρόνου εκτέλεσης που έχει τη δυνατότητα να αποκτά πραγματικές διευθύνσεις άλλων κόμβων (συνεχίστε να διαβάζετε για λεπτομέρειες).

οι άλλοι τύποι προέρχονται από cats:

  • F[_] — τύπος εφέ (Στην απλούστερη περίπτωση F[A] θα μπορούσε να είναι απλά () => A. Σε αυτήν την ανάρτηση θα χρησιμοποιήσουμε cats.IO.)
  • Reader[A,B] — είναι λίγο πολύ συνώνυμο μιας συνάρτησης A => B
  • cats.Resource — έχει τρόπους απόκτησης και απελευθέρωσης
  • Timer — επιτρέπει τον ύπνο/μέτρηση του χρόνου
  • ContextShift - αναλογικό του ExecutionContext
  • Applicative — περιτύλιγμα συναρτήσεων σε ισχύ (σχεδόν μια μονάδα) (ενδεχομένως να την αντικαταστήσουμε με κάτι άλλο)

Χρησιμοποιώντας αυτή τη διεπαφή μπορούμε να υλοποιήσουμε μερικές υπηρεσίες. Για παράδειγμα, μια υπηρεσία που δεν κάνει τίποτα:

  trait ZeroServiceImpl[F[_]] extends ServiceImpl[F] {
    type Config <: Any
    def resource(...): ResourceReader[F, Config, Unit] =
      Reader(_ => Resource.pure[F, Unit](()))
  }

(Βλέπω Ο πηγαίος κώδικας για άλλες υλοποιήσεις υπηρεσιών — υπηρεσία ηχούς,
echo client και ελεγκτές διάρκειας ζωής.)

Ένας κόμβος είναι ένα μεμονωμένο αντικείμενο που εκτελεί μερικές υπηρεσίες (η εκκίνηση μιας αλυσίδας πόρων ενεργοποιείται από το Μοτίβο κέικ):

object SingleNodeImpl extends ZeroServiceImpl[IO]
  with EchoServiceService
  with EchoClientService
  with FiniteDurationLifecycleServiceImpl
{
  type Config = EchoConfig[String] with EchoClientConfig[String] with FiniteDurationLifecycleConfig
}

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

Ανάλυση διεύθυνσης κόμβου

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

case class NodeAddress[NodeId](host: Uri.Host)
trait AddressResolver[F[_]] {
  def resolve[NodeId](nodeId: NodeId): F[NodeAddress[NodeId]]
}

Υπάρχουν μερικοί πιθανοί τρόποι υλοποίησης μιας τέτοιας λειτουργίας.

  1. Εάν γνωρίζουμε τις πραγματικές διευθύνσεις πριν από την ανάπτυξη, κατά τη διάρκεια της εγκατάστασης των κεντρικών υπολογιστών κόμβου, τότε μπορούμε να δημιουργήσουμε κώδικα Scala με τις πραγματικές διευθύνσεις και να εκτελέσουμε το build στη συνέχεια (το οποίο εκτελεί ελέγχους χρόνου μεταγλώττισης και στη συνέχεια εκτελεί τη σουίτα δοκιμής ενοποίησης). Σε αυτή την περίπτωση η συνάρτηση χαρτογράφησης μας είναι γνωστή στατικά και μπορεί να απλοποιηθεί σε κάτι σαν α Map[NodeId, NodeAddress].
  2. Μερικές φορές λαμβάνουμε πραγματικές διευθύνσεις μόνο σε μεταγενέστερο σημείο, όταν ο κόμβος ξεκινά πραγματικά, ή δεν έχουμε διευθύνσεις κόμβων που δεν έχουν ξεκινήσει ακόμα. Σε αυτήν την περίπτωση μπορεί να έχουμε μια υπηρεσία εντοπισμού που ξεκινά πριν από όλους τους άλλους κόμβους και κάθε κόμβος μπορεί να διαφημίζει τη διεύθυνσή του σε αυτήν την υπηρεσία και να εγγραφεί σε εξαρτήσεις.
  3. Αν μπορούμε να τροποποιήσουμε /etc/hosts, μπορούμε να χρησιμοποιήσουμε προκαθορισμένα ονόματα κεντρικών υπολογιστών (όπως my-project-main-node και echo-backend) και απλώς συσχετίστε αυτό το όνομα με τη διεύθυνση IP κατά την ανάπτυξη.

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

Σε αυτήν την ανάρτηση θα εξετάσουμε δύο κατανεμημένες διατάξεις συστημάτων:

  1. Διάταξη ενός κόμβου, όπου όλες οι υπηρεσίες τοποθετούνται στον μεμονωμένο κόμβο.
  2. Διάταξη δύο κόμβων, όπου η υπηρεσία και ο πελάτης βρίσκονται σε διαφορετικούς κόμβους.

Η διαμόρφωση για ένα ενιαίος κόμβος η διάταξη έχει ως εξής:

Διαμόρφωση ενός κόμβου

object SingleNodeConfig extends EchoConfig[String] 
  with EchoClientConfig[String] with FiniteDurationLifecycleConfig
{
  case object Singleton // identifier of the single node 
  // configuration of server
  type NodeId = Singleton.type
  def nodeId = Singleton

  /** Type safe service port specification. */
  override def portNumber: PortNumber = 8088

  // configuration of client

  /** We'll use the service provided by the same host. */
  def echoServiceDependency = echoService

  override def testMessage: UrlPathElement = "hello"

  def pollInterval: FiniteDuration = 1.second

  // lifecycle controller configuration
  def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 requests, not 9.
}

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

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

Διαμόρφωση δύο κόμβων

  object NodeServerConfig extends EchoConfig[String] with SigTermLifecycleConfig
  {
    type NodeId = NodeIdImpl

    def nodeId = NodeServer

    override def portNumber: PortNumber = 8080
  }

  object NodeClientConfig extends EchoClientConfig[String] with FiniteDurationLifecycleConfig
  {
    // NB! dependency specification
    def echoServiceDependency = NodeServerConfig.echoService

    def pollInterval: FiniteDuration = 1.second

    def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 request, not 9.

    def testMessage: String = "dolly"
  }

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

Υλοποίηση δύο κόμβων

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

  object TwoJvmNodeServerImpl extends ZeroServiceImpl[IO] with EchoServiceService with SigIntLifecycleServiceImpl {
    type Config = EchoConfig[String] with SigTermLifecycleConfig
  }

  object TwoJvmNodeClientImpl extends ZeroServiceImpl[IO] with EchoClientService with FiniteDurationLifecycleServiceImpl {
    type Config = EchoClientConfig[String] with FiniteDurationLifecycleConfig
  }

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

Συνολική διαδικασία ανάπτυξης

Ας δούμε πώς αυτή η προσέγγιση αλλάζει τον τρόπο που εργαζόμαστε με τη διαμόρφωση.

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

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

Εισιτήριο -> PR -> αναθεώρηση -> συγχώνευση -> συνεχής ενοποίηση -> συνεχής ανάπτυξη

Υπάρχουν οι ακόλουθες συνέπειες της προσέγγισης:

  1. Η διαμόρφωση είναι συνεπής για την περίπτωση ενός συγκεκριμένου συστήματος. Φαίνεται ότι δεν υπάρχει τρόπος να υπάρχει λανθασμένη σύνδεση μεταξύ των κόμβων.
  2. Δεν είναι εύκολο να αλλάξετε τη διαμόρφωση μόνο σε έναν κόμβο. Φαίνεται παράλογο να συνδεθείτε και να αλλάξετε ορισμένα αρχεία κειμένου. Έτσι, η μετατόπιση της διαμόρφωσης γίνεται λιγότερο δυνατή.
  3. Δεν είναι εύκολο να γίνουν μικρές αλλαγές διαμόρφωσης.
  4. Οι περισσότερες από τις αλλαγές διαμόρφωσης θα ακολουθήσουν την ίδια διαδικασία ανάπτυξης και θα υποβληθούν σε έλεγχο.

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

Παραλλαγές

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

Πρώτα απ 'όλα, θα απαριθμήσουμε μερικές εναλλακτικές λύσεις για τις διάφορες πτυχές του προτεινόμενου τρόπου αντιμετώπισης της διαμόρφωσης:

  1. Αρχείο κειμένου στο μηχάνημα προορισμού.
  2. Κεντρική αποθήκευση κλειδιού-τιμής (όπως etcd/zookeeper).
  3. Στοιχεία υποδιεργασίας που θα μπορούσαν να ρυθμιστούν εκ νέου/ επανεκκινηθούν χωρίς διαδικασία επανεκκίνησης.
  4. Διαμόρφωση εκτός τεχνουργήματος και έλεγχος έκδοσης.

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

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

Η κεντρική αποθήκευση κλειδιού-τιμής είναι ένας καλός μηχανισμός για τη διανομή μετα-παραμέτρων εφαρμογής. Εδώ πρέπει να σκεφτούμε ποιες θεωρούμε ότι είναι τιμές διαμόρφωσης και τι είναι απλώς δεδομένα. Δίνεται μια λειτουργία C => A => B ονομάζουμε συνήθως σπάνια μεταβαλλόμενες τιμές C "διαμόρφωση", ενώ άλλαζαν συχνά δεδομένα A - απλά εισάγετε δεδομένα. Η διαμόρφωση πρέπει να παρέχεται στη συνάρτηση νωρίτερα από τα δεδομένα A. Δεδομένης αυτής της ιδέας μπορούμε να πούμε ότι η αναμενόμενη συχνότητα αλλαγών θα μπορούσε να χρησιμοποιηθεί για τη διάκριση των δεδομένων διαμόρφωσης από απλά δεδομένα. Επίσης, τα δεδομένα συνήθως προέρχονται από μία πηγή (χρήστης) και οι ρυθμίσεις παραμέτρων προέρχονται από διαφορετική πηγή (διαχειριστής). Η αντιμετώπιση παραμέτρων που μπορούν να αλλάξουν μετά τη διαδικασία αρχικοποίησης οδηγεί σε αύξηση της πολυπλοκότητας της εφαρμογής. Για τέτοιες παραμέτρους θα πρέπει να χειριστούμε τον μηχανισμό παράδοσης, ανάλυση και επικύρωση, χειρισμό εσφαλμένων τιμών. Ως εκ τούτου, για να μειώσουμε την πολυπλοκότητα του προγράμματος, θα ήταν καλύτερα να μειώσουμε τον αριθμό των παραμέτρων που μπορούν να αλλάξουν κατά το χρόνο εκτέλεσης (ή ακόμα και να τις εξαλείψουμε εντελώς).

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

Μια σημαντική πτυχή της χρήσης στατικής διαμόρφωσης που μερικές φορές κάνει τους ανθρώπους να εξετάζουν τη δυναμική διαμόρφωση (χωρίς άλλους λόγους) είναι ο χρόνος διακοπής της υπηρεσίας κατά την ενημέρωση της διαμόρφωσης. Πράγματι, εάν πρέπει να κάνουμε αλλαγές στη στατική διαμόρφωση, πρέπει να επανεκκινήσουμε το σύστημα έτσι ώστε οι νέες τιμές να γίνουν αποτελεσματικές. Οι απαιτήσεις για το χρόνο διακοπής διαφέρουν για διαφορετικά συστήματα, επομένως μπορεί να μην είναι τόσο κρίσιμο. Εάν είναι κρίσιμο, τότε πρέπει να προγραμματίσουμε εκ των προτέρων για τυχόν επανεκκίνηση του συστήματος. Για παράδειγμα, θα μπορούσαμε να εφαρμόσουμε Αποστράγγιση σύνδεσης AWS ELB. Σε αυτό το σενάριο, όποτε χρειάζεται να επανεκκινήσουμε το σύστημα, ξεκινάμε παράλληλα μια νέα παρουσία του συστήματος, μετά αλλάζουμε το ELB σε αυτό, ενώ αφήνουμε το παλιό σύστημα να ολοκληρώσει την εξυπηρέτηση των υπαρχουσών συνδέσεων.

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

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

Πλεονεκτήματα μειονεκτήματα

Εδώ θα θέλαμε να επισημάνουμε ορισμένα πλεονεκτήματα και να συζητήσουμε ορισμένα μειονεκτήματα της προτεινόμενης προσέγγισης.

Πλεονεκτήματα

Χαρακτηριστικά της μεταγλωττιζόμενης διαμόρφωσης ενός πλήρους κατανεμημένου συστήματος:

  1. Στατικός έλεγχος διαμόρφωσης. Αυτό δίνει ένα υψηλό επίπεδο εμπιστοσύνης, ότι η διαμόρφωση είναι σωστή, δεδομένων των περιορισμών τύπου.
  2. Πλούσια γλώσσα διαμόρφωσης. Τυπικά, άλλες προσεγγίσεις διαμόρφωσης περιορίζονται το πολύ σε αντικατάσταση μεταβλητών.
    Χρησιμοποιώντας το Scala μπορεί κανείς να χρησιμοποιήσει ένα ευρύ φάσμα γλωσσικών χαρακτηριστικών για να βελτιώσει τη διαμόρφωση. Για παράδειγμα, μπορούμε να χρησιμοποιήσουμε χαρακτηριστικά για να παρέχουμε προεπιλεγμένες τιμές, αντικείμενα για να ορίσουμε διαφορετικό εύρος, μπορούμε να αναφερθούμε σε vals ορίζεται μόνο μία φορά στο εξωτερικό πεδίο (DRY). Είναι δυνατό να χρησιμοποιηθούν κυριολεκτικές ακολουθίες ή περιπτώσεις συγκεκριμένων κλάσεων (Seq, Map, Κ.λπ.).
  3. DSL. Η Scala έχει αξιοπρεπή υποστήριξη για εγγραφές DSL. Κάποιος μπορεί να χρησιμοποιήσει αυτές τις δυνατότητες για να δημιουργήσει μια γλώσσα διαμόρφωσης που είναι πιο βολική και φιλική προς τον τελικό χρήστη, έτσι ώστε η τελική διαμόρφωση να είναι τουλάχιστον ευανάγνωστη από τους χρήστες του τομέα.
  4. Ακεραιότητα και συνοχή μεταξύ των κόμβων. Ένα από τα πλεονεκτήματα της διαμόρφωσης για ολόκληρο το κατανεμημένο σύστημα σε ένα μέρος είναι ότι όλες οι τιμές ορίζονται αυστηρά μία φορά και στη συνέχεια επαναχρησιμοποιούνται σε όλα τα μέρη όπου τις χρειαζόμαστε. Επίσης, πληκτρολογήστε δηλώσεις ασφαλούς θύρας βεβαιωθείτε ότι σε όλες τις πιθανές σωστές διαμορφώσεις οι κόμβοι του συστήματος θα μιλούν την ίδια γλώσσα. Υπάρχουν σαφείς εξαρτήσεις μεταξύ των κόμβων που καθιστά δύσκολο να ξεχάσουμε την παροχή ορισμένων υπηρεσιών.
  5. Υψηλή ποιότητα αλλαγών. Η συνολική προσέγγιση της αλλαγής της διαμόρφωσης παραμέτρων μέσω της κανονικής διαδικασίας PR καθιερώνει υψηλά πρότυπα ποιότητας και στη διαμόρφωση.
  6. Ταυτόχρονες αλλαγές διαμόρφωσης. Κάθε φορά που κάνουμε οποιεσδήποτε αλλαγές στη διαμόρφωση, η αυτόματη ανάπτυξη διασφαλίζει ότι όλοι οι κόμβοι ενημερώνονται.
  7. Απλοποίηση εφαρμογής. Η εφαρμογή δεν χρειάζεται να αναλύει και να επικυρώνει τη διαμόρφωση και να χειρίζεται εσφαλμένες τιμές διαμόρφωσης. Αυτό απλοποιεί τη συνολική εφαρμογή. (Κάποια αύξηση της πολυπλοκότητας οφείλεται στην ίδια τη διαμόρφωση, αλλά είναι μια συνειδητή αντιστάθμιση προς την ασφάλεια.) Είναι πολύ απλό να επιστρέψετε στη συνηθισμένη διαμόρφωση — απλώς προσθέστε τα κομμάτια που λείπουν. Είναι πιο εύκολο να ξεκινήσετε με τη μεταγλωττισμένη διαμόρφωση και να αναβάλλετε την υλοποίηση πρόσθετων κομματιών σε κάποιες μεταγενέστερες στιγμές.
  8. Διαμόρφωση με έκδοση. Λόγω του γεγονότος ότι οι αλλαγές στη διαμόρφωση ακολουθούν την ίδια διαδικασία ανάπτυξης, με αποτέλεσμα να έχουμε ένα τεχνούργημα με μοναδική έκδοση. Μας επιτρέπει να αλλάξουμε τη διαμόρφωση εάν χρειάζεται. Μπορούμε ακόμη και να αναπτύξουμε μια διαμόρφωση που χρησιμοποιήθηκε πριν από ένα χρόνο και θα λειτουργεί ακριβώς με τον ίδιο τρόπο. Η σταθερή διαμόρφωση βελτιώνει την προβλεψιμότητα και την αξιοπιστία του κατανεμημένου συστήματος. Η διαμόρφωση είναι σταθερή κατά το χρόνο μεταγλώττισης και δεν μπορεί εύκολα να παραβιαστεί σε ένα σύστημα παραγωγής.
  9. Αρθρωτότητα. Το προτεινόμενο πλαίσιο είναι αρθρωτό και οι ενότητες θα μπορούσαν να συνδυαστούν με διάφορους τρόπους
    υποστηρίζει διαφορετικές διαμορφώσεις (ρυθμίσεις/διατάξεις). Συγκεκριμένα, είναι δυνατό να έχετε μια μικρής κλίμακας διάταξη ενός κόμβου και μια μεγάλης κλίμακας ρύθμιση πολλαπλών κόμβων. Είναι λογικό να υπάρχουν πολλαπλές διατάξεις παραγωγής.
  10. Δοκιμές. Για δοκιμαστικούς σκοπούς, θα μπορούσε κανείς να εφαρμόσει μια εικονική υπηρεσία και να τη χρησιμοποιήσει ως εξάρτηση με ασφαλή τρόπο. Θα μπορούσαν να διατηρηθούν ταυτόχρονα μερικές διαφορετικές διατάξεις δοκιμών με διάφορα μέρη που αντικαθίστανται από μακέτες.
  11. Δοκιμή ενσωμάτωσης. Μερικές φορές σε κατανεμημένα συστήματα είναι δύσκολο να εκτελεστούν δοκιμές ενοποίησης. Χρησιμοποιώντας την περιγραφόμενη προσέγγιση για να πληκτρολογήσουμε την ασφαλή διαμόρφωση του πλήρους κατανεμημένου συστήματος, μπορούμε να εκτελέσουμε όλα τα κατανεμημένα μέρη σε έναν μόνο διακομιστή με ελεγχόμενο τρόπο. Είναι εύκολο να μιμηθείς την κατάσταση
    όταν μια από τις υπηρεσίες δεν είναι διαθέσιμη.

Μειονεκτήματα

Η προσέγγιση μεταγλωττισμένης διαμόρφωσης είναι διαφορετική από την «κανονική» διαμόρφωση και μπορεί να μην ταιριάζει σε όλες τις ανάγκες. Εδώ είναι μερικά από τα μειονεκτήματα της μεταγλωττισμένης διαμόρφωσης:

  1. Στατική διαμόρφωση. Μπορεί να μην είναι κατάλληλο για όλες τις εφαρμογές. Σε ορισμένες περιπτώσεις υπάρχει ανάγκη γρήγορης επιδιόρθωσης της διαμόρφωσης στην παραγωγή παρακάμπτοντας όλα τα μέτρα ασφαλείας. Αυτή η προσέγγιση το κάνει πιο δύσκολο. Η μεταγλώττιση και η αναδιάταξη απαιτούνται μετά την πραγματοποίηση οποιασδήποτε αλλαγής στη διαμόρφωση. Αυτό είναι και το χαρακτηριστικό και το βάρος.
  2. Δημιουργία διαμόρφωσης. Όταν οι ρυθμίσεις παραμέτρων δημιουργούνται από κάποιο εργαλείο αυτοματισμού, αυτή η προσέγγιση απαιτεί μετέπειτα μεταγλώττιση (η οποία με τη σειρά της μπορεί να αποτύχει). Ίσως χρειαστεί πρόσθετη προσπάθεια για την ενσωμάτωση αυτού του πρόσθετου βήματος στο σύστημα κατασκευής.
  3. Οργανα. Υπάρχουν πολλά εργαλεία που χρησιμοποιούνται σήμερα που βασίζονται σε ρυθμίσεις που βασίζονται σε κείμενο. Μερικοί από αυτούς
    δεν θα ισχύει κατά τη μεταγλώττιση της διαμόρφωσης.
  4. Χρειάζεται αλλαγή νοοτροπίας. Οι προγραμματιστές και οι DevOps είναι εξοικειωμένοι με τα αρχεία διαμόρφωσης κειμένου. Η ιδέα της μεταγλώττισης της διαμόρφωσης μπορεί να τους φαίνεται περίεργη.
  5. Πριν από την εισαγωγή μεταγλωττιζόμενης διαμόρφωσης απαιτείται μια διαδικασία ανάπτυξης λογισμικού υψηλής ποιότητας.

Υπάρχουν ορισμένοι περιορισμοί του παραδείγματος που εφαρμόστηκε:

  1. Εάν παρέχουμε επιπλέον ρυθμίσεις που δεν απαιτούνται από την υλοποίηση του κόμβου, ο μεταγλωττιστής δεν θα μας βοηθήσει να εντοπίσουμε την εφαρμογή που απουσιάζει. Αυτό θα μπορούσε να αντιμετωπιστεί χρησιμοποιώντας HList ή ADT (κλάσεις περίπτωσης) για διαμόρφωση κόμβου αντί για χαρακτηριστικά και το μοτίβο κέικ.
  2. Πρέπει να παρέχουμε λίγο boilerplate στο αρχείο διαμόρφωσης: (package, import, object δηλώσεις·
    override def's για παραμέτρους που έχουν προεπιλεγμένες τιμές). Αυτό μπορεί να αντιμετωπιστεί εν μέρει χρησιμοποιώντας ένα DSL.
  3. Σε αυτήν την ανάρτηση δεν καλύπτουμε τη δυναμική αναδιαμόρφωση συμπλεγμάτων παρόμοιων κόμβων.

Συμπέρασμα

Σε αυτήν την ανάρτηση έχουμε συζητήσει την ιδέα της αναπαράστασης της διαμόρφωσης απευθείας στον πηγαίο κώδικα με έναν ασφαλή τρόπο. Η προσέγγιση θα μπορούσε να χρησιμοποιηθεί σε πολλές εφαρμογές ως αντικατάσταση των παραμέτρων xml και άλλων παραμέτρων που βασίζονται σε κείμενο. Παρά το γεγονός ότι το παράδειγμά μας έχει εφαρμοστεί στο Scala, θα μπορούσε επίσης να μεταφραστεί σε άλλες μεταγλωττιζόμενες γλώσσες (όπως Kotlin, C#, Swift, κ.λπ.). Θα μπορούσε κανείς να δοκιμάσει αυτήν την προσέγγιση σε ένα νέο έργο και, σε περίπτωση που δεν ταιριάζει καλά, να μεταβεί στον παλιομοδίτικο τρόπο.

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

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

  1. Κάποιος θα μπορούσε να χρησιμοποιήσει μακροεντολές για να εκτελέσει επικύρωση διαμόρφωσης και να αποτύχει κατά το χρόνο μεταγλώττισης σε περίπτωση αποτυχίας οποιωνδήποτε περιορισμών επιχειρησιακής λογικής.
  2. Ένα DSL θα μπορούσε να εφαρμοστεί για να αναπαραστήσει τη διαμόρφωση με τρόπο φιλικό προς τον χρήστη.
  3. Δυναμική διαχείριση πόρων με αυτόματες ρυθμίσεις διαμόρφωσης. Για παράδειγμα, όταν προσαρμόζουμε τον αριθμό των κόμβων συμπλέγματος μπορεί να θέλουμε (1) οι κόμβοι να έχουν ελαφρώς τροποποιημένη διαμόρφωση. (2) διαχειριστής συμπλέγματος για λήψη πληροφοριών νέων κόμβων.

Ευχαριστώ

Θα ήθελα να πω ευχαριστώ στους Andrey Saksonov, Pavel Popov, Anton Nehaev για την εμπνευσμένη ανατροφοδότηση σχετικά με το προσχέδιο αυτής της ανάρτησης που με βοήθησε να το κάνω πιο σαφές.

Πηγή: www.habr.com