A Redis használata a webes API-k feltöltéséhez

A teljesítmény elengedhetetlen paraméter, amelyet figyelembe kell venni bármilyen szoftver tervezésekor. Különösen fontos, ha a kulisszák mögött történik.

Mi, mint fejlesztők és technológusok, több fejlesztést és megvalósítást alkalmazunk a teljesítmény javítása érdekében. Itt játszik szerepet a gyorsítótár.

A gyorsítótárazás az adatok vagy fájlok ideiglenes tárolási helyen történő tárolásának mechanizmusa, ahonnan szükség esetén azonnal hozzáférhető.

A gyorsítótárazás napjainkban kötelezővé vált a webes alkalmazásokban. A Redis segítségével feltölthetjük webes APIinkat - amelyek a Node.js és a MongoDB segítségével épülnek fel.

Redis: Egy laikus áttekintése

A Redis a hivatalos dokumentáció szerint egy memóriában lévő adatszerkezet-tároló, amelyet adatbázisként, üzenetközvetítőként vagy gyorsítótárként használnak. Támogatja az olyan adatstruktúrákat, mint a karakterláncok, hashek, listák, halmazok, válogatott sorok, lekérdezésekkel, bitképek, hiperlogók, földrajzi térbeli indexek, sugár lekérdezésekkel és folyamokkal.

Oké, ez elég sok adatstruktúra. Az egyszerűség kedvéért szinte az összes támogatott adatstruktúra összefűzhető a húrok egyik vagy másik formájába. Nagyobb egyértelműségre tesz szert, amikor végigvezetjük a megvalósítást.

De egy dolog világos. A Redis erőteljes, és megfelelő használat esetén alkalmazásaink nemcsak gyorsabbak, de elképesztően hatékonyak is lehetnek. Elég beszéd. Piszkítsuk be a kezünket.

Beszéljünk kóddal

Mielőtt nekilátnánk, újra be kell állítania a helyi rendszert. Kövesse ezt a gyors telepítési folyamatot az újbóli működéshez.

Kész? Menő. Kezdjük. Van egy egyszerű alkalmazásunk, amelyet az Express-ben készítettünk, amely a MongoDB Atlas példányát használja adatok olvasására és írására.

Két fő API-t hoztunk létre az /blogsútvonalfájlban.

... // 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', }); }); ...

Meghintve néhány Redis jóságot

Először az npm csomag letöltésével rediskapcsolódhatunk a helyi redis szerverhez.

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); ...

A utils.promisifyfüggvény segítségével átalakítjuk a client.hgetfüggvényt, így visszahívás helyett ígéretet adunk vissza. Bővebben promisificationitt olvashat .

A Redis kapcsolat megvan. Mielőtt elkezdenénk írni a gyorsítótár-kódot, tegyünk egy lépést hátra, és próbáljuk megérteni, milyen követelményeknek kell megfelelnünk és milyen kihívásokkal nézhetünk szembe.

Gyorsítótárazott stratégiánknak képesnek kell lennie a következő pontok kezelésére.

  • Gyorsítótárazzuk az összes felhasználó összes blogbejegyzésére vonatkozó kérést
  • A gyorsítótár törlése minden új blogbejegyzés létrehozásakor

A várható kihívások, amelyekkel körültekintően kell eljárnunk stratégiánk során, a következők:

  • A gyorsítótár-adatok tárolásának kulcskészítésének megfelelő módja
  • A gyorsítótár lejárati logikája és a kényszerített lejárat a gyorsítótár frissességének fenntartása érdekében
  • A gyorsítótár-logika újrafelhasználható megvalósítása

Rendben. Pontjainkat feljegyeztük és újracsatlakoztattuk. Folytassa a következő lépéssel.

Az alapértelmezett Mongoose Exec funkció felülírása

Azt akarjuk, hogy gyorsítótár-logikánk újrafelhasználható legyen. És nem csak újrafelhasználható, hanem azt is szeretnénk, hogy ez legyen az első ellenőrző pont, mielőtt bármilyen lekérdezést tennénk az adatbázisba. Ez könnyen megoldható a mongúz exec funkció egyszerű malackás háttámlájával.

