Προγραμματισμός

Συγκεντρώστε πόρους χρησιμοποιώντας το Apache's Commons Pool Framework

Η συγκέντρωση πόρων (που ονομάζεται επίσης συγκέντρωση αντικειμένων) μεταξύ πολλαπλών πελατών είναι μια τεχνική που χρησιμοποιείται για την προώθηση της επαναχρησιμοποίησης αντικειμένων και τη μείωση των γενικών εξόδων δημιουργίας νέων πόρων, με αποτέλεσμα καλύτερη απόδοση και απόδοση. Φανταστείτε μια βαρέως τύπου εφαρμογή διακομιστή Java που στέλνει εκατοντάδες ερωτήματα SQL ανοίγοντας και κλείνοντας συνδέσεις για κάθε αίτημα SQL. Ή ένας διακομιστής Web που εξυπηρετεί εκατοντάδες αιτήματα HTTP, χειρισμό κάθε αιτήματος δημιουργώντας ένα ξεχωριστό νήμα. Ή φανταστείτε να δημιουργήσετε μια παρουσία ανάλυσης XML για κάθε αίτημα ανάλυσης ενός εγγράφου χωρίς να επαναχρησιμοποιήσετε τις παρουσίες. Αυτά είναι μερικά από τα σενάρια που απαιτούν βελτιστοποίηση των πόρων που χρησιμοποιούνται.

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

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

Οι περισσότεροι προμηθευτές διακομιστών εφαρμογών J2EE παρέχουν συγκέντρωση πόρων ως αναπόσπαστο μέρος των κοντέινερ Web και EJB (Enterprise JavaBean). Για συνδέσεις βάσης δεδομένων, ο προμηθευτής διακομιστή παρέχει συνήθως μια εφαρμογή του Πηγή δεδομένων διεπαφή, η οποία λειτουργεί σε συνδυασμό με τον προμηθευτή του προγράμματος οδήγησης JDBC (Java Database Connectivity) ConnectionPoolDataSource εκτέλεση. ο ConnectionPoolDataSource Η εφαρμογή χρησιμεύει ως εργοστάσιο σύνδεσης διαχειριστή πόρων για συγκέντρωση java.sql.Σύνδεση αντικείμενα. Παρομοίως, οι εμφανίσεις EJB χωρίς κόκκους συνεδρίας, φασόλια που βασίζονται σε μηνύματα και φασόλια οντοτήτων συγκεντρώνονται σε δοχεία EJB για υψηλότερη απόδοση και απόδοση. Οι παρουσίες ανάλυσης XML είναι επίσης υποψήφιες για συγκέντρωση, επειδή η δημιουργία παρουσιών ανάλυσης καταναλώνει μεγάλο μέρος των πόρων ενός συστήματος.

Μια επιτυχημένη υλοποίηση συγκέντρωσης πόρων ανοιχτού κώδικα είναι το DBCP του Commons Pool framework, ένα στοιχείο συγκέντρωσης συνδέσεων βάσεων δεδομένων από το Apace Software Foundation που χρησιμοποιείται ευρέως σε εταιρικές εφαρμογές παραγωγής. Σε αυτό το άρθρο, συζητώ εν συντομία τα εσωτερικά του πλαισίου Commons Pool και στη συνέχεια το χρησιμοποιώ για να εφαρμόσει μια ομάδα νήματος.

Ας δούμε πρώτα τι παρέχει το πλαίσιο.

Πλαίσιο του Commons Pool

Το πλαίσιο Commons Pool προσφέρει μια βασική και ισχυρή εφαρμογή για τη συγκέντρωση αυθαίρετων αντικειμένων. Παρέχονται πολλές εφαρμογές, αλλά για τους σκοπούς αυτού του άρθρου, χρησιμοποιούμε την πιο γενική εφαρμογή, το GenericObjectPool. Χρησιμοποιεί ένα CursorableLinkedList, η οποία είναι μια εφαρμογή διπλής σύνδεσης-λίστας (μέρος των Συλλογών Jakarta Commons), ως η υποκείμενη υποδομή για τη συγκράτηση των αντικειμένων που συγκεντρώνονται.

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

