Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Redis

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web

Khi bạn đang xây dựng các ứng dụng web quy mô lớn, tốc độ là ưu tiên hàng đầu. Người dùng không muốn đợi lâu để nhận được phản hồi nữa và họ cũng không cần phải làm vậy. Tuy nhiên, một số quy trình cần có thời gian và không thể thực hiện nhanh hơn hoặc loại bỏ chúng được nữa.

Hàng đợi tin nhắn giúp giải quyết vấn đề này bằng cách cung cấp một nhánh bổ sung cho hành trình phản hồi yêu cầu thông thường. Nhánh bổ sung này giúp đảm bảo người dùng có thể nhận được phản hồi ngay lập tức và các quy trình tốn thời gian có thể được thực hiện ngay lập tức. Mọi người về nhà vui vẻ.

Bài viết này sẽ tập trung giải thích hàng đợi tin nhắn là gì và cách bắt đầu với chúng bằng cách xây dựng một ứng dụng rất đơn giản. Bạn nên làm quen với kiến ​​thức cơ bản về Node.js và bạn nên cài đặt Redis cục bộ hoặc trên phiên bản đám mây. Tìm hiểu cách cài đặt Redis tại đây.

Hàng đợi là gì?

Hàng đợi là cấu trúc dữ liệu cho phép bạn lưu trữ các thực thể theo thứ tự. Hàng đợi sử dụng nguyên tắc nhập trước xuất trước (FIFO).

Khái niệm xếp hàng trong khoa học máy tính cũng giống như khái niệm xếp hàng trong cuộc sống hàng ngày, nơi mọi người xếp hàng để lấy đồ. Bạn xếp hàng từ phía sau, đợi đến lượt mình rồi rời khỏi hàng từ phía trước sau khi đã có người phục vụ.

Trong khoa học máy tính, khi một quy trình như yêu cầu API đang chạy và bạn cần xóa một tác vụ nhất định (chẳng hạn như gửi email) khỏi luồng hiện tại, bạn sẽ đẩy tác vụ đó vào hàng đợi và tiếp tục quá trình.

Sơ đồ bên dưới minh họa vòng đời của hàng đợi:

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Vòng đời hàng đợi | https://optimalbits.github.io/bull/

Công việc là gì?

Công việc là bất kỳ phần dữ liệu nào được sử dụng trên hàng đợi, thường là đối tượng giống JSON.

Như được minh họa trong ảnh bìa của bài viết này, bạn có thể hình dung công việc giống như mỗi người đang xếp hàng tại sân bay. Mỗi người cầm một chiếc cặp chứa dữ liệu cụ thể và các hướng dẫn khác (hộ chiếu và có thể cả giấy tờ y tế nếu được yêu cầu) sẽ hữu ích khi đến lượt họ được chăm sóc.

Những người mới tham gia hàng đợi này sẽ vào từ phía sau (với tư cách là người cuối cùng) và mọi người sẽ được chăm sóc từ phía trước. Đó là cách các công việc cũng được xử lý, mỗi công việc chứa dữ liệu sẽ được sử dụng để xử lý nó. Công việc mới được thêm vào từ phía sau trong khi công việc được đưa ra từ phía trước.

Người tạo việc làm là gì?

Trình tạo công việc là bất kỳ đoạn mã nào thêm công việc vào hàng đợi. Trong đời thực, đây sẽ là nhân viên bảo vệ tại sân bay đưa ra chỉ dẫn cho mọi người, cho họ biết nên xếp hàng nào cho các mục đích khác nhau.

Người tạo việc làm có thể tồn tại độc lập với người tiêu dùng việc làm. Điều này có nghĩa là trong quá trình thiết lập vi dịch vụ, một dịch vụ cụ thể có thể chỉ liên quan đến việc thêm công việc vào hàng đợi chứ không phải cách chúng được xử lý sau đó.

Công nhân (Người tiêu dùng việc làm) là gì?

