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

Αναπτύξτε μια γενική υπηρεσία προσωρινής αποθήκευσης για βελτίωση της απόδοσης

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

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

Σίγουρα, τις επόμενες εβδομάδες οι συνάδελφοί σας συνεχίζουν να ζητούν τη λίστα ξανά και ξανά. Συγχαίρετε τον εαυτό σας για την υποβολή του εγγράφου, καθώς μπορείτε να εξαγάγετε το έγγραφο από το ντουλάπι αρχειοθέτησης πιο γρήγορα από ότι μπορείτε να το εξαγάγετε από τον Ιστότοπο. Η ιδέα της αρχειοθήκης αρχειοθετεί. σύντομα όλοι αρχίζουν να βάζουν αντικείμενα στο ντουλάπι σας. Για να αποτρέψετε την αποδιοργάνωση της καμπίνας, ορίζετε οδηγίες για τη χρήση του. Με την επίσημη ιδιότητά σας ως διευθυντής αρχειοθέτησης αρχειοθέτησης, δίνετε εντολή στους συναδέλφους σας να τοποθετήσουν ετικέτες και σημειώσεις Post-it σε όλα τα έγγραφα, τα οποία προσδιορίζουν τα έγγραφα και την ημερομηνία απόρριψης / λήξης τους. Οι ετικέτες βοηθούν τους συναδέλφους σας να εντοπίσουν το έγγραφο που αναζητούν και οι σημειώσεις Post-it πληρούν τις προϋποθέσεις εάν οι πληροφορίες είναι ενημερωμένες.

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

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

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

Δημιουργήστε την προσωρινή μνήμη

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

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

Ωστόσο, προκύπτουν ορισμένα ερωτήματα, τα οποία περιλαμβάνουν τέτοιες περιπλοκές όπως:

  • Τι γίνεται αν αλλάξει η λίστα προϊόντων; Πώς μπορεί η προσωρινή μνήμη να λήξει τις λίστες; Πώς θα ξέρω πόσο καιρό θα πρέπει να παραμείνει η λίστα προϊόντων στην προσωρινή μνήμη πριν λήξει;
  • Τι γίνεται αν υπάρχουν δύο ξεχωριστές λίστες προϊόντων και οι δύο λίστες αλλάζουν σε διαφορετικά διαστήματα; Μπορώ να λήξω κάθε λίστα ξεχωριστά ή πρέπει όλοι να έχουν την ίδια διάρκεια ζωής;
  • Τι γίνεται αν η προσωρινή μνήμη είναι κενή και δύο αιτούντες δοκιμάσουν την προσωρινή μνήμη την ίδια στιγμή; Όταν και οι δύο το βρίσκουν άδειο, θα δημιουργήσουν τις δικές τους λίστες και, στη συνέχεια, θα προσπαθήσουν να βάλουν τα αντίγραφά τους στην κρυφή μνήμη;
  • Τι γίνεται αν τα στοιχεία παραμείνουν στην κρυφή μνήμη για μήνες χωρίς πρόσβαση; Δεν θα τρώνε τη μνήμη;

Για να αντιμετωπίσετε αυτές τις προκλήσεις, πρέπει να δημιουργήσετε μια υπηρεσία προσωρινής αποθήκευσης λογισμικού.

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

Σημείωση: Πριν προχωρήσετε στις απαιτήσεις και τον κωδικό της υπηρεσίας προσωρινής αποθήκευσης, ίσως θελήσετε να δείτε την πλαϊνή γραμμή παρακάτω, "Caching Versus Pooling." Εξηγεί ομαδοποίηση, μια σχετική έννοια.