... 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; } ...

A mongúz prototípus objektumát használjuk a gyorsítótárazott logikai kód hozzáadásához a lekérdezés első végrehajtásaként.

Gyorsítótár hozzáadása lekérdezésként

Annak érdekében, hogy jelöljük, mely lekérdezéseket kell felvenni a gyorsítótárba, létrehozunk egy mongúz lekérdezést. Lehetőséget biztosítunk arra, hogy a userhash-kulcsként használandó optionsobjektumot átadjuk az objektumon.

Megjegyzés: A Hashkey egy hash adatstruktúra azonosítójaként szolgál, amely laikus értelemben meghatározható szülő kulcsként egy kulcs-érték pár halmazhoz. Ezáltal nagyobb számú lekérdezési értékkészlet gyorsítótárazásának engedélyezése. A hasisokról a redisben itt olvashat bővebben.
... mongoose.Query.prototype.cache = function(options = {})  'default'); return this; ; ...

Ezt cache()követően a következő módon könnyedén felhasználhatjuk a lekérdezést a gyorsítótárba tenni kívánt lekérdezésekkel együtt.

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

A gyorsítótár-logika elkészítése

Felállítottunk egy közös, újrafelhasználható lekérdezést annak jelölésére, hogy mely lekérdezéseket kell gyorsítótárba helyezni. Menjünk előre, és írjuk meg a központi gyorsítótár-logikát.

... 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; }; ...

Amikor a cache()lekérdezést a fő lekérdezésünkkel együtt használjuk , a enableCachekulcsot igazra állítjuk .

Ha a kulcs hamis, akkor a fő execlekérdezést alapértelmezésként visszaküldjük . Ha nem, akkor először a kulcsot formázzuk a gyorsítótár adatainak beolvasásához és tárolásához / frissítéséhez.

A collectionnevet az alapértelmezett lekérdezéssel együtt használjuk kulcsnévként az egyediség kedvéért. A használt hash-kulcs annak a neve, useramelyet már korábban beállítottunk a cache()függvénydefinícióban.

A gyorsítótárazott adatokat a client.hget()függvény használatával kapjuk meg, amelyhez a hash-kulcs és az ebből következő kulcs szükséges.

Megjegyzés: Mindig használunk, JSON.parse()miközben bármilyen adatot lekérünk a redis-ből. És hasonlóan használjuk JSON.stringify()a kulcsot és az adatokat, mielőtt bármit is tárolnánk a redis-be. Ez azért történik, mert a redis nem támogatja a JSON adatstruktúrákat.

A gyorsítótárazott adatok megszerzése után a gyorsítótárban tárolt objektumokat mongúz modellekké kell átalakítanunk. Ez egyszerűen felhasználható 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. Még tejazegyszer lekérjük az összes blogbejegyzést a felhasználók számára .

Jól látható, hogy amikor a gyorsítótárból keresünk, az eltelt idő 409 ms- ról 24 ms- ra csökkent . Ez az API-t úgy tölti fel, hogy az elvárt időt csaknem 95% -kal csökkenti.

Ráadásul jól láthatjuk, hogy a gyorsítótár lejárata és a frissítési műveletek a várakozásoknak megfelelően működnek.

A teljes forráskódot az redis-expressitt található mappában találja meg.

tarique93102 / article-snippets Tárház, amely prototípus alkalmazásokat és a koncepciók terjesztésével kapcsolatos kódrészleteket tartalmaz - tarique93102 / article- snippets tarique93102 GitHub

Következtetés

Caching is a mandatory step for any performance-efficient and data-intensive application. Redis helps you easily achieve this in your web applications. It is a super powerful tool, and if used properly it can definitely provide an excellent experience to developers as well as users all around.

You can find the complete set of redis commands here. You can use it with redis-cli to monitor your cache data and application processes.

The possibilities offered by any particular technology is truly endless. If you have any queries, you can reach out to me on LinkedIn.

In the mean time, keep coding.