Một công nhân hoặc người tiêu dùng công việc là một quy trình hoặc chức năng có thể thực hiện một công việc. Hãy nghĩ về một công nhân như một nhân viên thu ngân ngân hàng đang chăm sóc những người đang xếp hàng tại ngân hàng. Khi người đầu tiên bước vào, họ sẽ xếp hàng với tư cách là người duy nhất trong hàng đợi. Nhân viên thu ngân sau đó gọi cho họ và hàng đợi trống rỗng.

Nhân viên thu ngân yêu cầu các chi tiết cụ thể sẽ được sử dụng để xử lý giao dịch từ người đó. Trong khi nhân viên thu ngân đang phục vụ khách hàng đó, bốn khách hàng khác có thể đã xếp hàng. Họ sẽ vẫn xếp hàng cho đến khi nhân viên thu ngân làm xong việc với khách hàng đầu tiên trước khi gọi cho khách hàng tiếp theo. Đây là quy trình tương tự với nhân viên xếp hàng — họ chọn công việc đầu tiên trong hàng đợi và xử lý nó.

Công việc thất bại là gì?

Thông thường, một số công việc có thể bị lỗi trong quá trình xử lý.

Dưới đây là một số lý do khiến công việc có thể thất bại:

  • Dữ liệu đầu vào không hợp lệ hoặc bị thiếu:Khi thiếu dữ liệu cần thiết cho công việc cần xử lý, công việc sẽ không thành công. Ví dụ:công việc gửi email sẽ không thành công nếu không có địa chỉ email của người nhận.
  • Hết thời gian chờ:Cơ chế xếp hàng có thể thất bại nếu công việc đó mất nhiều thời gian hơn bình thường. Điều này có thể là do sự cố phụ thuộc vào công việc hoặc điều gì khác, nhưng thông thường bạn không muốn một công việc duy nhất chạy mãi.
  • Sự cố mạng hoặc cơ sở hạ tầng:Những sự cố này gần như nằm ngoài tầm kiểm soát của bạn nhưng vẫn xảy ra. Ví dụ:lỗi kết nối cơ sở dữ liệu sẽ khiến công việc thất bại.
  • Vấn đề phụ thuộc:Đôi khi một công việc dựa vào một số tài nguyên bên ngoài để hoạt động tốt. Bất cứ khi nào những tài nguyên khác này không có sẵn hoặc không thành công thì công việc sẽ thất bại.

Khi công việc không thành công, bạn có thể định cấu hình cơ chế xếp hàng của mình để thử lại chúng. Bạn có thể thử lại công việc ngay lập tức hoặc sau một khoảng thời gian được tính toán. Bạn có thể đặt số lần thử tối đa theo khuyến nghị. Nếu không, bạn sẽ phải làm một công việc luôn thất bại.

Hàng đợi rất hữu ích trong việc tạo các kênh liên lạc mạnh mẽ giữa các vi dịch vụ. Nhiều dịch vụ có thể sử dụng cùng một hàng đợi. Các dịch vụ khác nhau có thể được giao nhiệm vụ với các vấn đề khác nhau. Khi một dịch vụ hoàn thành nhiệm vụ của mình, nó có thể đẩy một công việc sang một dịch vụ khác có nhân viên đang chờ công việc đó. Dịch vụ đó sẽ xử lý dữ liệu và làm bất cứ điều gì cần thiết với dữ liệu.

Hàng đợi cũng hữu ích cho việc giảm tải các tác vụ nặng nề từ một quy trình. Như bạn sẽ thấy trong bài viết này, một tác vụ tốn thời gian như gửi email có thể được xếp hàng đợi để tránh làm chậm thời gian phản hồi.

Hàng đợi giúp tránh các điểm lỗi duy nhất. Một quy trình có khả năng thất bại và có thể thử lại được xử lý tốt nhất bằng cách sử dụng hàng đợi nơi nó có thể được thử lại sau một thời gian.

Cách xây dựng một ứng dụng đơn giản sử dụng hàng đợi

