Redis で Web API を強化する:実績のあるパフォーマンス最適化ガイド
タリク・エジャズ著
パフォーマンスは、ソフトウェアを設計するときに考慮すべき重要なパラメーターです。舞台裏で何が起こっているかに関しては特に重要です。
開発者および技術者として、私たちはパフォーマンスを向上させるために複数の調整や実装を採用しています。ここでキャッシングが登場します。
キャッシュは、必要なときにいつでもすぐにアクセスできる一時的な保存場所にデータやファイルを保存するメカニズムとして定義されます。
現在、キャッシュは Web アプリケーションに必須となっています。 Redis を使用すると、Node.js と MongoDB を使用して構築された Web API を強化できます。
「キャッシュは 100 ~ 200 年後も非常に重要な役割を果たすようです。」
Redis:素人向けの概要
公式ドキュメントによると、Redis はデータベース、メッセージ ブローカー、またはキャッシュ ストレージとして使用されるメモリ内データ構造ストアとして定義されています。文字列、ハッシュ、リスト、セット、範囲クエリを使用したソートされたセット、ビットマップ、ハイパーログログ、RADIUS クエリおよびストリームを使用した地理空間インデックスなどのデータ構造をサポートします。
さて、そこには非常に多くのデータ構造があります。簡単にするために、サポートされているほとんどすべてのデータ構造は、いずれかの形式の文字列に凝縮できます。実装を進めていくと、さらに明確になるでしょう。
しかし、一つだけ明らかなことがある。 Redis は強力であり、適切に使用すると、アプリケーションを高速化するだけでなく、驚くほど効率的にすることができます。話はもう十分です。手を汚してみましょう。
コードについて話しましょう
始める前に、ローカル システムで Redis をセットアップする必要があります。この簡単なセットアップ プロセスに従って、redis を起動して実行できます。
完了しましたか?いいね。始めましょう。 Express で作成したシンプルなアプリケーションでは、MongoDB Atlas のインスタンスを利用してデータの読み取りと書き込みを行います。
/blogs で作成された 2 つの主要な API があります。 ルート ファイル。
...
// 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 を変換する関数 コールバックの代わりに Promise を返す関数。 promisification について詳しく読むことができます。 ここ。
Redis 接続が確立されています。さらにキャッシュ コードを書き始める前に、一歩下がって、満たす必要がある要件と直面する可能性のある課題を理解してみましょう。
私たちのキャッシュ戦略は、次の点に対処できる必要があります。
- 特定のユーザーのすべてのブログ投稿に対するリクエストをキャッシュする
-
新しいブログ投稿が作成されるたびにキャッシュをクリアします
戦略を進める際に注意しなければならない可能性のある課題は次のとおりです。
-
キャッシュ データを保存するためのキーの作成を処理する正しい方法
- キャッシュの有効期限ロジックとキャッシュの鮮度を維持するための強制有効期限
- キャッシュ ロジックの再利用可能な実装
わかりました。ポイントを書き留めて、redis を接続しました。次のステップに進みます。
デフォルトの Mongoose Exec 関数をオーバーライドする
キャッシュ ロジックを再利用できるようにしたいと考えています。また、再利用できるだけでなく、データベースにクエリを実行する前の最初のチェックポイントにもしたいと考えています。これは、mongoose の exec 関数に便乗するという単純なハックを使用することで簡単に実行できます。
...
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 を渡す機能を提供します。 options を通じてハッシュキーとして使用されます。 オブジェクト。
注: ハッシュキーは、ハッシュ データ構造の識別子として機能します。平たく言えば、一連のキーと値のペアの親キーとして表現できます。これにより、より多くのクエリ値セットをキャッシュできるようになります。 Redis のハッシュについて詳しくは、こちらをご覧ください。
...
mongoose.Query.prototype.cache = function(options = {}) {
this.enableCache = true;
this.hashKey = JSON.stringify(options.key || 'default');
return this;
};
...
そうすることで、cache(<options argument>) を簡単に使用できるようになります。 次の方法で、クエリとキャッシュするクエリを追加します。
...
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 を設定します。 真実であるための鍵。
キーが false の場合は、メインの exec を返します。 デフォルトとしてクエリを実行します。そうでない場合は、まずキャッシュ データを取得して保存/更新するためのキーを形成します。
collection を使用します。 一意性を確保するために、名前とデフォルトのクエリをキー名として使用します。使用されるハッシュキーは user の名前です。 これは cache() の前半ですでに設定済みです。 関数定義。
キャッシュされたデータは、client.hget() を使用して取得されます。 パラメータとしてハッシュキーと結果のキーを必要とする関数。
注: 常に JSON.parse() を使用します。 Redis からデータをフェッチしている間。同様に、JSON.stringify() を使用します。 Redis に何かを保存する前に、キーとデータを確認します。 Redis は JSON データ構造をサポートしていないため、これが行われます。
キャッシュされたデータを取得したら、キャッシュされた各オブジェクトをマングース モデルに変換する必要があります。これは、new this.model(<object>) を使用するだけで実行できます。 。
キャッシュに必要なデータが含まれていない場合は、データベースにクエリを実行します。次に、データを API に返した後、client.hmset() を使用してキャッシュを更新します。 。また、デフォルトのキャッシュ有効期限を 300 秒に設定しました。これは、キャッシュ戦略に基づいてカスタマイズ可能です。
キャッシュロジックが整備されています。デフォルトの有効期限も設定しました。次に、新しいブログ投稿が作成されるたびにキャッシュの有効期限を強制する方法について見ていきます。
キャッシュの強制期限切れ
ユーザーが新しいブログ投稿を作成する場合など、特定のケースでは、ユーザーはすべての投稿を取得したときに新しい投稿が利用可能になることを期待します。
そのためには、そのユーザーに関連するキャッシュをクリアし、新しいデータで更新する必要があります。したがって、有効期限を強制する必要があります。 del() を呼び出すことでこれを行うことができます。 redisが提供する機能です。
...
module.exports = {
clearCache(hashKey) {
console.log('Cache cleaned');
client.del(JSON.stringify(hashKey));
}
}
...
また、複数のルートで有効期限を強制することにも留意する必要があります。拡張可能な方法の 1 つは、この clearCache() を使用することです。 ミドルウェアとして使用し、ルートに関連するクエリの実行が完了したら呼び出します。
const { clearCache } = require('../services/cache');
module.exports = async (req, res, next) => {
// wait for route handler to finish running
await next();
clearCache(req.body.user);
}
このミドルウェアは、次の方法で特定のルートで簡単に呼び出すことができます。
...
blogsRouter.route('/')
.post(cleanCache, async (req, res, next) => {
...
}
...
これで完了です。かなりの量のコードだったことに同意します。しかし、最後の部分では、アプリケーションで Redis をセットアップし、起こりそうなほぼすべての課題に対処しました。キャッシュ戦略が実際に動作しているのを見てみましょう。
Redis の動作
API クライアントとして Postman を使用して、実際のキャッシュ戦略を確認します。さぁ行こう。 API オペレーションを 1 つずつ見てみましょう。
<オル>/blogs を使用して新しいブログ投稿を作成します ルート
新しいブログ投稿の作成
tejaz に関連するすべてのブログ投稿を取得します。
ユーザー tejaz のすべてのブログ投稿を取得しています
tejaz のすべてのブログ投稿を取得します もう一度。
ユーザー tejaz のすべてのブログ投稿をもう一度取得します
キャッシュからフェッチすると、 所要時間が409 ミリ秒から短縮されていることがはっきりとわかります。 〜 24 ミリ秒 。これにより、所要時間がほぼ 95% 短縮され、API が大幅に強化されます。
さらに、キャッシュの有効期限と更新操作が期待どおりに機能していることが明確にわかります。
完全なソース コードは redis-express にあります。 フォルダはここにあります。
結論
キャッシュは、パフォーマンス効率が高くデータ集約型のアプリケーションにとって必須の手順です。 Redis は、Web アプリケーションでこれを簡単に実現するのに役立ちます。これは非常に強力なツールであり、適切に使用すれば、開発者だけでなくユーザー全体に優れたエクスペリエンスを提供できることは間違いありません。
Redis コマンドの完全なセットはここで見つけることができます。 redis-cli で使用できます。 キャッシュ データとアプリケーション プロセスを監視します。
特定のテクノロジーが提供する可能性は本当に無限です。ご質問がございましたら、[LinkedIn](https://www.linkedin.com/in/tarique-ejaz/) までご連絡ください。 。
それまでの間、コーディングを続けてください。
無料でコーディングを学びましょう。 freeCodeCamp のオープンソース カリキュラムは、40,000 人以上の人々が開発者としての職に就くのに役立ちました。始めましょう
-
Upstash を使用して強力な RAG チャットボットを作成する:ステップバイステップ ガイド
この投稿では、Upstash Vector、Upstash Redis、Hugging Face Inference API、複製 LLAMA-2-70B チャット モデル、および Vercel を使用して、オープンソースのカスタム コンテンツ RAG チャットボットを構築した方法について説明します。 Upstash Vector は、ベクターの挿入とクエリ、各ユーザー メッセージの関連コンテキストの動的作成または更新に役立ち、Upstash Redis はチャットボットの会話の保存に役立ちました。 前提条件 次のものが必要です。 Node.js 18 以降 Upstash アカウント ハ
-
Grafana用のRedisデータソースプラグインの紹介
Grafanaは、広く使用されている有名なオープンソースアプリケーション監視ツールです。そして今、Grafanaプラグイン用の新しいRedisデータソースのおかげで、Redisで動作します! この新機能により、DevOpsの実践者とデータベース管理者は、使い慣れたツールを使用して、Redisデータベースとアプリケーションデータを監視するためのダッシュボードを簡単に作成できます。新しいGrafanaRedisデータソースプラグインを使用すると、RedisTimeSeriesデータと、文字列、ハッシュ、セットなどのコアRedisデータ型を視覚化できます。また、 SLOWLOG GETなどのRe