Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する

大規模な Web アプリケーションを構築する場合、速度が最優先事項になります。ユーザーは応答を長く待つことをもう望んでいませんし、そうすべきではありません。ただし、一部のプロセスには時間がかかり、これ以上速くしたり削除したりすることはできません。

メッセージ キューは、通常の要求と応答の過程に追加の分岐を提供することで、この問題の解決に役立ちます。この追加のブランチにより、ユーザーはすぐに応答を得ることができ、時間のかかるプロセスを側で実行できるようになります。みんな満足して家に帰ります。

この記事では、メッセージ キューとは何か、そして非常に単純なアプリケーションを構築してメッセージ キューの使用を開始する方法について説明することに重点を置きます。 Node.js の基本を理解しており、Redis をローカルまたはクラウド インスタンスにインストールしておく必要があります。 Redis のインストール方法については、こちらをご覧ください。

キューとは何ですか?

キューは、エンティティを順序に従って保存できるデータ構造です。キューは先入れ先出し (FIFO) 原理を使用します。

コンピューターサイエンスにおける行列の概念は、人々が物を手に入れるために並ぶ日常生活における行列の概念と同じです。後ろから列に加わり、順番が来るまで待ち、対応が完了したら前から列を離れます。

コンピューター サイエンスでは、API リクエストなどのプロセスが実行中で、現在のフローから特定のタスク (メールの送信など) を削除する必要がある場合、そのタスクをキューにプッシュしてプロセスを続行します。

以下の図は、キューのライフサイクルを示しています。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する キューのライフサイクル | https://optimalbits.github.io/bull/

ジョブとは何ですか?

ジョブとは、キューで使用されるデータの一部であり、通常は JSON のようなオブジェクトです。

この記事の表紙画像に示されているように、仕事は空港で列に並んでいる各人として考えることができます。各人は、自分の順番が来たときに役立つ特定のデータとその他の指示 (パスポートや必要に応じて医療書類) が入ったブリーフケースを持っています。

この列に新しく加わる人は最後尾から入り、前から順番に対応していきます。これがジョブの処理方法であり、各ジョブにはその処理に使用されるデータが含まれています。新しいジョブは後ろから追加され、ジョブは前から削除されます。

ジョブプロデューサーとは何ですか?

ジョブ プロデューサーは、ジョブをキューに追加するコードの一部です。現実世界では、これは人々に指示を与え、さまざまな目的でどの列に加わるべきかを指示する空港の警備員です。

ジョブプロデューサーは、ジョブコンシューマーから独立して存在できます。これは、マイクロサービスのセットアップでは、特定のサービスはジョブをキューに追加することのみを考慮し、その後のジョブの処理方法には関心がない可能性があることを意味します。

ワーカー (ジョブコンシューマ) とは何ですか?

ワーカーまたはジョブ コンシューマは、ジョブを実行できるプロセスまたは関数です。労働者を、銀行の列に並んでいる人々に対応する銀行の出納係として考えてみましょう。最初の人が入ってきたら、その人は唯一の行列に加わります。その後、レジ係が彼らを呼び出すと、列は空になります。

レジ担当者は、取引を処理するために使用する特定の詳細情報をその人に要求します。レジ係がその顧客に対応している間に、さらに 4 人の顧客が並ぶ可能性があります。彼らは、レジ係が最初の顧客の対応を終えるまで、次の顧客を呼ぶ前に列に残ります。これはキュー ワーカーのプロセスと同じです。キュー ワーカーはキュー内の最初のジョブを選択して、それを処理します。

失敗したジョブとは何ですか?

多くの場合、一部のジョブは処理中に失敗することがあります。

ジョブが失敗する可能性がある理由は次のとおりです。

  • 入力データが無効または欠落している:ジョブの処理に必要なデータが欠落している場合、ジョブは失敗します。たとえば、受信者のメール アドレスがないと、メールを送信するジョブは失敗します。
  • タイムアウト:ジョブに通常より時間がかかる場合、キュー メカニズムによってジョブが失敗する可能性があります。これはジョブの依存関係などの問題が原因である可能性がありますが、通常は単一のジョブを永久に実行することは望ましくありません。
  • ネットワークまたはインフラストラクチャの問題:これらの問題はほとんど制御できませんが、実際に発生します。たとえば、データベース接続エラーが発生すると、ジョブは失敗します。
  • 依存関係の問題:ジョブが適切に機能するために、外部リソースに依存する場合があります。これらの他のリソースが利用できないか失敗すると、ジョブは失敗します。