Trong bài viết này, chúng ta sẽ xây dựng một dự án đơn giản bằng Node.js và Redis. Chúng ta sẽ sử dụng thư viện Bull vì nó đơn giản hóa rất nhiều vấn đề phức tạp liên quan đến việc xây dựng hệ thống xếp hàng. Dự án sẽ có một điểm cuối duy nhất để gửi email.

Tạo dự án Node.js mới

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

Các lệnh trên sẽ tạo một thư mục mới có tên nodejs-queue-project và một package.json tập tin trong đó. package.json tập tin sẽ trông như thế này:

{
 "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"
}

Cài đặt các phần phụ thuộc bắt buộc

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

Các lệnh trên sẽ cài đặt các gói và phần phụ thuộc khác nhau cần thiết cho dự án.

Sau khi cài đặt, bạn có thể cập nhật scripts phần package.json của bạn để có dev lệnh. Toàn bộ package.json của bạn bây giờ tập tin sẽ trông như thế này:

{
 "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"
 }
}

Tệp ở trên hiển thị tất cả các phần phụ thuộc đã cài đặt của bạn. npm run dev lệnh sẽ chạy khi bạn sử dụng dev kịch bản.

Cách xây dựng điểm cuối

Việc đầu tiên cần làm là tạo một thư mục mới có tên src . Thư mục này sẽ chứa tất cả các tập tin mã của bạn. Tệp đầu tiên sẽ chứa là tệp gốc của ứng dụng — app.ts tập tin như được định nghĩa trong package.json tập tin.

Chúng ta sẽ sử dụng app.ts để nhập các gói cần thiết và tạo một máy chủ đơn giản với một điểm cuối duy nhất để gửi email như bên dưới:

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");
});

Bây giờ, bạn có thể khởi động máy chủ của mình bằng cách chạy npm run dev trong thiết bị đầu cuối của bạn. Bạn sẽ thấy thông báo có nội dung Server started at [http://localhost:4300](http://localhost:4300) trong thiết bị đầu cuối của bạn.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web thông báo npm run dev

Bây giờ bạn có thể kiểm tra điểm cuối bằng công cụ như Postman:

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Thử nghiệm điểm cuối trên Postman

Yêu cầu mất gần 4 giây như trong ảnh chụp màn hình. Điều này là rất chậm đối với một điểm cuối. Nếu nhìn vào thiết bị đầu cuối của mình, bạn cũng sẽ thấy một URL nơi bạn có thể xem trước email đã được gửi.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web

Mở liên kết sẽ cho bạn biết email trông như thế nào.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Nội dung email

Cách tạo hàng đợi

Để giúp quá trình diễn ra nhanh hơn nữa, email có thể được xếp hàng đợi để gửi sau và phản hồi sẽ được gửi tới người dùng ngay lập tức.

Để thực hiện việc này, hãy cài đặt bull thư viện và @types của nó thư viện vì chúng tôi sẽ sử dụng nó để tạo hàng đợi. Đó là:

npm i bull @types/bull

Tạo hàng đợi mới bằng bull dễ dàng như việc tạo một Bull mới đối tượng có tên cho hàng đợi:

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

Khi hàng đợi được tạo chỉ với tên hàng đợi, nó sẽ cố gắng sử dụng URL kết nối Redis mặc định:localhost:6379 . Nếu bạn muốn sử dụng một URL khác, chỉ cần chuyển đối tượng thứ hai vào Bull lớp làm đối tượng tùy chọn:

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

Tại thời điểm này, bạn có thể tạo một hàm đơn giản để đóng vai trò là nhà sản xuất công việc và thêm công việc vào hàng đợi mỗi khi có yêu cầu.

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

Hàm mới được tạo này, sendNewEmail , chấp nhận một đối tượng chứa thông tin chi tiết về email mới sẽ được gửi thuộc loại EmailType . Có địa chỉ email người gửi (from ), địa chỉ email người nhận (to ), subject của email và nội dung của email (text ). Sau đó, nó đẩy một công việc mới vào hàng đợi.

Bây giờ bạn có thể sử dụng chức năng này thay vì gửi email trong khi yêu cầu. Sửa đổi điểm cuối để thực hiện việc này:

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",
 });
});