Η διεπαφή org.apache.commons.PoolableObjectFactory ορίζει τις ακόλουθες μεθόδους κύκλου ζωής, οι οποίες αποδεικνύονται απαραίτητες για την εφαρμογή ενός συνόλου συνιστωσών:

 // Δημιουργεί μια παρουσία που μπορεί να επιστραφεί από το κοινό pool Object makeObject () {} // Καταστρέφει μια παρουσία που δεν απαιτείται πλέον από το pool public void تباہObject (Object obj) {} // Επικυρώστε το αντικείμενο πριν το χρησιμοποιήσετε δημόσιο boolean validateObject (Object obj) {} // Αρχικοποιήστε μια παρουσία που θα επιστραφεί από το pool public void activateObject (Object obj) {} // Unisitialize μια παρουσία που θα επιστραφεί στο pool public void passivateObject (Object obj) {}

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

  • makeObject (): Εφαρμόστε τη δημιουργία αντικειμένων
  • καταστροφήObject (): Εφαρμόστε την καταστροφή αντικειμένων
  • επικύρωσηObject (): Επικυρώστε το αντικείμενο πριν χρησιμοποιηθεί
  • ενεργοποίηση αντικειμένου (): Εφαρμόστε τον κωδικό αρχικοποίησης αντικειμένου
  • passivateObject (): Εφαρμόστε τον κωδικό αρχικοποίησης αντικειμένου

Μια άλλη βασική διεπαφή—org.apache.commons.ObjectPool—Καθορίζει τις ακόλουθες μεθόδους για τη διαχείριση και την παρακολούθηση της ομάδας:

 // Λήψη παρουσίας από το pool μου Το Object loanObject () ρίχνει την Εξαίρεση; // Επιστρέψτε μια παρουσία στο pool μου void returnObject (Object obj) ρίχνει Εξαίρεση; // Ακυρώνει ένα αντικείμενο από το pool void validateObject (Object obj) ρίχνει Εξαίρεση; // Χρησιμοποιείται για την προ-φόρτωση μπιλιάρδου με αδρανή αντικείμενα άκυρα addObject () ρίχνει Exception; // Επιστρέψτε τον αριθμό των περιπτώσεων αδράνειας στο int getNumIdle () που ρίχνει UnsupportedOperationException; // Επιστρέψτε τον αριθμό των ενεργών παρουσιών στο getNumActive () ρίχνει UnsupportedOperationException; // Εκκαθαρίζει τα αδρανή αντικείμενα χωρίς να καθαρίζει () ρίχνει Exception, UnsupportedOperationException; // Κλείσιμο της πισίνας κενό () ρίχνει Εξαίρεση; // Ορίστε το ObjectFactory που θα χρησιμοποιείται για τη δημιουργία παρουσιών void setFactory (PoolableObjectFactory factory) ρίχνει IllegalStateException, UnsupportedOperationException;

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

Όπως αναφέρθηκε παραπάνω, η τάξη org.apache.commons.GenericObjectPool είναι μόνο μία εφαρμογή του org.apache.commons.ObjectPool διεπαφή. Το πλαίσιο παρέχει επίσης υλοποιήσεις για ομάδες αντικειμένων με κλειδί, χρησιμοποιώντας τις διεπαφές org.apache.commons.KeyedObjectPoolFactory και org.apache.commons.KeyedObjectPool, όπου μπορεί κανείς να συσχετίσει μια ομάδα με ένα κλειδί (όπως στο HashMap) και έτσι να διαχειριστείτε πολλές ομάδες.

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

Λεπτομέρειες διαμόρφωσης

Η πισίνα μπορεί να ρυθμιστεί χρησιμοποιώντας το GenericObjectPool.Config τάξη, που είναι μια στατική εσωτερική τάξη. Εναλλακτικά, θα μπορούσαμε απλώς να χρησιμοποιήσουμε το GenericObjectPoolΟι μέθοδοι ρύθμισης για να ορίσετε τις τιμές.