ジョブが失敗した場合、ジョブを再試行するようにキュー メカニズムを構成できます。ジョブをすぐに再試行することも、計算された時間が経過した後に再試行することもできます。最大試行回数を設定できます。これをお勧めします。そうしないと、常に無限に失敗するジョブを実行することになります。

キューは、マイクロサービス間の堅牢な通信チャネルを作成するのに役立ちます。複数のサービスが同じキューを使用できます。異なるサービスには異なる問題が課せられる可能性があります。サービスがタスクを完了すると、ワーカーがそのジョブを待機している別のサービスにジョブをプッシュできます。そのサービスはそれを取得し、そのデータに対して必要な処理を行います。

キューは、プロセスから重いタスクをオフロードするのにも役立ちます。この記事で説明するように、メールの送信などの時間のかかるタスクは、応答時間の低下を避けるためにキューに入れることができます。

キューは単一障害点を回避するのに役立ちます。失敗する可能性があり、再試行できるプロセスは、しばらくしてから再試行できるキューを使用して処理するのが最適です。

キューを使用する簡単なアプリケーションを構築する方法

この記事では、Node.js と Redis を使用して簡単なプロジェクトを構築します。 Bull ライブラリを使用すると、キュー システムの構築に伴う複雑な作業の多くが簡素化されます。プロジェクトには、電子メールを送信するための単一のエンドポイントがあります。

新しい Node.js プロジェクトを作成する

mkdir nodejs-queue-project
cd nodejs-queue-project
npm init -y

上記のコマンドは、nodejs-queue-project という名前の新しいフォルダーを作成します。 と package.json その中のファイル。 package.json ファイルは次のようになります:

{
 "name": "nodejs-queue-project",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC"
}

必要な依存関係をインストールする

npm i express @types/express @types/node body-parser ts-node ts-lint typescript nodemon nodemailer @types/nodemailer

上記のコマンドは、プロジェクトに必要なさまざまなパッケージと依存関係をインストールします。

インストール後、scripts を更新できます。 package.json のセクション dev を持つには コマンド。 package.json 全体 ファイルは次のようになっているはずです:

{
 "name": "nodejs-queue-project",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
 "dev": "nodemon src/app.ts"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
 "@types/express": "^4.17.17",
 "@types/node": "^20.3.3",
 "@types/nodemailer": "^6.4.8",
 "body-parser": "^1.20.2",
 "express": "^4.18.2",
 "nodemailer": "^6.9.3",
 "nodemon": "^2.0.22",
 "ts-lint": "^4.5.1",
 "ts-node": "^10.9.1",
 "typescript": "^5.1.6"
 }
}

上記のファイルには、インストールされているすべての依存関係が表示されます。 npm run dev dev を使用するとコマンドが実行されます。 スクリプト。

エンドポイントの構築方法

まず最初に、src という名前の新しいフォルダーを作成します。 。このフォルダーにはすべてのコード ファイルが含まれます。これに含まれる最初のファイルはアプリケーションのルート ファイル、app.ts です。 package.json で定義されているファイル ファイル。

app.ts を使用します。 ファイルを使用して必要なパッケージをインポートし、以下に示すように電子メールを送信する単一のエンドポイントを持つ単純なサーバーを作成します。

import express from "express";
import bodyParser from "body-parser";
import nodemailer from "nodemailer";
const app = express();
app.use(bodyParser.json());
app.post("/send-email", async (req, res) => {
 const { from, to, subject, text } = req.body;
 // Use a test account as this is a tutorial
 const testAccount = await nodemailer.createTestAccount();
 const transporter = nodemailer.createTransport({
 host: "smtp.ethereal.email",
 port: 587,
 secure: false,
 auth: {
 user: testAccount.user,
 pass: testAccount.pass,
 },
 tls: {
 rejectUnauthorized: false,
 },
 });
 console.log("Sending mail to %s", to);
 let info = await transporter.sendMail({
 from,
 to,
 subject,
 text,
 html: `<strong>${text}</strong>`,
 });
 console.log("Message sent: %s", info.messageId);
 console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
 res.json({
 message: "Email Sent",
 });
});
app.listen(4300, () => {
 console.log("Server started at http://localhost:4300");
});