Απαιτήσεις

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

  1. Κάθε εφαρμογή Java μπορεί να έχει πρόσβαση στην υπηρεσία προσωρινής αποθήκευσης.
  2. Τα αντικείμενα μπορούν να τοποθετηθούν στην προσωρινή μνήμη.
  3. Τα αντικείμενα μπορούν να εξαχθούν από την προσωρινή μνήμη.
  4. Τα προσωρινά αποθηκευμένα αντικείμενα μπορούν να καθοριστούν από μόνα τους όταν λήγουν, επιτρέποντας έτσι τη μέγιστη ευελιξία. Οι υπηρεσίες προσωρινής αποθήκευσης που λήγουν όλα τα αντικείμενα χρησιμοποιώντας τον ίδιο τύπο λήξης δεν παρέχουν τη βέλτιστη χρήση των προσωρινά αποθηκευμένων αντικειμένων. Αυτή η προσέγγιση είναι ανεπαρκής σε συστήματα μεγάλης κλίμακας καθώς, για παράδειγμα, μια λίστα προϊόντων μπορεί να αλλάζει καθημερινά, ενώ μια λίστα τοποθεσιών καταστημάτων μπορεί να αλλάζει μόνο μία φορά το μήνα.
  5. Ένα νήμα φόντου που εκτελείται με χαμηλή προτεραιότητα αφαιρεί τα αποθηκευμένα αντικείμενα που έχουν λήξει.
  6. Η υπηρεσία προσωρινής αποθήκευσης μπορεί να ενισχυθεί αργότερα μέσω της χρήσης ενός μηχανισμού εκκαθάρισης που χρησιμοποιείται λιγότερο πρόσφατα (LRU) ή λιγότερο συχνά χρησιμοποιούμενου (LFU).

Εκτέλεση

Για να ικανοποιήσουμε την απαίτηση 1, υιοθετούμε καθαρό περιβάλλον Java 100%. Παρέχοντας στο κοινό παίρνω και σειρά μεθόδους στην υπηρεσία προσωρινής αποθήκευσης, πληρούμε επίσης τις απαιτήσεις 2 και 3.

Πριν προχωρήσω σε μια συζήτηση για την απαίτηση 4, θα αναφέρω εν συντομία ότι θα ικανοποιήσουμε την απαίτηση 5 δημιουργώντας ένα ανώνυμο νήμα στον διαχειριστή κρυφής μνήμης. αυτό το νήμα ξεκινά στο στατικό μπλοκ. Επίσης, ικανοποιούμε την απαίτηση 6, προσδιορίζοντας τα σημεία όπου αργότερα θα προστεθεί κώδικας για την εφαρμογή των αλγορίθμων LRU και LFU. Θα αναφερθώ σε περισσότερες λεπτομέρειες σχετικά με αυτές τις απαιτήσεις αργότερα στο άρθρο.

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

Ας ξεκινήσουμε με τους κανόνες που διέπουν τα αντικείμενα που τοποθετούνται στην προσωρινή μνήμη.

  1. Όλα τα αντικείμενα πρέπει να έχουν μια δημόσια μέθοδο που ονομάζεται έχει λήξει (), η οποία επιστρέφει μια τιμή Boolean.
  2. Όλα τα αντικείμενα πρέπει να έχουν μια δημόσια μέθοδο που ονομάζεται getIdentifier (), που επιστρέφει ένα αντικείμενο που διακρίνει το αντικείμενο από όλα τα άλλα στην κρυφή μνήμη.

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

Θα βρείτε τον κωδικό διεπαφής για την υπηρεσία προσωρινής αποθήκευσης αυτού του άρθρου στην Καταχώριση 1.

Λίστα 1. Cacheable.java

/ ** * Τίτλος: Caching Περιγραφή: Αυτή η διεπαφή καθορίζει τις μεθόδους, οι οποίες πρέπει να εφαρμοστούν από όλα τα αντικείμενα που επιθυμούν να τοποθετηθούν στην προσωρινή μνήμη. * * Πνευματικά δικαιώματα: Πνευματικά δικαιώματα (c) 2001 * Εταιρεία: JavaWorld * Όνομα αρχείου: Cacheable.java @author Jonathan Lurie @version 1.0 * / δημόσια διεπαφή Cacheable {/ * Απαιτώντας από όλα τα αντικείμενα να προσδιορίσουν τις δικές τους λήξεις, ο αλγόριθμος αφαιρείται από το υπηρεσία προσωρινής αποθήκευσης, παρέχοντας έτσι μέγιστη ευελιξία αφού κάθε αντικείμενο μπορεί να υιοθετήσει μια διαφορετική στρατηγική λήξης. * / public boolean isExpired (); / * Αυτή η μέθοδος θα διασφαλίσει ότι η υπηρεσία προσωρινής αποθήκευσης δεν είναι υπεύθυνη για τον μοναδικό προσδιορισμό αντικειμένων που τοποθετούνται στην προσωρινή μνήμη. * / δημόσιο αντικείμενο getIdentifier (); } 