Η ακόλουθη λίστα περιγράφει μερικές από τις διαθέσιμες παραμέτρους διαμόρφωσης για το GenericObjectPool εκτέλεση:

  • maxIdle: Ο μέγιστος αριθμός περιστατικών ύπνου στην πισίνα, χωρίς να απελευθερώνονται επιπλέον αντικείμενα.
  • minIdle: Ο ελάχιστος αριθμός περιστατικών ύπνου στην πισίνα, χωρίς να δημιουργούνται επιπλέον αντικείμενα.
  • maxActive: Ο μέγιστος αριθμός ενεργών παρουσιών στην ομάδα.
  • timeBetweenEvictionRunsMillis: Ο αριθμός χιλιοστών του δευτερολέπτου που πρέπει να κοιμηθεί μεταξύ των τρεξίματος του νήματος εκκένωσης αντικειμένου ρελαντί Όταν είναι αρνητικό, δεν θα εκτελείται κανένα νήμα αιωρούμενου αντικειμένου. Χρησιμοποιήστε αυτήν την παράμετρο μόνο όταν θέλετε να εκτελείται το νήμα εκκένωσης.
  • minEvictableIdleTimeMillis: Το ελάχιστο χρονικό διάστημα ενός αντικειμένου, εάν είναι ενεργό, μπορεί να παραμείνει αδρανές στο χώρο συγκέντρωσης πριν είναι επιλέξιμο για έξωση από τον εκδιώκτη ρελαντί-αντικειμένου. Εάν παρέχεται μια αρνητική τιμή, δεν απομακρύνονται αντικείμενα εξαιτίας μόνο του χρόνου αδράνειας.
  • testOnBorrow: Όταν "true", τα αντικείμενα επικυρώνονται. Εάν το αντικείμενο αποτύχει στην επικύρωση, θα πέσει από το σύνολο και το σύνολο θα προσπαθήσει να δανειστεί άλλο.

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

Για να καταλάβετε περισσότερα για την ομάδα και τα εσωτερικά της, ας εφαρμόσουμε μια ομάδα νήματος.

Προτεινόμενες απαιτήσεις συγκέντρωσης νημάτων

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

  • Το νήμα πρέπει να μπορεί να επικαλεστεί οποιαδήποτε αυθαίρετη μέθοδο κλάσης (η προγραμματισμένη εργασία)
  • Το νήμα πρέπει να είναι σε θέση να επιστρέψει το αποτέλεσμα μιας εκτέλεσης
  • Το νήμα πρέπει να μπορεί να αναφέρει την ολοκλήρωση μιας εργασίας

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

  • Το όνομα της τάξης
  • Το όνομα της μεθόδου προς επίκληση
  • Οι παράμετροι που πρέπει να περάσουν στη μέθοδο
  • Οι τύποι παραμέτρων των παραμέτρων που πέρασαν

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

Η τρίτη απαίτηση σχετίζεται κάπως με τη δεύτερη απαίτηση. Η αναφορά της ολοκλήρωσης μιας εργασίας μπορεί επίσης να σημαίνει ότι ο πελάτης περιμένει να πάρει το αποτέλεσμα της εκτέλεσης. Για να χειριστούμε αυτήν την ικανότητα, μπορούμε να παρέχουμε κάποια μορφή μηχανισμού επανάκλησης. Ο απλούστερος μηχανισμός επανάκλησης μπορεί να εφαρμοστεί χρησιμοποιώντας το java.lang.Object'μικρό Περίμενε() και κοινοποιώ() σημασιολογία. Εναλλακτικά, θα μπορούσαμε να χρησιμοποιήσουμε το Παρατηρητής μοτίβο, αλλά προς το παρόν ας κρατήσουμε τα πράγματα απλά. Ίσως να μπείτε στον πειρασμό να χρησιμοποιήσετε το java.lang.Tread της τάξης Συμμετοχή() μέθοδο, αλλά αυτό δεν θα λειτουργήσει αφού το συγκεντρωμένο νήμα δεν ολοκληρώνει ποτέ το τρέξιμο() μέθοδο και συνεχίζει να λειτουργεί όσο το χρειάζεται η πισίνα.

