เมื่อคุณสร้างเว็บแอปพลิเคชันขนาดใหญ่ ความเร็วคือสิ่งสำคัญอันดับแรก ผู้ใช้ไม่ต้องการรอการตอบกลับเป็นเวลานานอีกต่อไป และพวกเขาก็ไม่ควรต้องรอ แต่กระบวนการบางอย่างต้องใช้เวลา และไม่สามารถทำให้เร็วขึ้นหรือกำจัดออกไปได้
คิวข้อความช่วยแก้ปัญหานี้โดยจัดเตรียมสาขาเพิ่มเติมให้กับเส้นทางการตอบกลับคำขอตามปกติ สาขาเพิ่มเติมนี้ช่วยให้แน่ใจว่าผู้ใช้จะได้รับการตอบสนองทันที และกระบวนการที่ใช้เวลานานสามารถทำได้ที่ด้านข้าง ทุกคนกลับบ้านอย่างมีความสุข
บทความนี้จะเน้นไปที่การอธิบายว่าคิวข้อความคืออะไร และจะเริ่มต้นใช้งานคิวข้อความเหล่านี้ได้อย่างไรโดยการสร้างแอปพลิเคชันที่เรียบง่าย คุณควรคุ้นเคยกับพื้นฐานของ Node.js และคุณควรติดตั้ง Redis ภายในเครื่องหรือบนอินสแตนซ์ระบบคลาวด์ เรียนรู้วิธีติดตั้ง Redis ที่นี่
คิวคืออะไร
คิวคือโครงสร้างข้อมูลที่ช่วยให้คุณจัดเก็บเอนทิตีในคำสั่งซื้อได้ การเข้าคิวใช้หลักการเข้าก่อนออกก่อน (FIFO) ป>
แนวคิดเรื่องคิวในวิทยาการคอมพิวเตอร์ก็เหมือนกับแนวคิดเรื่องคิวในชีวิตประจำวันที่ผู้คนเข้าแถวเพื่อรับสิ่งของ คุณเข้าร่วมคิวจากด้านหลัง รอจนกว่าจะถึงตาคุณ จากนั้นออกจากคิวจากด้านหน้าหลังจากที่คุณได้เข้าร่วมแล้ว
ในวิทยาการคอมพิวเตอร์ เมื่อกระบวนการต่างๆ เช่น คำขอ API กำลังทำงานอยู่ และคุณจำเป็นต้องลบงานบางอย่าง (เช่น การส่งอีเมล) ออกจากโฟลว์ปัจจุบัน คุณจะพุชงานนั้นไปที่คิวและดำเนินการกระบวนการต่อไป
แผนภาพด้านล่างแสดงวงจรชีวิตของคิว:
วงจรชีวิตของคิว | https://optimalbits.github.io/bull/ ป>
งานคืออะไร?
งานคือชิ้นส่วนของข้อมูลใดๆ ที่ใช้ในคิว ซึ่งโดยปกติจะเป็นออบเจ็กต์ที่มีลักษณะคล้าย JSON
ดังที่แสดงไว้ในภาพหน้าปกของบทความนี้ คุณสามารถนึกถึงงานในฐานะที่แต่ละคนต้องต่อคิวที่สนามบิน แต่ละคนถือกระเป๋าเอกสารที่มีข้อมูลเฉพาะและคำแนะนำอื่นๆ (หนังสือเดินทางและเอกสารทางการแพทย์หากจำเป็น) ซึ่งจะช่วยได้เมื่อถึงตาพวกเขาที่ต้องดูแล
คนใหม่ที่เข้าคิวนี้จะเข้าจากด้านหลัง (เป็นคนสุดท้าย) และคนจะเข้าจากด้านหน้า นั่นคือวิธีการประมวลผลงาน แต่ละงานประกอบด้วยข้อมูลที่จะใช้สำหรับการประมวลผล งานใหม่จะถูกเพิ่มจากด้านหลัง ในขณะที่งานถูกนำออกจากด้านหน้า
ผู้ผลิตงานคืออะไร
ผู้ผลิตงานคือโค้ดใดๆ ที่เพิ่มงานลงในคิว ในชีวิตจริง นี่จะเป็นเจ้าหน้าที่รักษาความปลอดภัยที่สนามบินคอยบอกทิศทางให้ผู้คนทราบว่าต้องเข้าคิวไหนเพื่อจุดประสงค์ที่แตกต่างกัน ป>
ผู้ผลิตงานสามารถดำรงอยู่ได้โดยอิสระจากผู้บริโภคงาน ซึ่งหมายความว่าในการตั้งค่าไมโครเซอร์วิส บริการเฉพาะอาจเกี่ยวข้องกับการเพิ่มงานลงในคิว แต่ไม่ใช่วิธีดำเนินการหลังจากนั้น
พนักงาน (ผู้บริโภคงาน) คืออะไร
ผู้ปฏิบัติงานหรือผู้บริโภคงานเป็นกระบวนการหรือฟังก์ชันที่สามารถดำเนินงานได้ ลองนึกถึงพนักงานในฐานะแคชเชียร์ของธนาคารที่คอยดูแลผู้คนที่ต่อคิวอยู่ที่ธนาคาร เมื่อคนแรกเข้ามาก็จะเข้าคิวเป็นคนเดียวในคิว แคชเชียร์ก็โทรมาเรียกคิวก็ว่าง ป>
แคชเชียร์ขอรายละเอียดเฉพาะเพื่อใช้ในการประมวลผลธุรกรรมจากบุคคลนั้น ขณะที่แคชเชียร์ดูแลลูกค้ารายนั้น ลูกค้าอีกสี่คนก็อาจเข้าแถวรอ พวกเขาจะยังคงอยู่ในคิวจนกว่าแคชเชียร์จะเสร็จสิ้นกับลูกค้ารายแรกก่อนที่จะเรียกลูกค้ารายต่อไป นี่เป็นกระบวนการเดียวกันกับผู้ปฏิบัติงานคิว โดยพวกเขาจะเลือกงานแรกในคิวและดำเนินการ
งานที่ล้มเหลวคืออะไร
บ่อยครั้ง งานบางอย่างอาจล้มเหลวในระหว่างการประมวลผล
นี่คือสาเหตุบางประการที่ทำให้งานล้มเหลว:
- ข้อมูลอินพุตไม่ถูกต้องหรือขาดหายไป:เมื่อข้อมูลที่จำเป็นสำหรับงานที่ประมวลผลหายไป งานจะล้มเหลว ตัวอย่างเช่น งานส่งอีเมลจะล้มเหลวหากไม่มีที่อยู่อีเมลของผู้รับ
- หมดเวลา:งานอาจล้มเหลวโดยกลไกคิวหากใช้เวลานานกว่าปกติ นี่อาจเป็นเพราะปัญหาเกี่ยวกับการพึ่งพางานหรืออย่างอื่น แต่โดยปกติแล้วคุณไม่ต้องการให้งานเดียวดำเนินไปตลอดกาล
- ปัญหาเครือข่ายหรือโครงสร้างพื้นฐาน:ปัญหาเหล่านี้เกือบจะอยู่นอกเหนือการควบคุมของคุณ แต่ก็เกิดขึ้นได้ ข้อผิดพลาดในการเชื่อมต่อฐานข้อมูลอาจทำให้งานล้มเหลว
- ปัญหาการพึ่งพา:บางครั้งงานต้องอาศัยทรัพยากรภายนอกเพื่อให้ทำงานได้ดี เมื่อใดก็ตามที่ทรัพยากรอื่นๆ เหล่านี้ไม่พร้อมใช้งานหรือไม่สำเร็จ งานก็จะล้มเหลว
เมื่องานล้มเหลว คุณสามารถกำหนดค่ากลไกคิวของคุณเพื่อลองใหม่ได้ คุณสามารถลองงานใหม่ได้ทันทีหรือหลังจากระยะเวลาที่คำนวณได้ คุณสามารถกำหนดจำนวนครั้งสูงสุดได้ ซึ่งแนะนำ ถ้าไม่เช่นนั้น คุณจะจบลงด้วยการทำงานที่จะล้มเหลวอย่างไม่มีที่สิ้นสุด
คิวมีประโยชน์สำหรับการสร้างช่องทางการสื่อสารที่มีประสิทธิภาพระหว่างไมโครเซอร์วิส บริการหลายอย่างสามารถใช้คิวเดียวกันได้ บริการที่แตกต่างกันอาจได้รับมอบหมายให้แก้ไขปัญหาที่แตกต่างกัน เมื่อบริการเสร็จสิ้นงานแล้ว บริการสามารถส่งงานไปยังบริการอื่นที่มีผู้ปฏิบัติงานรองานนั้นได้ บริการนั้นจะรับและทำทุกอย่างที่จำเป็นกับข้อมูล
คิวยังมีประโยชน์สำหรับการถ่ายงานหนักออกจากกระบวนการอีกด้วย ดังที่คุณจะเห็นในบทความนี้ งานที่ต้องใช้เวลามาก เช่น การส่งอีเมล อาจต้องอยู่ในคิวเพื่อหลีกเลี่ยงการชะลอเวลาตอบสนอง
คิวช่วยหลีกเลี่ยงจุดล้มเหลวเพียงจุดเดียว กระบวนการที่มีความสามารถในการล้มเหลวและสามารถลองใหม่ได้นั้นดีที่สุดในการประมวลผลโดยใช้คิวซึ่งสามารถลองใหม่ได้ในภายหลัง
วิธีสร้างแอปพลิเคชันอย่างง่ายที่ใช้คิว
ในบทความนี้ เราจะสร้างโปรเจ็กต์ง่ายๆ โดยใช้ Node.js และ Redis เราจะใช้ไลบรารี Bull เนื่องจากช่วยลดความซับซ้อนมากมายที่เกี่ยวข้องกับการสร้างระบบคิว โปรเจ็กต์จะมีปลายทางเดียวในการส่งอีเมล
สร้างโครงการ Node.js ใหม่
mkdir nodejs-queue-project
cd nodejs-queue-project
npm init -y
คำสั่งด้านบนจะสร้างโฟลเดอร์ใหม่ชื่อ 09 และ 16 ไฟล์ในนั้น 29รหัส> ไฟล์ควรมีลักษณะดังนี้:
{
"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
คำสั่งด้านบนจะติดตั้งแพ็คเกจและการขึ้นต่อกันต่าง ๆ ที่จำเป็นสำหรับโปรเจ็กต์ ป>
หลังการติดตั้ง คุณสามารถอัพเดต 37 ได้ ส่วนของ 47 ของคุณ ที่จะมี 52 คำสั่ง 60 ทั้งหมดของคุณ ไฟล์ควรมีลักษณะดังนี้:
{
"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"
}
}
ไฟล์ด้านบนแสดงการอ้างอิงที่ติดตั้งทั้งหมดของคุณ 70รหัส> คำสั่งจะทำงานเมื่อคุณใช้ 88 สคริปต์
วิธีสร้างจุดสิ้นสุด
สิ่งแรกที่ต้องทำคือสร้างโฟลเดอร์ใหม่ชื่อ 92 . โฟลเดอร์นี้จะมีไฟล์โค้ดทั้งหมดของคุณ ไฟล์แรกจะมีคือไฟล์รูทของแอปพลิเคชัน — 101 ตามที่กำหนดไว้ใน 116 ไฟล์.
เราจะใช้ 120 ไฟล์เพื่อนำเข้าแพ็คเกจที่จำเป็นและสร้างเซิร์ฟเวอร์อย่างง่ายที่มีจุดสิ้นสุดเดียวเพื่อส่งอีเมลดังที่แสดงด้านล่าง:
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");
});
ตอนนี้คุณสามารถเริ่มต้นเซิร์ฟเวอร์ของคุณโดยเรียกใช้ 135 ในเทอร์มินัลของคุณ คุณควรเห็นข้อความว่า 146 ในเทอร์มินัลของคุณ
npm เรียกใช้ข้อความ dev ป>
ตอนนี้คุณสามารถทดสอบตำแหน่งข้อมูลโดยใช้เครื่องมือเช่นบุรุษไปรษณีย์:
การทดสอบปลายทางกับบุรุษไปรษณีย์ ป>
คำขอใช้เวลาเกือบ 4 วินาทีตามที่แสดงในภาพหน้าจอ ซึ่งช้ามากสำหรับปลายทาง หากคุณดูที่เทอร์มินัลของคุณ คุณจะเห็น URL ที่คุณสามารถดูตัวอย่างอีเมลที่ถูกส่งไปได้ด้วย
การเปิดลิงก์จะทำให้คุณเห็นว่าอีเมลมีลักษณะอย่างไร
เนื้อหาอีเมล ป>
วิธีสร้างคิว
เพื่อให้กระบวนการรวดเร็วยิ่งขึ้น สามารถจัดคิวอีเมลเพื่อส่งในภายหลังและส่งการตอบกลับไปยังผู้ใช้ได้ทันที
เมื่อต้องการทำเช่นนี้ ให้ติดตั้ง 155 ไลบรารี่และ 167 ไลบรารี่ที่เราจะใช้เพื่อสร้างคิว นั่นคือ:
npm i bull @types/bull
การสร้างคิวใหม่โดยใช้ 178 ง่ายเหมือนกับการสร้างอินสแตนซ์ 181 ใหม่ วัตถุที่มีชื่อสำหรับคิว:
// This goes at the top of your file
import Bull from 'bull';
const emailQueue = new Bull("email");
เมื่อคิวถูกสร้างขึ้นด้วยชื่อคิวเพียงอย่างเดียว คิวจะพยายามใช้ URL การเชื่อมต่อ Redis เริ่มต้น:195 . หากคุณต้องการใช้ URL อื่น เพียงส่งวัตถุที่สองไปที่ 205 คลาสเป็นวัตถุตัวเลือก:
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 });
};
ฟังก์ชันที่สร้างขึ้นใหม่นี้
คุณสามารถใช้ฟังก์ชันนี้แทนการส่งอีเมลในระหว่างการร้องขอได้ทันที แก้ไขจุดสิ้นสุดเพื่อทำสิ่งนี้:
ณ จุดนี้ โค้ดจะง่ายกว่าและกระบวนการก็เร็วขึ้น คำขอใช้เวลาประมาณ 40 เมตร — เร็วกว่าเดิมประมาณ 100 เท่า
ณ จุดนี้ อีเมลจะถูกเพิ่มลงในคิว มันจะยังคงอยู่ในคิวจนกว่าจะประมวลผล งานสามารถประมวลผลได้โดยแอปพลิเคชันเดียวกันหรือบริการอื่น (หากอยู่ในการตั้งค่าไมโครเซอร์วิส)
วงจรนี้จะไม่สมบูรณ์และไม่มีประโยชน์หากอีเมลไม่เคยออกจากคิว เราจะสร้างผู้ใช้งานเพื่อประมวลผลงานและล้างคิว
เราสามารถทำได้โดยการสร้างตรรกะสำหรับฟังก์ชันที่ยอมรับ
ฟังก์ชั่นด้านบนยอมรับ
ณ จุดนี้ ทั้งหมดที่เรามีคือฟังก์ชัน ไม่รับงานโดยอัตโนมัติเพราะไม่รู้ว่าจะทำงานกับคิวไหน
ก่อนที่จะเชื่อมต่อเข้ากับคิว คุณสามารถเพิ่มงานสองสามงานลงในคิวต่อไปได้โดยส่งคำขอบางส่วน คุณสามารถตรวจสอบงานอีเมลที่อยู่ในคิวปัจจุบันได้โดยการรันคำสั่งนี้ใน
ซึ่งจะตรวจสอบรายการรออีเมล และส่งกลับ
ฉันได้สร้างงานสองสามงานเพียงเพื่อแสดงให้เห็นว่าคนงานทำงานอย่างไร
ตอนนี้ เชื่อมต่อผู้ปฏิบัติงานเข้ากับคิวโดยการเพิ่มบรรทัดโค้ดนี้:
นี่คือสิ่งที่
เมื่อคุณบันทึก คุณจะสังเกตเห็นว่าเซิร์ฟเวอร์รีสตาร์ทและเริ่มส่งอีเมลทันที เนื่องจากผู้ปฏิบัติงานมองเห็นคิวและเริ่มประมวลผลทันที
ตอนนี้ทั้งผู้ผลิตและคนงานต่างกระตือรือร้นกัน คำขอ API ใหม่ทุกรายการจะถูกส่งไปยังคิว และผู้ปฏิบัติงานจะดำเนินการทันที เว้นแต่จะมีงานที่ค้างอยู่บางส่วนอยู่แล้ว
ฉันหวังว่าบทความนี้จะช่วยให้คุณเข้าใจว่าคิวข้อความคืออะไร วิธีเพิ่มงานและสร้างกระบวนการเพื่อเรียกใช้งาน และวิธีที่คุณสามารถใช้เพื่อสร้างแอปพลิเคชันเว็บที่ดีขึ้น คุณสามารถค้นหาไฟล์โค้ดที่ใช้ในบทความนี้ได้ที่ GitHub
หากคุณมีคำถามหรือคำแนะนำที่เกี่ยวข้อง โปรดติดต่อฉันเพื่อแบ่งปัน
หากต้องการอ่านบทความของฉันเพิ่มเติมหรือติดตามผลงานของฉัน คุณสามารถเชื่อมต่อกับฉันได้ทาง LinkedIn, Twitter และ Github รวดเร็ว ง่ายดาย และฟรี!
เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น 217 ยอมรับออบเจ็กต์ที่มีรายละเอียดของอีเมลใหม่ที่จะส่งประเภท 229 . มีที่อยู่อีเมลของผู้ส่ง (238 ) ที่อยู่อีเมลผู้รับ (243 ), 256รหัส> ของอีเมล และเนื้อหาของอีเมล (260 ). แล้วมันดันงานใหม่เข้าคิว ป> 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",
});
});
การทดสอบปลายทางกับบุรุษไปรษณีย์ ป>
วิธีการประมวลผลงาน
274 คัดค้านและส่งอีเมล: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);
};
288 วัตถุ วัตถุมีคุณสมบัติที่เป็นประโยชน์ที่แสดงสถานะและข้อมูลในงาน ที่นี่เราใช้ 297 ทรัพย์สิน ป> 305 ของคุณ :LRANGE bull:email:wait 0 -1
311 ของงานที่รอคอยRedis CLI ป>
emailQueue.process(processEmailQueue);
322 ของคุณ ไฟล์ควรดูแลสิ่งนั้น: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");
});
เซิร์ฟเวอร์ส่งอีเมลที่อยู่ในคิว ป>
สรุป