Πώς να χρησιμοποιήσετε το Redis για να υπερφορτώσετε τα API Ιστού σας

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

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

Η προσωρινή αποθήκευση ορίζεται ως ένας μηχανισμός αποθήκευσης δεδομένων ή αρχείων σε μια προσωρινή τοποθεσία αποθήκευσης από όπου μπορεί να έχει άμεση πρόσβαση όποτε απαιτείται.

Η προσωρινή αποθήκευση έχει γίνει απαραίτητη στις εφαρμογές Ιστού στις μέρες μας. Μπορούμε να χρησιμοποιήσουμε το Redis για να υπερφορτώσουμε τα API ιστού μας - τα οποία κατασκευάζονται χρησιμοποιώντας Node.js και MongoDB.

Redis: Επισκόπηση ενός λαϊκού

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

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

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

Ας μιλήσουμε κώδικα

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

Εγινε? Δροσερός. Ας αρχίσουμε. Έχουμε μια απλή εφαρμογή που δημιουργήθηκε στο Express, η οποία χρησιμοποιεί μια παρουσία στο MongoDB Atlas για να διαβάζει και να γράφει δεδομένα από.

Έχουμε δημιουργήσει δύο σημαντικά API στο /blogsαρχείο διαδρομής.

... // GET - Fetches all blog posts for required user blogsRouter.route('/:user') .get(async (req, res, next) => { const blogs = await Blog.find({ user: req.params.user }); res.status(200).json({ blogs, }); }); // POST - Creates a new blog post blogsRouter.route('/') .post(async (req, res, next) => { const existingBlog = await Blog.findOne({ title: req.body.title }); if (!existingBlog) { let newBlog = new Blog(req.body); const result = await newBlog.save(); return res.status(200).json({ message: `Blog ${result.id} is successfully created`, result, }); } res.status(200).json({ message: 'Blog with same title exists', }); }); ...

Ψεκάζοντας κάποια καλοσύνη Redis

Ξεκινάμε κάνοντας λήψη του πακέτου npm redisγια σύνδεση στον τοπικό διακομιστή redis.

const mongoose = require('mongoose'); const redis = require('redis'); const util = require('util'); const redisUrl = 'redis://127.0.0.1:6379'; const client = redis.createClient(redisUrl); client.hget = util.promisify(client.hget); ...

Χρησιμοποιούμε τη utils.promisifyσυνάρτηση για να μετατρέψουμε τη client.hgetσυνάρτηση για να επιστρέψουμε μια υπόσχεση αντί για μια επιστροφή κλήσης. Μπορείτε να διαβάσετε περισσότερα για promisificationεδώ.

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

Η στρατηγική προσωρινής αποθήκευσης πρέπει να είναι σε θέση να αντιμετωπίσει τα ακόλουθα σημεία.

  • Cache το αίτημα για όλες τις αναρτήσεις ιστολογίου για έναν συγκεκριμένο χρήστη
  • Εκκαθαρίστε την προσωρινή μνήμη κάθε φορά που δημιουργείται μια νέα ανάρτηση ιστολογίου

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

  • Ο σωστός τρόπος χειρισμού δημιουργίας κλειδιών για την αποθήκευση δεδομένων cache
  • Λογική λήξης προσωρινής μνήμης και αναγκαστική λήξη για διατήρηση της φρεσκάδας της προσωρινής μνήμης
  • Επαναχρησιμοποιήσιμη εφαρμογή της λογικής προσωρινής αποθήκευσης

Εντάξει. Έχουμε σημειώσει τα σημεία μας και συνδέονται ξανά. Στο επόμενο βήμα.

Παράκαμψη της προεπιλεγμένης συνάρτησης Mongoose Exec

Θέλουμε η λογική προσωρινής αποθήκευσης να είναι επαναχρησιμοποιήσιμη. Και όχι μόνο επαναχρησιμοποιήσιμο, θέλουμε επίσης να είναι το πρώτο σημείο ελέγχου πριν κάνουμε οποιοδήποτε ερώτημα στη βάση δεδομένων. Αυτό μπορεί εύκολα να γίνει με τη χρήση ενός απλού hacking piggy-backing στη λειτουργία exec mongoose.

... const exec = mongoose.Query.prototype.exec; ... mongoose.Query.prototype.exec = async function() { ... const result = await exec.apply(this, arguments); console.log('Data Source: Database'); return result; } ...

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

Προσθήκη της προσωρινής μνήμης ως ερώτημα

Προκειμένου να υποδηλώσουμε ποια ερωτήματα πρέπει να είναι για προσωρινή αποθήκευση, δημιουργούμε ένα ερώτημα μαγκούζ Παρέχουμε τη δυνατότητα να περάσουμε το userνα χρησιμοποιηθεί ως hash-key μέσα από το optionsαντικείμενο.

Σημείωση: Το Hashkey χρησιμεύει ως αναγνωριστικό για μια δομή δεδομένων κατακερματισμού που, σε απλούς όρους, μπορεί να δηλωθεί ως το γονικό κλειδί σε ένα σύνολο ζευγών κλειδιών-τιμών. Με αυτόν τον τρόπο, επιτρέπεται η προσωρινή αποθήκευση ενός μεγαλύτερου αριθμού συνόλου τιμών ερωτήματος. Μπορείτε να διαβάσετε περισσότερα για τα hashes στο redis εδώ.
... mongoose.Query.prototype.cache = function(options = {})  'default'); return this; ; ...