これで、npm run dev を実行してサーバーを起動できます。 あなたの端末で。 Server started at [http://localhost:4300](http://localhost:4300) というメッセージが表示されるはずです。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する npm run dev メッセージ

Postman などのツールを使用してエンドポイントをテストできるようになりました。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する Postman でのエンドポイント テスト

スクリーンショットに示されているように、リクエストには約 4 秒かかりました。これはエンドポイントとしては非常に遅いです。端末を見ると、送信されたメールをプレビューできる URL も表示されるはずです。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する

リンクを開くと、メールがどのように表示されるかを確認できます。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する メールの内容

キューの作成方法

プロセスをさらに高速化するために、メールをキューに入れて後で送信し、応答をすぐにユーザーに送信することができます。

これを行うには、bull をインストールします。 ライブラリとその @types キューを作成するために使用するライブラリです。つまり:

npm i bull @types/bull

bull を使用して新しいキューを作成する 新しい Bull をインスタンス化するのと同じくらい簡単です キューの名前を持つオブジェクト:

// This goes at the top of your file
import Bull from 'bull';
const emailQueue = new Bull("email");

キュー名のみを使用してキューが作成されると、デフォルトの Redis 接続 URL:localhost:6379 を使用しようとします。 。別の URL を使用したい場合は、2 番目のオブジェクトを Bull に渡すだけです。 クラスをオプション オブジェクトとして:

const emailQueue = new Bull("email", {
 redis: "localhost:6379",
});

この時点で、ジョブ プロデューサーとして機能する単純な関数を作成し、リクエストが受信されるたびにジョブをキューに追加できます。

type EmailType = {
 from: string;
 to: string;
 subject: string;
 text: string;
};
const sendNewEmail = async (email: EmailType) => {
 emailQueue.add({ ...email });
};

この新しく作成された関数、sendNewEmail 、タイプ EmailType の、送信される新しい電子メールの詳細を含むオブジェクトを受け入れます。 。送信者のメールアドレス (from) があります )、受信者の電子メール アドレス (to) )、subject メールの内容とメールの内容 (text) )。次に、新しいジョブをキューにプッシュします。

リクエスト中に電子メールを送信する代わりに、この機能を使用できます。これを行うためにエンドポイントを変更します。

app.post("/send-email", async (req, res) => {
 const { from, to, subject, text } = req.body;
 await sendNewEmail({ from, to, subject, text });
 console.log("Added to queue");
 res.json({
 message: "Email Sent",
 });
});

この時点で、コードはより単純になり、プロセスはより速くなります。リクエストにかかる時間はわずか約 40 メートルで、以前よりも約 100 倍速くなります。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する Postman を使用したエンドポイント テスト

この時点で、電子メールはキューに追加されます。処理されるまでキューに残ります。ジョブは、同じアプリケーションまたは別のサービス (マイクロサービス設定の場合) によって処理できます。

ジョブの処理方法

メールがキューから出ない場合、サイクルは不完全で役に立ちません。ジョブを処理し、キューをクリアするジョブ コンシューマを作成します。

これを行うには、Job を受け入れる関数のロジックを作成します。 反対して電子メールを送信します:

const processEmailQueue = async (job: Job) => {
 // Use a test account as this is a tutorial
 const testAccount = await nodemailer.createTestAccount();
 const transporter = nodemailer.createTransport({
 host: "smtp.ethereal.email",
 port: 587,
 secure: false,
 auth: {
 user: testAccount.user,
 pass: testAccount.pass,
 },
 tls: {
 rejectUnauthorized: false,
 },
 });
 const { from, to, subject, text } = job.data;
 console.log("Sending mail to %s", to);
 let info = await transporter.sendMail({
 from,
 to,
 subject,
 text,
 html: `<strong>${text}</strong>`,
 });
 console.log("Message sent: %s", info.messageId);
 console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
 return nodemailer.getTestMessageUrl(info);
};

上記の関数は Job を受け入れます。 オブジェクト。このオブジェクトには、ジョブのステータスとデータを示す便利なプロパティがあります。ここでは、data を使用します。 財産。

この時点では、関数しかありません。どのキューを処理すべきかわからないため、ジョブは自動的に選択されません。

キューに接続する前に、いくつかのリクエストを送信して、いくつかのジョブをキューに追加できます。 redis-cli でこのコマンドを実行すると、現在キューに入れられている電子メール ジョブを確認できます。 :

LRANGE bull:email:wait 0 -1

これにより、電子メールの待機リストがチェックされ、ids が返されます。 待機中のジョブの数。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する Redis CLI