Οποιοδήποτε αντικείμενο τοποθετείται στην προσωρινή μνήμη - α Σειρά, για παράδειγμα - πρέπει να τυλιχτεί μέσα σε ένα αντικείμενο που εφαρμόζει το Κρυφή μνήμη διεπαφή. Η λίστα 2 είναι ένα παράδειγμα μιας γενικής κατηγορίας περιτυλίγματος που ονομάζεται CachedObject; Μπορεί να περιέχει οποιοδήποτε αντικείμενο χρειάζεται να τοποθετηθεί στην υπηρεσία προσωρινής αποθήκευσης. Σημειώστε ότι αυτή η τάξη περιτυλίγματος εφαρμόζει το Κρυφή μνήμη διεπαφή που ορίζεται στην καταχώριση 1.

Λίστα 2. CachedManagerTestProgram.java

/ ** * Τίτλος: Caching * Περιγραφή: Ένα γενικό περιτύλιγμα αντικειμένου Cache. Εφαρμόζει τη διασύνδεση Cacheable * χρησιμοποιεί μια στρατηγική TimeToLive για τη λήξη του CacheObject. * Πνευματικά δικαιώματα: Πνευματικά δικαιώματα (c) 2001 * Εταιρεία: JavaWorld * Όνομα αρχείου: CacheManagerTestProgram.java * @author Jonathan Lurie * @version 1.0 * / δημόσια τάξη Εφαρμογές CachedObject Cacheable {// +++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++ ++++ / * Αυτή η μεταβλητή θα χρησιμοποιηθεί για να προσδιοριστεί εάν το αντικείμενο έχει λήξει. * / private java.util.Date dateofExpiration = null; ιδιωτικό αναγνωριστικό αντικειμένου = null; / * Περιέχει την πραγματική "τιμή". Αυτό είναι το αντικείμενο που πρέπει να κοινοποιηθεί. * / public Object αντικείμενο = null; // +++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++ δημόσια CachedObject (Object obj, Object id, int minutesToLive) {this.object = obj; this.identifier = id; // minutesToLive of 0 σημαίνει ότι ζει επ 'αόριστον. if (minutesToLive! = 0) {dateofExpiration = new java.util.Date (); java.util.Calendar cal = java.util.Calendar.getInstance (); cal.setTime (dateofExpiration); cal.add (cal.MINUTE, minutesToLive); dateofExpiration = cal.getTime (); }} // ++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++ δημόσια boolean isExpired () {// Θυμηθείτε αν τα λεπτά για να ζήσετε είναι μηδέν τότε ζει για πάντα! εάν (dateofExpiration! = null) {// ημερομηνία λήξης συγκρίνεται. if (dateofExpiration.before (new java.util.Date ())) {System.out.println ("CachedResultSet.isExpired: Έληξε από την προσωρινή μνήμη! ΛΗΞΗ ΧΡΟΝΟΥ:" + dateofExpiration.toString () + "CURRENT TIME:" + ( νέο java.util.Date ()). toString ()); επιστροφή αληθινή? } αλλιώς {System.out.println ("CachedResultSet.isExpired: Έληξε όχι από την προσωρινή μνήμη!"); επιστροφή ψευδής? }} άλλο // Αυτό σημαίνει ότι ζει για πάντα! επιστροφή ψευδής? } // ++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++ δημόσιο αντικείμενο getIdentifier () {return identifier; } // ++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++} 

ο CachedObject Η τάξη εκθέτει μια μέθοδο κατασκευής που λαμβάνει τρεις παραμέτρους:

public CachedObject (Object obj, Object id, int minutesToLive) 

Ο παρακάτω πίνακας περιγράφει αυτές τις παραμέτρους.

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

Η μέθοδος κατασκευαστή καθορίζει την ημερομηνία λήξης του αντικειμένου στην προσωρινή μνήμη χρησιμοποιώντας ένα ώρα να ζήσω στρατηγική. Όπως υποδηλώνει το όνομά του, το time-to-live σημαίνει ότι ένα συγκεκριμένο αντικείμενο έχει έναν καθορισμένο χρόνο στο τέλος του οποίου θεωρείται νεκρό. Προσθέτοντας λεπτάToLive, ο κατασκευαστής int παράμετρος, στην τρέχουσα ώρα, υπολογίζεται μια ημερομηνία λήξης. Αυτή η λήξη ανατίθεται στη μεταβλητή κλάσης ημερομηνία λήξης.

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