Με αυτόν τον τρόπο, μπορούμε εύκολα να χρησιμοποιήσουμε το cache()ερώτημα μαζί με τα ερωτήματα που θέλουμε να αποθηκεύσουμε στην κρυφή μνήμη με τον ακόλουθο τρόπο.

... const blogs = await Blog .find({ user: req.params.user }) .cache({ key: req.params.user }); ...

Δημιουργία της λογικής της προσωρινής μνήμης

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

... mongoose.Query.prototype.exec = async function() { if (!this.enableCache) { console.log('Data Source: Database'); return exec.apply(this, arguments); } const key = JSON.stringify(Object.assign({}, this.getQuery(), { collection: this.mongooseCollection.name, })); const cachedValue = await client.hget(this.hashKey, key); if (cachedValue) { const parsedCache = JSON.parse(cachedValue); console.log('Data Source: Cache'); return Array.isArray(parsedCache) ? parsedCache.map(doc => new this.model(doc)) : new this.model(parsedCache); } const result = await exec.apply(this, arguments); client.hmset(this.hashKey, key, JSON.stringify(result), 'EX', 300); console.log('Data Source: Database'); return result; }; ...

Κάθε φορά που χρησιμοποιούμε το cache()ερώτημα μαζί με το κύριο ερώτημά μας, θέτουμε το enableCacheκλειδί να είναι αληθινό.

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

Χρησιμοποιούμε το collectionόνομα μαζί με το προεπιλεγμένο ερώτημα ως όνομα κλειδιού για χάρη της μοναδικότητας. Το κλειδί κατακερματισμού που χρησιμοποιείται είναι το όνομα του userοποίου έχουμε ήδη ορίσει νωρίτερα στον cache()ορισμό της συνάρτησης.

Τα προσωρινά αποθηκευμένα δεδομένα ανακτώνται χρησιμοποιώντας τη client.hget()συνάρτηση που απαιτεί το κλειδί κατακερματισμού και το συνακόλουθο κλειδί ως παραμέτρους.

Σημείωση: Χρησιμοποιούμε πάντα JSON.parse()κατά τη λήψη οποιωνδήποτε δεδομένων από την επανάληψη. Και ομοίως, χρησιμοποιούμε JSON.stringify()το κλειδί και τα δεδομένα πριν αποθηκεύσουμε οτιδήποτε στο redis. Αυτό γίνεται επειδή το redis δεν υποστηρίζει δομές δεδομένων JSON.

Μόλις λάβουμε τα προσωρινά αποθηκευμένα δεδομένα, πρέπει να μετατρέψουμε κάθε ένα από τα προσωρινά αποθηκευμένα αντικείμενα σε ένα μοντέλο μαγκόζης. Αυτό μπορεί να γίνει απλά χρησιμοποιώντας new this.model().

If the cache does not contain the required data, we make a query to the database. Then, having returned the data to the API, we refresh the cache using client.hmset(). We also set a default cache expiration time of 300 seconds. This is customizable based on your caching strategy.

The caching logic is in place. We have also set a default expiration time. Next up, we look at forcing cache expiration whenever a new blog post is created.

Forced Cache Expiration

In certain cases, such as when a user creates a new blog post, the user expects that the new post should be available when they fetche all the posts.

In order to do so, we have to clear the cache related to that user and update it with new data. So we have to force expiration. We can do that by invoking the del() function provided by redis.

... module.exports = { clearCache(hashKey) { console.log('Cache cleaned'); client.del(JSON.stringify(hashKey)); } } ...

We also have to keep in mind that we will be forcing expiration on multiple routes. One extensible way is to use this clearCache() as a middleware and call it once any query related to a route has finished execution.

const { clearCache } = require('../services/cache'); module.exports = async (req, res, next) => { // wait for route handler to finish running await next(); clearCache(req.body.user); } 

This middleware can be easily called on a particular route in the following way.

... blogsRouter.route('/') .post(cleanCache, async (req, res, next) => { ... } ...

And we are done. I agree that was a quite a lot of code. But with that last part, we have set up redis with our application and taken care of almost all the likely challenges. It is time to see our caching strategy in action.

Redis in Action

We make use of Postman as the API client to see our caching strategy in action. Here we go. Let's run through the API operations, one by one.

  1. We create a new blog post using the /blogs route

2. We then fetch all the blog posts related to user tejaz

3. Παίρνουμε για tejazάλλη μια φορά όλες τις αναρτήσεις ιστολογίου για τον χρήστη .

Μπορείτε να δείτε καθαρά ότι όταν φέρω από την κρυφή μνήμη, ο χρόνος που έχει περάσει κάτω από 409ms έως 24ms . Αυτό υπερφορτώνει το API σας μειώνοντας το χρόνο που απαιτείται κατά σχεδόν 95%.

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

Μπορείτε να βρείτε τον πλήρη πηγαίο κώδικα στον redis-expressφάκελο εδώ.

tarique93102 / article-snippets Αποθήκη που περιέχει πρωτότυπες εφαρμογές και αποσπάσματα κώδικα που σχετίζονται με τη διάδοση εννοιών - tarique93102 / article-snippets tarique93102 GitHub

συμπέρασμα

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

Μπορείτε να βρείτε το πλήρες σύνολο εντολών redis εδώ. Μπορείτε να το χρησιμοποιήσετε για redis-cliνα παρακολουθείτε τα δεδομένα της προσωρινής μνήμης και τις διαδικασίες εφαρμογής.

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

Εν τω μεταξύ, συνεχίστε την κωδικοποίηση.