労働者が実際にどのように働くかを示すためだけに、いくつかのジョブを作成しました。

次に、次のコード行を追加して、ワーカーをキューに接続します。

emailQueue.process(processEmailQueue);

これはあなたの app.ts です。 ファイルはその後を処理する必要があります:

import express from "express";
import bodyParser from "body-parser";
import nodemailer from "nodemailer";
import Bull, { Job } from "bull";
const app = express();
app.use(bodyParser.json());
const emailQueue = new Bull("email", {
 redis: "localhost:6379",
});
type EmailType = {
 from: string;
 to: string;
 subject: string;
 text: string;
};
const sendNewEmail = async (email: EmailType) => {
 emailQueue.add({ ...email });
};
const processEmailQueue = async (job: Job) => {
 // Use a test account as this is a tutorial
 const testAccount = await nodemailer.createTestAccount();
 const transporter = nodemailer.createTransport({
 host: "smtp.ethereal.email",
 port: 587,
 secure: false,
 auth: {
 user: testAccount.user,
 pass: testAccount.pass,
 },
 tls: {
 rejectUnauthorized: false,
 },
 });
 const { from, to, subject, text } = job.data;
 console.log("Sending mail to %s", to);
 let info = await transporter.sendMail({
 from,
 to,
 subject,
 text,
 html: `<strong>${text}</strong>`,
 });
 console.log("Message sent: %s", info.messageId);
 console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
};
emailQueue.process(processEmailQueue);
app.post("/send-email", async (req, res) => {
 const { from, to, subject, text } = req.body;
 await sendNewEmail({ from, to, subject, text });
 console.log("Added to queue");
 res.json({
 message: "Email Sent",
 });
});
app.listen(4300, () => {
 console.log("Server started at http://localhost:4300");
});

保存すると、サーバーが再起動し、すぐにメールの送信が開始されることがわかります。これは、ワーカーがキューを認識してすぐに処理を開始するためです。

Node.js と Redis でメッセージ キューをマスターする:Web アプリのパフォーマンスを向上する サーバーがキューに入れられたメールを送信中

これで、プロデューサーとワーカーの両方がアクティブになりました。新しい API リクエストはすべてキューにプッシュされ、保留中のジョブがすでに存在しない限り、ワーカーは即座にそれを処理します。

概要

この記事が、メッセージ キューとは何か、ジョブを追加してそれらを実行するプロセスを作成する方法、およびメッセージ キューを使用してより優れた Web アプリケーションを構築する方法を理解するのに役立つことを願っています。この記事で使用されているコード ファイルは GitHub で見つけることができます。

ご質問や関連するアドバイスがございましたら、私に連絡して共有してください。

私の記事をもっと読んだり、私の作品をフォローしたりするには、LinkedIn、Twitter、Github で私とつながることができます。素早くて簡単、そして無料です!

無料でコーディングを学びましょう。 freeCodeCamp のオープンソース カリキュラムは、40,000 人以上の人々が開発者としての職に就くのに役立ちました。始めましょう


  1. RedisGraph 2.8がリリースされました!

    本日、RedisGraph2.8の一般提供リリースを発表できることをうれしく思います。このブログ投稿では、現在利用可能な主な新機能について詳しく説明しています。 RedisGraphについて RedisGraphは、Redis用の高性能でメモリファーストのグラフデータ構造です。 RedisGraphは、グラフのマルチテナンシーをサポートし(多数のグラフを同時に保持できます)、グラフに同時にアクセスする複数のクライアントにサービスを提供できます。 Redisスタックの一部としても利用できるようになりました。 RedisGraph2.8の主な新機能 より豊富なグラフモデル マルチラベルノード

  2. パフォーマンスの向上とコストの削減:Upstash Redis を使用して OpenAI API レスポンスをキャッシュする

    OpenAI API を使用したことがある方は、API が非常に遅く、場合によっては応答すらしないことに気づいたかもしれません。特に GPT-4 モデルは応答遅延が長くなる傾向があります。また、回答が得られるたびに料金が発生します。これらはすべて、応答を直接提供することを避ける理由です。 応答を Upstash Redis に保存すると、これらの問題を回避できます。多くのクライアントに同じ応答を提供している場合は、かなりのコストを節約できます。また、グローバル展開により、ユーザーができるだけ早く情報を確実に入手できるようになります。 この記事では、OpenAI API 応答を Upstas