Τώρα που έχουμε έτοιμες τις απαιτήσεις μας και μια γενική ιδέα για το πώς να εφαρμόσουμε το νήμα, είναι καιρός να κάνουμε κάποια πραγματική κωδικοποίηση.

Σε αυτό το στάδιο, το διάγραμμα κλάσης UML του προτεινόμενου σχεδιασμού μοιάζει με το παρακάτω σχήμα.

Εφαρμογή της ομάδας νήματος

Το αντικείμενο νήματος που πρόκειται να συγκεντρώσουμε είναι στην πραγματικότητα ένα περιτύλιγμα γύρω από το αντικείμενο νήματος. Ας καλέσουμε το περιτύλιγμα το WorkerThread τάξη, η οποία επεκτείνει το java.lang.Tread τάξη. Πριν ξεκινήσουμε την κωδικοποίηση WorkerThread, πρέπει να εφαρμόσουμε τις απαιτήσεις του πλαισίου. Όπως είδαμε νωρίτερα, πρέπει να εφαρμόσουμε το PoolableObjectFactory, που λειτουργεί ως εργοστάσιο, για να δημιουργήσουμε το poolable μας WorkerThreadμικρό. Μόλις το εργοστάσιο είναι έτοιμο, εφαρμόζουμε το ThreadPool επεκτείνοντας το GenericObjectPool. Τότε, τελειώνουμε WorkerThread.

Εφαρμογή της διασύνδεσης PoolableObjectFactory

Ξεκινάμε με το PoolableObjectFactory διεπαφή και προσπαθήστε να εφαρμόσετε τις απαραίτητες μεθόδους κύκλου ζωής για το νήμα μας. Γράφουμε την εργοστασιακή τάξη ThreadObjectFactory ως εξής:

δημόσια τάξη ThreadObjectFactory υλοποιεί PoolableObjectFactory {

δημόσιο αντικείμενο makeObject () {return new WorkerThread (); } public void destrObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; rt.setStopped (true); // Κάντε το τρέξιμο νήματος stop}} public boolean validateObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; if (rt.isRunning ()) {if (rt.getThreadGroup () == null) {επιστροφή false; } επιστροφή αληθινή; }} επιστρέψτε αληθινή; } public void activateObject (Object obj) {log.debug ("activateObject ..."); }

public void passivateObject (Object obj) {log.debug ("passivateObject ..." + obj); if (obj instanceof WorkerThread) {WorkerThread wt = (WorkerThread) obj; wt.setResult (μηδέν); // Εκκαθάριση του αποτελέσματος της εκτέλεσης}}}

Ας δούμε λεπτομερώς κάθε μέθοδο:

Μέθοδος makeObject () δημιουργεί το WorkerThread αντικείμενο. Για κάθε αίτημα, η ομάδα ελέγχου ελέγχεται για να δει εάν ένα νέο αντικείμενο πρόκειται να δημιουργηθεί ή ένα υπάρχον αντικείμενο πρόκειται να επαναχρησιμοποιηθεί. Για παράδειγμα, εάν ένα συγκεκριμένο αίτημα είναι το πρώτο αίτημα και το σύνολο είναι κενό, το ObjectPool κλήσεις υλοποίησης makeObject () και προσθέτει το WorkerThread στην πισίνα.

Μέθοδος καταστροφήObject () αφαιρεί το WorkerThread Αντικείμενο από το μπιλιάρδο, ορίζοντας μια σημαία Boolean και έτσι σταματώντας το νήμα που τρέχει. Θα δούμε ξανά αυτό το κομμάτι αργότερα, αλλά παρατηρούμε ότι τώρα αναλαμβάνουμε τον έλεγχο της καταστροφής των αντικειμένων μας.

$config[zx-auto] not found$config[zx-overlay] not found