Tại thời điểm này, mã đơn giản hơn và quá trình nhanh hơn. Yêu cầu chỉ mất khoảng 40m — nhanh hơn khoảng 100 lần so với trước đây.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Thử nghiệm điểm cuối với Người đưa thư

Tại thời điểm này, email sẽ được thêm vào hàng đợi. Nó sẽ vẫn ở trong hàng đợi cho đến khi được xử lý. Công việc có thể được xử lý bởi cùng một ứng dụng hoặc dịch vụ khác (nếu trong thiết lập vi dịch vụ).

Cách xử lý công việc

Chu trình này không đầy đủ và vô ích nếu thư không bao giờ rời khỏi hàng đợi. Chúng tôi sẽ tạo một trình tiêu dùng công việc để xử lý công việc và xóa hàng đợi.

Chúng ta có thể làm điều này bằng cách tạo logic cho hàm chấp nhận Job phản đối và gửi 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));
 return nodemailer.getTestMessageUrl(info);
};

Hàm trên chấp nhận Job đối tượng. Đối tượng có các thuộc tính hữu ích hiển thị trạng thái và dữ liệu trong một công việc. Ở đây, chúng tôi sử dụng data tài sản.

Tại thời điểm này, tất cả những gì chúng ta có là một chức năng. Nó không tự động nhận công việc vì nó không biết nên làm việc với hàng đợi nào.

Trước khi kết nối nó với hàng đợi, bạn có thể tiếp tục thêm một số công việc vào hàng đợi bằng cách gửi một số yêu cầu. Bạn có thể kiểm tra các công việc email hiện đang được xếp hàng đợi bằng cách chạy lệnh này trong redis-cli của bạn :

LRANGE bull:email:wait 0 -1

Thao tác này sẽ kiểm tra danh sách chờ email và trả về ids của các công việc đang chờ.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Redis CLI

Tôi đã tạo ra một số công việc chỉ để cho thấy công nhân thực sự làm việc như thế nào.

Bây giờ, hãy kết nối nhân viên với hàng đợi bằng cách thêm dòng mã này:

emailQueue.process(processEmailQueue);

Đây là app.ts của bạn tập tin bây giờ sẽ xử lý việc đó:

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");
});

Sau khi lưu, bạn sẽ nhận thấy máy chủ khởi động lại và ngay lập tức bắt đầu gửi thư. Điều này là do nhân viên nhìn thấy hàng đợi và bắt đầu xử lý ngay lập tức.

Làm chủ hàng đợi tin nhắn với Node.js &Redis:Tăng hiệu suất ứng dụng web Máy chủ gửi email xếp hàng đợi

Bây giờ, cả nhà sản xuất và công nhân đều đang hoạt động. Mọi yêu cầu API mới sẽ được đẩy vào hàng đợi và nhân viên sẽ xử lý ngay lập tức trừ khi đã có một số công việc đang chờ xử lý.

Tóm tắt

Tôi hy vọng bài viết này đã giúp bạn hiểu hàng đợi tin nhắn là gì, cách thêm công việc và tạo quy trình để chạy chúng cũng như cách bạn có thể sử dụng chúng để xây dựng các ứng dụng web tốt hơn. Bạn có thể tìm thấy các tệp mã được sử dụng trong bài viết này trên GitHub.

Nếu bạn có bất kỳ câu hỏi hoặc lời khuyên liên quan nào, vui lòng liên hệ với tôi để chia sẻ.

Để đọc thêm các bài viết của tôi hoặc theo dõi công việc của tôi, bạn có thể kết nối với tôi trên LinkedIn, Twitter và Github. Thật nhanh chóng, dễ dàng và miễn phí!

Học cách viết mã miễn phí. Chương trình giảng dạy mã nguồn mở của freeCodeCamp đã giúp hơn 40.000 người có được việc làm với tư cách là nhà phát triển. Bắt đầu