Το λογισμικό σπάνια, αν ποτέ, έχει κενό πληροφοριών. Τουλάχιστον αυτή είναι η υπόθεση που μπορούν να κάνουν οι μηχανικοί λογισμικού για τις περισσότερες από τις εφαρμογές που αναπτύσσουμε.
Σε οποιαδήποτε κλίμακα, κάθε κομμάτι λογισμικού - με τη μία ή την άλλη μορφή - επικοινωνεί με άλλο λογισμικό για διάφορους λόγους: για τη λήψη δεδομένων αναφοράς από κάπου, για την αποστολή σημάτων παρακολούθησης, για να παραμείνει σε επαφή με άλλες υπηρεσίες, ενώ αποτελεί μέρος ενός κατανεμημένου συστήματος κι αλλα.
Σε αυτό το σεμινάριο, θα μάθετε ποιες είναι οι μεγαλύτερες προκλήσεις της ενσωμάτωσης μεγάλου λογισμικού και πώς η Apache Camel τα επιλύει εύκολα.
Ενδέχεται να έχετε κάνει τα ακόλουθα τουλάχιστον μία φορά στη ζωή του λογισμικού:
Γιατί είναι μια κακή πορεία δράσης;
Ενώ έχετε μόνο μερικές τέτοιες συνδέσεις, εξακολουθεί να είναι διαχειρίσιμη. Με έναν αυξανόμενο αριθμό σχέσεων μεταξύ συστημάτων, η επιχειρησιακή λογική της εφαρμογής συνδυάζεται με τη λογική ενοποίησης που προσπαθεί να προσαρμόσει δεδομένα, να αντισταθμίσει τις τεχνολογικές διαφορές μεταξύ δύο συστημάτων και να μεταφέρει δεδομένα στο εξωτερικό σύστημα με SOAP, REST ή περισσότερα εξωτικά αιτήματα.
Εάν ενσωματώνατε πολλές εφαρμογές, θα ήταν εξαιρετικά δύσκολο να ξαναγράψετε την πλήρη εικόνα των εξαρτήσεων σε έναν τέτοιο κώδικα: Πού παράγονται τα δεδομένα και ποιες υπηρεσίες τα καταναλώνουν; Θα έχετε πολλά μέρη όπου η λογική ενοποίησης αντιγράφεται για εκκίνηση.
Με αυτήν την προσέγγιση, παρόλο που η εργασία έχει γίνει τεχνικά, καταλήγουμε σε μεγάλα προβλήματα με τη συντηρησιμότητα και την επεκτασιμότητα της ολοκλήρωσης. Η ταχεία αναδιοργάνωση των ροών δεδομένων σε αυτό το σύστημα είναι σχεδόν αδύνατη, για να μην αναφέρουμε βαθύτερα ζητήματα όπως έλλειψη παρακολούθησης, διακοπή κυκλώματος, επίπονη ανάκτηση δεδομένων κ.λπ.
Αυτό είναι ιδιαίτερα σημαντικό κατά την ενσωμάτωση λογισμικού στο πεδίο μιας πολύ μεγάλης εταιρείας. Η αντιμετώπιση της ενοποίησης των επιχειρήσεων σημαίνει συνεργασία με ένα σύνολο εφαρμογών που λειτουργούν σε ένα ευρύ φάσμα πλατφορμών και βρίσκονται σε διαφορετικές τοποθεσίες. Η ανταλλαγή δεδομένων σε ένα τέτοιο περιβάλλον λογισμικού είναι αρκετά απαιτητική. Πρέπει να πληροί τα υψηλά πρότυπα ασφάλειας της βιομηχανίας και να παρέχει έναν αξιόπιστο τρόπο μεταφοράς δεδομένων. Σε ένα επιχειρηματικό περιβάλλον, η ολοκλήρωση συστημάτων απαιτεί ξεχωριστό και πλήρως περίτεχνο σχεδιασμό αρχιτεκτονικής.
Αυτό το άρθρο θα σας παρουσιάσει τις μοναδικές δυσκολίες που αντιμετωπίζει η ενσωμάτωση λογισμικού και παρέχει κάποιες λύσεις βασισμένες στην εμπειρία για εργασίες ολοκλήρωσης. Θα εξοικειωθούμε Καμήλα Apache , ένα χρήσιμο πλαίσιο που μπορεί να ανακουφίσει τις χειρότερες πτυχές του πονοκέφαλου ενός προγραμματιστή ολοκλήρωσης. Θα συνεχίσουμε με ένα παράδειγμα του τρόπου με τον οποίο η Καμήλα μπορεί να βοηθήσει στην καθιέρωση επικοινωνίας σε ένα σύμπλεγμα μικροϋπηρεσιών που τροφοδοτούνται από την Kubernetes.
Μια ευρέως χρησιμοποιούμενη προσέγγιση για την επίλυση του προβλήματος είναι η αποσύνδεση ενός επιπέδου ολοκλήρωσης στην εφαρμογή σας. Μπορεί να υπάρχει μέσα στην ίδια την εφαρμογή ή ως ειδικό λογισμικό που λειτουργεί ανεξάρτητα - στην τελευταία περίπτωση ονομάζεται μεσαίο λογισμικό .
Ποια προβλήματα αντιμετωπίζετε συνήθως κατά την ανάπτυξη και την υποστήριξη μεσαίο λογισμικό ; Γενικά, έχει τα ακόλουθα βασικά στοιχεία:
τι είναι υπεύθυνος CFO
Όλα τα κανάλια δεδομένων είναι αναξιόπιστα σε κάποιο βαθμό. Προβλήματα που προκύπτουν από αυτήν την αξιοπιστία ενδέχεται να μην προκύψουν εφόσον η ένταση των δεδομένων είναι χαμηλή ή μέτρια. Κάθε επίπεδο αποθήκευσης από τη μνήμη της εφαρμογής έως τις χαμηλότερες προσωρινές μνήμες και τον παρακάτω εξοπλισμό υπόκειται σε πιθανή αστοχία. Ορισμένα σπάνια σφάλματα προκύπτουν μόνο με μεγάλους όγκους δεδομένων. Ακόμη και ώριμα προϊόντα από προμηθευτές έτοιμους για παραγωγή έχουν ανεπίλυτα προβλήματα παρακολούθησης σφαλμάτων που σχετίζονται με την απώλεια δεδομένων. Ένα σύστημα μεσαίο λογισμικό Θα πρέπει να μπορεί να σας ενημερώνει σχετικά με αυτά τα θύματα δεδομένων και να προωθεί το μήνυμα παροχής εγκαίρως.
Οι εφαρμογές χρησιμοποιούν διαφορετικά πρωτόκολλα και μορφές δεδομένων. Αυτό σημαίνει ότι ένα σύστημα ολοκλήρωσης είναι μια κουρτίνα για μετασχηματισμούς δεδομένων και προσαρμογείς για άλλους συμμετέχοντες και χρησιμοποιεί μια ποικιλία τεχνολογιών. Αυτές μπορεί να περιλαμβάνουν απλές κλήσεις REST API, αλλά θα μπορούσαν επίσης να έχουν πρόσβαση σε έναν μεσίτη ουράς, να στείλουν εντολές CSV μέσω FTP ή να συγκεντρώσουν δεδομένα σε έναν πίνακα βάσης δεδομένων. Αυτή είναι μια μεγάλη λίστα και δεν θα συντομευτεί ποτέ.
Οι αλλαγές στις μορφές δεδομένων και τους κανόνες δρομολόγησης είναι αναπόφευκτες. Κάθε βήμα σε μια διαδικασία ανάπτυξης εφαρμογών, η οποία αλλάζει τη δομή των δεδομένων, οδηγεί γενικά σε αλλαγές στις μορφές και τους μετασχηματισμούς δεδομένων ολοκλήρωσης. Μερικές φορές απαιτούνται αλλαγές υποδομής με αναδιοργανωμένες ροές δεδομένων εταιρειών. Για παράδειγμα, αυτές οι αλλαγές μπορούν να προκύψουν όταν εισαχθεί ένα μόνο σημείο επικύρωσης δεδομένων βάσης που πρέπει να επεξεργαστεί όλες τις καταχωρήσεις κύριων δεδομένων σε ολόκληρη την επιχείρηση. Με συστήματα N
, μπορούμε να καταλήξουμε σε μέγιστες σχεδόν συνδέσεις N^2
μεταξύ τους, οπότε ο αριθμός των θέσεων όπου πρέπει να εφαρμοστούν αλλαγές αυξάνεται αρκετά γρήγορα. Θα είναι σαν χιονοστιβάδα. Για να διατηρηθεί η συντηρησιμότητα, ένα στρώμα από μεσαίο λογισμικό Πρέπει να παρέχετε μια σαφή εικόνα των εξαρτήσεων με ευέλικτη δρομολόγηση και μετασχηματισμό δεδομένων.
Αυτές οι ιδέες πρέπει να ληφθούν υπόψη κατά το σχεδιασμό της ολοκλήρωσης και την επιλογή της λύσης για μεσαίο λογισμικό πιο κατάλληλο. Ένας από τους πιθανούς τρόπους για να το χειριστείτε είναι να εκμεταλλευτείτε ένα λεωφορείο εξυπηρέτησης επιχειρήσεων ( ESB ). Αλλά το ESB παρέχονται από μεγάλους προμηθευτές είναι συνήθως πολύ βαρύ και συχνά πολύ ενοχλητικό - είναι σχεδόν αδύνατο να ξεκινήσετε γρήγορα με ένα ESB , έχει μια αρκετά απότομη καμπύλη μάθησης και η ευελιξία της θυσιάζεται σε μια μεγάλη λίστα με ενσωματωμένα χαρακτηριστικά και εργαλεία. Κατά τη γνώμη μου, οι ελαφριές λύσεις ενσωμάτωσης ανοιχτού κώδικα είναι πολύ ανώτερες: είναι πιο ελαστικές, εύκολες στην ανάπτυξη στο cloud και εύκολες στην κλίμακα.
Η ενοποίηση λογισμικού δεν είναι εύκολο να γίνει. Σήμερα, καθώς χτίζουμε αρχιτεκτονικές μικροϋπηρεσιών και αντιμετωπίζουμε πλήθος μικρών υπηρεσιών, έχουμε επίσης μεγάλες προσδοκίες για το πόσο αποτελεσματικά θα πρέπει να επικοινωνούν.
Όπως θα περίμενε κανείς, όπως η ανάπτυξη λογισμικού εν γένει, ο μετασχηματισμός δεδομένων και η ανάπτυξη δρομολόγησης περιλαμβάνουν επαναλαμβανόμενες λειτουργίες. Η εμπειρία σε αυτόν τον τομέα συνοψίστηκε και συστηματοποιήθηκε από επαγγελματίες που ασχολούνται με προβλήματα ένταξης για αρκετό καιρό. Στην έξοδο, υπάρχει ένα σύνολο εξαγόμενων προτύπων που ονομάζονται πρότυπα ενοποίησης επιχειρήσεων χρησιμοποιείται για το σχεδιασμό ροών δεδομένων. Αυτές οι μέθοδοι ενσωμάτωσης περιγράφηκαν στο βιβλίο με το ίδιο όνομα από τους Gregor Hophe και Bobby Wolfe, το οποίο μοιάζει πολύ με το σημαντικό βιβλίο του Gang of Four, αλλά στον τομέα του λογισμικού κόλλησης.
Για να δώσω ένα παράδειγμα, το μοτίβο κανονικοποίησης εισάγει ένα στοιχείο που χαρτογραφεί σημασιολογικά τα ίδια μηνύματα που έχουν διαφορετικές μορφές δεδομένων σε ένα μονό κανονικό μοντέλο ή ο συσσωρευτής είναι ένα EIP που συνδυάζει μια ακολουθία μηνυμάτων σε ένα.
Δεδομένου ότι είναι τεχνολογικά ανεξάρτητες αφαιρέσεις που χρησιμοποιούνται για την επίλυση αρχιτεκτονικών προβλημάτων, τα EIP συμβάλλουν στη σύνταξη ενός αρχιτεκτονικού σχεδιασμού που δεν πηγαίνει βαθιά στο επίπεδο του κώδικα αλλά μάλλον περιγράφει τις ροές δεδομένων με επαρκείς λεπτομέρειες. Μια τέτοια σημείωση για την περιγραφή των διαδρομών ολοκλήρωσης όχι μόνο καθιστά το σχέδιο συνοπτικό, αλλά επίσης καθιερώνει μια κοινή ονοματολογία και μια κοινή γλώσσα, οι οποίες είναι πολύ σημαντικές στο πλαίσιο της επίλυσης μιας εργασίας ολοκλήρωσης με μέλη της ομάδας από διάφορες εμπορικές περιοχές
Πριν από μερικά χρόνια, έφτιαχνα μια εταιρική ενοποίηση σε ένα μεγάλο δίκτυο λιανικής πώλησης παντοπωλείων με καταστήματα σε ευρεία διανομή. Ξεκίνησα με μια ιδιόκτητη λύση του ESB η οποία αποδείχθηκε πολύ δυσκίνητη για να διατηρηθεί. Έτσι, η ομάδα μας συναντήθηκε με την Apache Camel και αφού κάναμε μια «απόδειξη της ιδέας», ξαναγράψαμε γρήγορα όλες τις ροές δεδομένων μας σε διαδρομές Camel.
ένα πλαίσιο διαδικασίας που χρησιμοποιείται για τη διαχείριση εργασιών σε σύνθετα προϊόντα σε πολύπλοκα περιβάλλοντα είναι γνωστό ως
Καμήλα Apache μπορεί να περιγραφεί ως 'δρομολογητής διαμεσολάβησης', ένα πλαίσιο του μεσαίο λογισμικό προσανατολισμένη στα μηνύματα που εφαρμόζει τη λίστα EIP , με τα οποία εξοικειώθηκα. Χρησιμοποιεί αυτά τα μοτίβα, υποστηρίζει όλα τα κοινά πρωτόκολλα μεταφοράς και περιλαμβάνει ένα πλούσιο σύνολο χρήσιμων προσαρμογέων. Το Camel επιτρέπει τον χειρισμό μιας σειράς ρουτίνων ολοκλήρωσης χωρίς να χρειάζεται να γράψετε τον δικό σας κωδικό.
Εκτός από αυτό, θα ήθελα να επισημάνω τα ακόλουθα χαρακτηριστικά Apache Camel:
Οι διαδρομές Apache Camel μπορούν να γραφτούν σε Java ή Scala DSL. (Μια διαμόρφωση XML είναι επίσης διαθέσιμη, αλλά γίνεται πολύ λεκτική και έχει χειρότερες δυνατότητες εντοπισμού σφαλμάτων.) Δεν επιβάλλει περιορισμούς στη στοίβα τεχνολογίας των υπηρεσιών επικοινωνίας, αλλά εάν γράφετε σε Java ή Scala, μπορείτε να ενσωματώσετε την Καμήλα σε μια εφαρμογή αντί να την εκτελέσετε ανεξάρτητα.
Η σημείωση δρομολόγησης που χρησιμοποιείται από το Camel μπορεί να περιγραφεί με τον ακόλουθο απλό ψευδοκώδικα:
from(Source) .transform(Transformer) .to(Destination)
Τα Fuente
, Transformador
, και Destino
είναι τα τελικά σημεία που αναφέρονται στα στοιχεία εφαρμογής από τα URI τους.
Τι επιτρέπει στην Camel να λύσει τα προβλήματα ενσωμάτωσης που περιέγραψα προηγουμένως; Ας ΡΙΞΟΥΜΕ μια ΜΑΤΙΑ. Πρώτα απ 'όλα, η λογική δρομολόγησης και μετασχηματισμού βρίσκεται τώρα μόνο σε μια ειδική διαμόρφωση Apache Camel. Δεύτερον, μέσω της συνοπτικής και φυσικής DSL μαζί με τη χρήση του EIP, εμφανίζεται μια εικόνα των εξαρτήσεων μεταξύ των συστημάτων. Είναι κατασκευασμένο από κατανοητές αφαιρέσεις και η λογική δρομολόγησης είναι εύκολα ρυθμιζόμενη. Και τέλος, δεν χρειάζεται να γράψουμε πολλούς κώδικες μετασχηματισμού επειδή πιθανώς ήδη περιλαμβάνονται οι κατάλληλοι προσαρμογείς.
Πρέπει να προσθέσω ότι το Apache Camel είναι ένα ώριμο πλαίσιο και λαμβάνει τακτικές ενημερώσεις. Έχει μια μεγάλη κοινότητα και μια σημαντική συσσωρευμένη βάση γνώσεων.
Έχει τα μειονεκτήματά του. Η καμήλα δεν πρέπει να θεωρηθεί ως σύνθετη σουίτα ενσωμάτωσης. Είναι μια εργαλειοθήκη χωρίς χαρακτηριστικά υψηλού επιπέδου, όπως εργαλεία διαχείρισης επιχειρηματικών διαδικασιών ή οθόνες δραστηριότητας, αλλά μπορεί να χρησιμοποιηθεί για τη δημιουργία τέτοιου λογισμικού.
Εναλλακτικά συστήματα μπορεί να είναι, για παράδειγμα, Spring Integration ή Mule ESB . Για το Spring Integration, αν και θεωρείται ελαφρύ, κατά την εμπειρία μου, το να τα συνδυάζω και να γράφω πολλά αρχεία διαμόρφωσης XML μπορεί απροσδόκητα να είναι περίπλοκο και όχι εύκολη διέξοδος. Μουλάρι ESB Είναι ένα στιβαρό και εξαιρετικά λειτουργικό σύνολο εργαλείων, αλλά όπως υποδηλώνει το όνομα, είναι ένα επιχειρησιακό λεωφορείο εξυπηρέτησης, οπότε ανήκει σε μια διαφορετική κατηγορία βάρους. Το μουλάρι μπορεί να συγκριθεί με το Fuse ESB , ένα παρόμοιο προϊόν βασισμένο στο Apache Camel με ένα πλούσιο σύνολο χαρακτηριστικών. Για μένα, η χρήση του Apache Camel για επικόλληση υπηρεσιών σήμερα είναι προφανές έργο. Είναι εύκολο στη χρήση και παράγει μια σαφή περιγραφή του τι συμβαίνει, την ίδια στιγμή, είναι αρκετά λειτουργικό για τη δημιουργία σύνθετων ενοποιήσεων.
Ας αρχίσουμε να γράφουμε τον κωδικό. Θα ξεκινήσουμε με μια σύγχρονη ροή δεδομένων που δρομολογεί μηνύματα από μία πηγή σε μια λίστα παραληπτών. Οι κανόνες δρομολόγησης θα γραφτούν σε Java DSL .
Θα το χρησιμοποιησουμε Μέβεν για την κατασκευή του έργου. Προσθέστε πρώτα την ακόλουθη εξάρτηση στο pom.xml
:
... org.apache.camel camel-core 2.20.0
Εναλλακτικά, η εφαρμογή μπορεί να δημιουργηθεί πάνω από τον αρχέτυπο του camel-archetype-java
Οι ορισμοί της διαδρομής της καμήλας δηλώνονται στη μέθοδο RouteBuilder.configure
.
public void configure() { errorHandler(defaultErrorHandler().maximumRedeliveries(0)); from('file:orders?noop=true').routeId('main') .log('Incoming File: ${file:onlyname}') .unmarshal().json(JsonLibrary.Jackson, Order.class) // unmarshal JSON to Order class containing List .split().simple('body.items') // split list to process one by one .to('log:inputOrderItem') .choice() .when().simple('${body.type} == 'Drink'') .to('direct:bar') .when().simple('${body.type} == 'Dessert'') .to('direct:dessertStation') .when().simple('${body.type} == 'Hot Meal'') .to('direct:hotMealStation') .when().simple('${body.type} == 'Cold Meal'') .to('direct:coldMealStation') .otherwise() .to('direct:others'); from('direct:bar').routeId('bar').log('Handling Drink'); from('direct:dessertStation').routeId('dessertStation').log('Handling Dessert'); from('direct:hotMealStation').routeId('hotMealStation').log('Handling Hot Meal'); from('direct:coldMealStation').routeId('coldMealStation').log('Handling Cold Meal'); from('direct:others').routeId('others').log('Handling Something Other'); }
Σε αυτόν τον ορισμό, δημιουργούμε μια διαδρομή που ανακτά τις εγγραφές από το αρχείο JSON, τις χωρίζει σε στοιχεία και τις διαδρομές σε ένα σύνολο ελεγκτών με βάση το περιεχόμενο του μηνύματος.
Ας το τρέξουμε στα προετοιμασμένα δεδομένα δοκιμής. Θα πάρουμε την έξοδο:
βέλτιστες πρακτικές σχεδιασμού με απόκριση 2019
INFO | Total 6 routes, of which 6 are started INFO | Apache Camel 2.20.0 (CamelContext: camel-1) started in 10.716 seconds INFO | Incoming File: order1.json INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='1', type='Drink', name='Americano', qty='1'}] INFO | Handling Drink INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='2', type='Hot Meal', name='French Omelette', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='3', type='Hot Meal', name='Lasagna', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='4', type='Hot Meal', name='Rice Balls', qty='1'}] INFO | Handling Hot Meal INFO | Exchange[ExchangePattern: InOnly, BodyType: com.antongoncharov.camel.example.model.OrderItem, Body: OrderItem{id='5', type='Dessert', name='Blueberry Pie', qty='1'}] INFO | Handling Dessert
Όπως ήταν αναμενόμενο, η Camel έστειλε μηνύματα σε προορισμούς.
Στο προηγούμενο παράδειγμα, η αλληλεπίδραση μεταξύ των στοιχείων είναι σύγχρονη και γίνεται μέσω της μνήμης της εφαρμογής. Ωστόσο, υπάρχουν πολλοί περισσότεροι τρόποι επικοινωνίας όταν χειριζόμαστε ξεχωριστές εφαρμογές που δεν μοιράζονται μνήμη:
Υπάρχουν περισσότεροι τρόποι αλληλεπίδρασης, αλλά πρέπει να έχουμε κατά νου ότι, σε γενικές γραμμές, υπάρχουν δύο τύποι αλληλεπίδρασης: σύγχρονη και ασύγχρονη. Το πρώτο είναι σαν να καλείτε μια συνάρτηση στον κώδικά σας: η ροή εκτέλεσης θα περιμένει μέχρι να εκτελέσει και να επιστρέψει μια τιμή. Με μια ασύγχρονη προσέγγιση, τα ίδια δεδομένα αποστέλλονται μέσω μιας ενδιάμεσης ουράς μηνυμάτων ή θέματος συνδρομής. Μπορείτε να πραγματοποιήσετε μια ασύγχρονη κλήση απομακρυσμένης λειτουργίας όπως Αίτηση-απάντηση EIP .
Ωστόσο, τα ασύγχρονα μηνύματα δεν αποτελούν θεραπεία. συνεπάγεται ορισμένους περιορισμούς. Σπάνια βλέπετε API ανταλλαγής μηνυμάτων στον ιστό. Οι σύγχρονες υπηρεσίες REST είναι πολύ πιο δημοφιλείς. Αλλά μεσαίο λογισμικό Η ανταλλαγή μηνυμάτων χρησιμοποιείται ευρέως σε εταιρικά ενδοδίκτυα ή σε υποδομές back-end κατανεμημένου συστήματος.
Ας κάνουμε το ασύγχρονο παράδειγμά μας. Ένα σύστημα λογισμικού που διαχειρίζεται τις ουρές και τα θέματα συνδρομής καλείται πράκτορας μηνυμάτων . Είναι σαν Σχετικό σύστημα διαχείρισης βάσεων δεδομένων ( RDBMS ) για πίνακες και στήλες. Οι ουρές λειτουργούν ως ενοποίηση peer-to-peer, ενώ τα θέματα αφορούν την επικοινωνία δημοσίευσης-εγγραφής με πολλούς παραλήπτες. Θα το χρησιμοποιησουμε Apache ActiveMQ ως μεσίτης μηνυμάτων JMS επειδή είναι ισχυρός και ενσωματωμένος.
Προσθέστε την ακόλουθη εξάρτηση. Μερικές φορές είναι υπερβολικό να προσθέσετε activemq-all
, το οποίο περιέχει όλα τα αρχεία βάζων ActiveMQ, για το έργο, αλλά θα διατηρήσουμε τις εξαρτήσεις της εφαρμογής μας χωρίς επιπλοκές.
org.apache.activemq activemq-all 5.15.2
Στη συνέχεια, ξεκινήστε τον μεσίτη μέσω προγραμματισμού. Στο Spring Boot έχουμε μια αυτόματη διαμόρφωση για αυτό κατά τη σύνδεση του Maven Dependency. spring-boot-starter-activemq
.
Εκτελέστε έναν νέο μεσίτη μηνυμάτων με τις ακόλουθες εντολές, καθορίζοντας μόνο το τελικό σημείο της εφαρμογής σύνδεσης:
BrokerService broker = new BrokerService(); broker.addConnector('tcp://localhost:61616'); broker.start();
Και προσθέστε το ακόλουθο απόσπασμα config στο σώμα της μεθόδου configure
:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory('tcp://localhost:61616'); this.getContext().addComponent('activemq', ActiveMQComponent.jmsComponent(connectionFactory));
Τώρα μπορούμε να ενημερώσουμε το προηγούμενο παράδειγμα χρησιμοποιώντας ουρές μηνυμάτων. Οι ουρές θα δημιουργηθούν αυτόματα κατά την παράδοση των μηνυμάτων.
public void configure() { errorHandler(defaultErrorHandler().maximumRedeliveries(0)); ConnectionFactory connectionFactory = new ActiveMQConnectionFactory('tcp://localhost:61616'); this.getContext().addComponent('activemq', ActiveMQComponent.jmsComponent(connectionFactory)); from('file:orders?noop=true').routeId('main') .log('Incoming File: ${file:onlyname}') .unmarshal().json(JsonLibrary.Jackson, Order.class) // unmarshal JSON to Order class containing List .split().simple('body.items') // split list to process one by one .to('log:inputOrderItem') .choice() .when().simple('${body.type} == 'Drink'') .to('activemq:queue:bar') .when().simple('${body.type} == 'Dessert'') .to('activemq:queue:dessertStation') .when().simple('${body.type} == 'Hot Meal'') .to('activemq:queue:hotMealStation') .when().simple('${body.type} == 'Cold Meal'') .to('activemq:queue:coldMealStation') .otherwise() .to('activemq:queue:others'); from('activemq:queue:bar').routeId('barAsync').log('Drinks'); from('activemq:queue:dessertStation').routeId('dessertAsync').log('Dessert'); from('activemq:queue:hotMealStation').routeId('hotMealAsync').log('Hot Meals'); from('activemq:queue:coldMealStation').routeId('coldMealAsync').log('Cold Meals'); from('activemq:queue:others').routeId('othersAsync').log('Others'); }
Εντάξει, τώρα η αλληλεπίδραση έχει πάει ασύγχρονη. Οι πιθανοί καταναλωτές αυτών των πληροφοριών μπορούν να έχουν πρόσβαση σε αυτές όταν είναι έτοιμοι να το πράξουν. Αυτό είναι ένα παράδειγμα χαλαρά συνδεδεμένου που προσπαθούμε να επιτύχουμε σε μια αντιδραστική αρχιτεκτονική. Η μη διαθεσιμότητα μιας από τις υπηρεσίες δεν θα αποκλείσει τις άλλες. Επίσης, ένας καταναλωτής μπορεί να κλιμακώσει και να διαβάσει παράλληλα από την ουρά. Η ίδια η ουρά μπορεί να κλιμακωθεί και να χωριστεί. Οι μόνιμες ουρές μπορούν να αποθηκεύσουν δεδομένα σε δίσκο, περιμένοντας να υποβληθούν σε επεξεργασία, ακόμη και όταν όλοι οι συμμετέχοντες έπεσαν σε σφάλμα. Κατά συνέπεια, αυτό το σύστημα είναι πιο ανεκτικό σε σφάλματα.
Ένα εκπληκτικό γεγονός είναι ότι Το CERN χρησιμοποιεί Apache Camel και ActiveMQ για την παρακολούθηση των συστημάτων του Μεγάλου Hadron Collider ( LHC ) . Υπάρχει επίσης μια διατριβή του ενδιαφέρουσα τεχνογνωσία που εξηγεί την επιλογή μιας λύσης μεσαίο λογισμικό κατάλληλο για αυτήν την εργασία . Έτσι, όπως λένε στο βασικό μέρος, 'Χωρίς JMS - χωρίς σωματιδιακή φυσική!'
Στο παραπάνω παράδειγμα, δημιουργούμε το κανάλι δεδομένων μεταξύ δύο υπηρεσιών. Είναι ένα επιπλέον πιθανό σημείο αποτυχίας σε μια αρχιτεκτονική, οπότε πρέπει να το φροντίσουμε. Ας ρίξουμε μια ματιά στις δυνατότητες παρακολούθησης που προσφέρει η Apache Camel. Βασικά, εκθέτει στατιστικές πληροφορίες σχετικά με τις διαδρομές σας μέσω των MBeans στα οποία μπορεί να έχει πρόσβαση το JMX. Το ActiveMQ εκθέτει στατιστικά στοιχεία ουράς με τον ίδιο τρόπο.
Θα ενεργοποιήσουμε τον διακομιστή JMX στην εφαρμογή για να τον επιτρέψουμε να εκτελείται με τις επιλογές γραμμής εντολών:
-Dorg.apache.camel.jmx.createRmiConnector=true -Dorg.apache.camel.jmx.mbeanObjectDomainName=org.apache.camel -Dorg.apache.camel.jmx.rmiConnector.registryPort=1099 -Dorg.apache.camel.jmx.serviceUrlPath=camel
Τώρα εκτελέστε την εφαρμογή έτσι το μονοπάτι έχει κάνει τη δουλειά του. Ανοίγει το τυπικό εργαλείο jconsole
και συνδεθείτε στη διαδικασία αίτησης. Συνδεθείτε στη διεύθυνση URL service:jmx:rmi:///jndi/rmi://localhost:1099/camel
. Μεταβείτε στον τομέα org.apache.camel στο δέντρο MBeans.
ο καλύτερος τρόπος για να σπουδάσετε για πιστοποίηση aws
Μπορούμε να δούμε ότι όλα σχετικά με τη δρομολόγηση είναι υπό έλεγχο. Έχουμε τον αριθμό των μηνυμάτων κατά την πτήση, τον αριθμό σφαλμάτων και τον αριθμό μηνυμάτων στις ουρές. Αυτές οι πληροφορίες μπορούν να διοχετευτούν σε ένα σύνολο εργαλείων παρακολούθησης υψηλής λειτουργικότητας, όπως Graphana ή Kibana. Μπορείτε να το κάνετε αυτό εφαρμόζοντας τη γνωστή στοίβα ELK.
Υπάρχει επίσης μια ενσωματωμένη και επεκτάσιμη κονσόλα ιστού που παρέχει μια διεπαφή χρήστη για τη διαχείριση Camel, ActiveMQ και πολλά άλλα, που ονομάζονται hawt.io .
Το Apache Camel έχει αρκετά εκτεταμένη λειτουργικότητα για γράψτε διαδρομές δοκιμής με πλαστά στοιχεία . Είναι ένα ισχυρό εργαλείο, αλλά το να γράφεις ξεχωριστές διαδρομές μόνο για δοκιμές είναι μια χρονοβόρα διαδικασία. Θα ήταν πιο αποτελεσματικό να εκτελέσετε δοκιμές στις διαδρομές παραγωγής χωρίς να τροποποιήσετε τον αγωγό σας. Η Camel διαθέτει αυτή τη δυνατότητα και μπορεί να εφαρμοστεί χρησιμοποιώντας το στοιχείο Συμβουλές .
Θα ενεργοποιήσουμε τη λογική δοκιμών στο παράδειγμά μας και θα εκτελέσουμε ένα δείγμα δοκιμής.
junit junit 4.11 test org.apache.camel camel-test 2.20.0 test
Η τάξη δοκιμής είναι:
public class AsyncRouteTest extends CamelTestSupport { @Override protected RouteBuilder createRouteBuilder() throws Exception { return new AsyncRouteBuilder(); } @Before public void mockEndpoints() throws Exception { context.getRouteDefinition('main').adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // we substitute all actual queues with mock endpoints mockEndpointsAndSkip('activemq:queue:bar'); mockEndpointsAndSkip('activemq:queue:dessertStation'); mockEndpointsAndSkip('activemq:queue:hotMealStation'); mockEndpointsAndSkip('activemq:queue:coldMealStation'); mockEndpointsAndSkip('activemq:queue:others'); // and replace the route's source with test endpoint replaceFromWith('file://testInbox'); } }); } @Test public void testSyncInteraction() throws InterruptedException { String testJson = '{'id': 1, 'order': [{'id': 1, 'name': 'Americano', 'type': 'Drink', 'qty': '1'}, {'id': 2, 'name': 'French Omelette', 'type': 'Hot Meal', 'qty': '1'}, {'id': 3, 'name': 'Lasagna', 'type': 'Hot Meal', 'qty': '1'}, {'id': 4, 'name': 'Rice Balls', 'type': 'Hot Meal', 'qty': '1'}, {'id': 5, 'name': 'Blueberry Pie', 'type': 'Dessert', 'qty': '1'}]}'; // get mocked endpoint and set an expectation MockEndpoint mockEndpoint = getMockEndpoint('mock:activemq:queue:hotMealStation'); mockEndpoint.expectedMessageCount(3); // simulate putting file in the inbox folder template.sendBodyAndHeader('file://testInbox', testJson, Exchange.FILE_NAME, 'test.json'); //checks that expectations were met assertMockEndpointsSatisfied(); } }
Τώρα εκτελέστε δοκιμές για την εφαρμογή με mvn test
. Μπορούμε να δούμε ότι η διαδρομή μας έχει εκτελεστεί με επιτυχία με τις δοκιμαστικές συμβουλές. Δεν έχουν περάσει μηνύματα μέσω των πραγματικών ουρών και οι δοκιμές έχουν περάσει.
INFO | Route: main started and consuming from: file://testInbox INFO | Incoming File: test.json INFO | Asserting: mock://activemq:queue:hotMealStation is satisfied
Ένα από τα προβλήματα ενσωμάτωσης σήμερα είναι ότι οι εφαρμογές δεν είναι πλέον στατικές. Σε μια υποδομή cloud, συνεργαζόμαστε με εικονικές υπηρεσίες που εκτελούνται ταυτόχρονα σε πολλούς κόμβους. Επιτρέπει την αρχιτεκτονική των μικροϋπηρεσιών με ένα δίκτυο μικρών και ελαφρών υπηρεσιών που αλληλεπιδρούν μεταξύ τους. Αυτές οι υπηρεσίες έχουν μια αναξιόπιστη ζωή και πρέπει να τις ανακαλύψουμε δυναμικά.
Η επικόλληση υπηρεσιών cloud μαζί είναι μια εργασία που μπορεί να επιλυθεί με το Apache Camel. Είναι ιδιαίτερα ενδιαφέρον λόγω της γεύσης EIP και του γεγονότος ότι η Camel διαθέτει πολλούς προσαρμογείς και υποστηρίζει ένα ευρύ φάσμα πρωτοκόλλων. Η πρόσφατη έκδοση 2.18 προσθέτει το στοιχείο ServiceCall , το οποίο εισάγει μια δυνατότητα κλήσης ενός API και επίλυσης της διεύθυνσής του μέσω μηχανισμών εντοπισμού συμπλεγμάτων. Προς το παρόν, υποστηρίζει Consul, Kubernetes, Ribbon κ.λπ. Ορισμένα δείγματα κώδικα, όπου το ServiceCall έχει ρυθμιστεί με τον Πρόξενο, μπορούν να βρεθούν εύκολα. Θα χρησιμοποιήσουμε το Kubernetes εδώ γιατί είναι η αγαπημένη μου λύση συμπλέγματος.
Το σύστημα ολοκλήρωσης θα έχει ως εξής:
Η υπηρεσία Order
και η υπηρεσία Inventory
θα είναι μερικές ασήμαντες εφαρμογές Ανοιξιάτικη μπότα επιστροφή στατικών δεδομένων. Δεν είμαστε συνδεδεμένοι με μια συγκεκριμένη στοίβα τεχνολογίας εδώ. Αυτές οι υπηρεσίες παράγουν τα δεδομένα που θέλουμε να επεξεργαστούμε.
Ελεγκτής υπηρεσίας παραγγελίας:
@RestController public class OrderController { private final OrderStorage orderStorage; @Autowired public OrderController(OrderStorage orderStorage) { this.orderStorage = orderStorage; } @RequestMapping('/info') public String info() { return 'Order Service UUID = ' + OrderApplication.serviceID; } @RequestMapping('/orders') public List getAll() { return orderStorage.getAll(); } @RequestMapping('/orders/{id}') public Order getOne(@PathVariable Integer id) { return orderStorage.getOne(id); } }
Παράγει δεδομένα με τη μορφή:
[{'id':1,'items':[2,3,4]},{'id':2,'items':[5,3]}]
Ο ελεγκτής υπηρεσιών Inventory
είναι απολύτως παρόμοια με την υπηρεσία Order
@RestController public class InventoryController { private final InventoryStorage inventoryStorage; @Autowired public InventoryController(InventoryStorage inventoryStorage) { this.inventoryStorage = inventoryStorage; } @RequestMapping('/info') public String info() { return 'Inventory Service UUID = ' + InventoryApplication.serviceID; } @RequestMapping('/items') public List getAll() { return inventoryStorage.getAll(); } @RequestMapping('/items/{id}') public InventoryItem getOne(@PathVariable Integer id) { return inventoryStorage.getOne(id); } }
InventoryStorage
είναι ένα γενικό αποθετήριο που περιέχει δεδομένα. Σε αυτό το παράδειγμα, επιστρέφει στατικά προκαθορισμένα αντικείμενα, τα οποία ταξινομούνται σύμφωνα με την ακόλουθη μορφή.
[{'id':1,'name':'Laptop','description':'Up to 12-hours battery life','price':499.9},{'id':2,'name':'Monitor','description':'27-inch, response time: 7ms','price':200.0},{'id':3,'name':'Headphones','description':'Soft leather ear-cups','price':29.9},{'id':4,'name':'Mouse','description':'Designed for comfort and portability','price':19.0},{'id':5,'name':'Keyboard','description':'Layout: US','price':10.5}]
Θα γράψουμε μια διαδρομή πύλης που τις συνδέει, αλλά χωρίς ServiceCall σε αυτό το βήμα:
rest('/orders') .get('/').description('Get all orders with details').outType(TestResponse.class) .route() .setHeader('Content-Type', constant('application/json')) .setHeader('Accept', constant('application/json')) .setHeader(Exchange.HTTP_METHOD, constant('GET')) .removeHeaders('CamelHttp*') .to('http4://localhost:8082/orders?bridgeEndpoint=true') .unmarshal(formatOrder) .enrich('direct:enrichFromInventory', new OrderAggregationStrategy()) .to('log:result') .endRest(); from('direct:enrichFromInventory') .transform().simple('${null}') .setHeader('Content-Type', constant('application/json')) .setHeader('Accept', constant('application/json')) .setHeader(Exchange.HTTP_METHOD, constant('GET')) .removeHeaders('CamelHttp*') .to('http4://localhost:8081/items?bridgeEndpoint=true') .unmarshal(formatInventory);
Τώρα φανταστείτε ότι κάθε υπηρεσία δεν είναι πλέον μια συγκεκριμένη παρουσία αλλά ένα σύννεφο παρουσιών που λειτουργούν ως μία. Θα χρησιμοποιήσουμε το Minikube για να δοκιμάσουμε τοπικά το σύμπλεγμα Kubernetes.
Διαμορφώστε τις διαδρομές δικτύου για να δείτε τους κόμβους Kubernetes τοπικά (το παράδειγμα που δίνεται είναι για περιβάλλον Mac / Linux):
s corp εναντίον c corp
# remove existing routes sudo route -n delete 10/24 > /dev/null 2>&1 # add routes sudo route -n add 10.0.0.0/24 $(minikube ip) # 172.17.0.0/16 ip range is used by docker in minikube sudo route -n add 172.17.0.0/16 $(minikube ip) ifconfig 'bridge100' | grep member | awk '{print }’ # use interface name from the output of the previous command # needed for xhyve driver, which I'm using for testing sudo ifconfig bridge100 -hostfilter en5
Υπηρεσίες αναδίπλωσης σε Δοχεία Docker με διαμόρφωση Dockerfile ως εξής:
FROM openjdk:8-jdk-alpine VOLUME /tmp ADD target/order-srv-1.0-SNAPSHOT.jar app.jar ADD target/lib lib ENV JAVA_OPTS='' ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar
Δημιουργήστε και υποβάλετε τις εικόνες υπηρεσίας στο μητρώο Docker. Τώρα εκτελέστε τους κόμβους στο τοπικό σύμπλεγμα Kubernetes.
Διαμόρφωση ανάπτυξης Kubernetes.yaml:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: inventory spec: replicas: 3 selector: matchLabels: app: inventory template: metadata: labels: app: inventory spec: containers: - name: inventory image: inventory-srv:latest imagePullPolicy: Never ports: - containerPort: 8081
Εκθέστε αυτές τις αναπτύξεις ως υπηρεσίες συμπλέγματος:
kubectl expose deployment order-srv --type=NodePort kubectl expose deployment inventory-srv --type=NodePort
Τώρα μπορούμε να ελέγξουμε εάν τα αιτήματα προβάλλονται από τυχαία επιλεγμένους κόμβους από το σύμπλεγμα. Εκτέλεση curl -X http://192.168.99.100:30517/info
διαδοχικά αρκετές φορές για πρόσβαση στο NodePort minikube, για την εκτεθειμένη υπηρεσία (χρησιμοποιώντας τον κεντρικό υπολογιστή και τη θύρα σας). Στο αποτέλεσμα, βλέπουμε ότι έχουμε επιτύχει το ισοζύγιο αιτημάτων. UUID υπηρεσίας αποθέματος = 22f8ca6b-f56b-4984-927b-cbf9fcf81da5 cbf9fcf81da5 UUID υπηρεσίας αποθέματος = 50323ddb-3ace-4424-820a-6b4e85775af4 ~~~
Προσθέστε τις εξαρτήσεις camel-kubernetes
και camel-netty4-http
al pom.xml
του σχεδίου. Στη συνέχεια, διαμορφώστε το στοιχείο ServiceCall για χρήση κοινόχρηστου κύριου εντοπισμού Kubernetes για όλες τις κλήσεις υπηρεσίας μεταξύ ορισμών διαδρομής:
KubernetesConfiguration kubernetesConfiguration = new KubernetesConfiguration(); kubernetesConfiguration.setMasterUrl('https://192.168.64.2:8443'); kubernetesConfiguration.setClientCertFile('/Users/antongoncharov/.minikube/client.crt'); kubernetesConfiguration.setClientKeyFile('/Users/antongoncharov/.minikube/client.key'); kubernetesConfiguration.setNamespace('default”); ServiceCallConfigurationDefinition config = new ServiceCallConfigurationDefinition(); config.setServiceDiscovery(new KubernetesClientServiceDiscovery(kubernetesConfiguration)); context.setServiceCallConfiguration(config);
Το ServiceCall EIP συμπληρώνει καλά το Spring Boot. Οι περισσότερες από τις επιλογές μπορούν να οριστούν απευθείας στο αρχείο application.properties
.
Βελτιώστε τη διαδρομή Camel με το στοιχείο ServiceCall:
rest('/orders') .get('/').description('Get all orders with details').outType(TestResponse.class) .route() .hystrix() .setHeader('Content-Type', constant('application/json')) .setHeader('Accept', constant('application/json')) .setHeader(Exchange.HTTP_METHOD, constant('GET')) .removeHeaders('CamelHttp*') .serviceCall('customer-srv','http4:customer-deployment?bridgeEndpoint=true') .unmarshal(formatOrder) .enrich('direct:enrichFromInventory', new OrderAggregationStrategy()) .to('log:result') .endRest(); from('direct:enrichFromInventory') .transform().simple('${null}') .setHeader('Content-Type', constant('application/json')) .setHeader('Accept', constant('application/json')) .setHeader(Exchange.HTTP_METHOD, constant('GET')) .removeHeaders('CamelHttp*') .serviceCall('order-srv','http4:order-srv?bridgeEndpoint=true') .unmarshal(formatInventory);
Ενεργοποιήσαμε επίσης το Circuit Breaker στη διαδρομή. Είναι ένα άγκιστρο ολοκλήρωσης που επιτρέπει τη διακοπή απομακρυσμένων κλήσεων από το σύστημα, σε περίπτωση σφαλμάτων παράδοσης ή έλλειψης διαθεσιμότητας του παραλήπτη. Αυτό έχει σχεδιαστεί για την αποφυγή αστοχιών του συστήματος καταρράκτη. Η συνιστώσα Hystrix συμβάλλει στην επίτευξη αυτού, εφαρμόζοντας το μοτίβο Circuit Breaker
Ας το εκτελέσουμε και να στείλουμε ένα δοκιμαστικό αίτημα. θα λάβουμε τη συνολική απάντηση και των δύο υπηρεσιών.
[{'id':1,'items':[{'id':2,'name':'Monitor','description':'27-inch, response time: 7ms','price':200.0},{'id':3,'name':'Headphones','description':'Soft leather ear-cups','price':29.9},{'id':4,'name':'Mouse','description':'Designed for comfort and portability','price':19.0}]},{'id':2,'items':[{'id':5,'name':'Keyboard','description':'Layout: US','price':10.5},{'id':3,'name':'Headphones','description':'Soft leather ear-cups','price':29.9}]}]
Το αποτέλεσμα είναι όπως αναμενόταν.
Έδειξα πώς το Apache Camel μπορεί να ενσωματώσει μικροσυσκευές σε ένα σύμπλεγμα. Ποιες άλλες χρήσεις για αυτό το πλαίσιο; Σε γενικές γραμμές, είναι χρήσιμο οπουδήποτε η δρομολόγηση βάσει κανόνων είναι μια λύση. Για παράδειγμα, το Apache Camel μπορεί να είναι μεσαίο λογισμικό για το Διαδίκτυο των πραγμάτων με τον προσαρμογέα Eclipse Kura. Μπορεί να χειριστεί την παρακολούθηση μεταφέροντας σήματα καταγραφής από διάφορα στοιχεία και υπηρεσίες, όπως στο σύστημα CERN. Μπορεί επίσης να είναι ένα πλαίσιο ολοκλήρωσης για την εταιρική SOA ή ένα χαρτοφυλάκιο για επεξεργασία δεδομένων παρτίδας, αν και δεν ανταγωνίζεται καλά Apache Spark σε αυτήν την περιοχή.
Μπορείτε να δείτε ότι η ολοκλήρωση συστημάτων δεν είναι μια εύκολη διαδικασία. Είμαστε τυχεροί γιατί έχει συγκεντρωθεί μεγάλη εμπειρία. Είναι σημαντικό να το εφαρμόσετε σωστά για τη δημιουργία ευέλικτων και ανεκτικών σφαλμάτων λύσεων.
Για να διασφαλιστεί η σωστή εφαρμογή, προτείνω να έχετε μια λίστα ελέγχου με σημαντικές πτυχές ενσωμάτωσης. Τα είδη που πρέπει να έχετε περιλαμβάνουν:
Σε αυτό το άρθρο, δοκιμάσαμε το Apache Camel, a δομή ελαφρύ εργαλείο ενσωμάτωσης, το οποίο βοηθά στην εξοικονόμηση χρόνου και προσπάθειας κατά την επίλυση προβλημάτων ενσωμάτωσης. Όπως δείχνουμε, μπορεί να χρησιμεύσει ως εργαλείο που υποστηρίζει τη σχετική αρχιτεκτονική μικροσυσκευών, αναλαμβάνοντας πλήρως την ευθύνη για την ανταλλαγή δεδομένων μεταξύ μικροϋπηρεσιών.
Εάν ενδιαφέρεστε να μάθετε περισσότερα για το Apache Camel, προτείνω ανεπιφύλακτα το βιβλίο 'Camel in Action' από τον δημιουργό του δομή , Claus Ibsen. Επίσημη τεκμηρίωση είναι διαθέσιμη στο camel.apache.org .