זרמים ב-Node.js יכולים להיות מסובכים, אבל שווה לקחת את הזמן כדי להבין אותם.
טייק אווי מפתח
- זרמים ב-Node.js הם כלי בסיסי לעיבוד והעברת נתונים, מה שהופך אותם לאידיאליים עבור יישומים מונעי זמן אמת ואירועים.
- כדי ליצור זרם בר כתיבה ב-Node.js, אתה יכול להשתמש בפונקציה createWriteStream() של מודול fs, אשר כותבת נתונים למיקום ספציפי.
- קריא, כתיבה, דופלקס ושינוי הם ארבעת סוגי הזרמים ב-Node.js, כל אחד עם מקרה שימוש ופונקציונליות משלו.
זרם הוא כלי תכנות בסיסי העוסק בזרימת הנתונים. בבסיסו, זרם מייצג בדרך כלל העברה רציפה של בתים מנקודה אחת לאחרת. התיעוד הרשמי של Node.js מגדיר זרם כממשק מופשט שבו אתה יכול להשתמש כדי לעבוד עם נתונים.
העברת נתונים במחשב או ברשת היא שימוש אידיאלי בזרם.
זרמים ב-Node.js
זרמים מילאו תפקיד חיוני בהצלחת Node.js. הם אידיאליים עבור עיבוד נתונים בזמן אמת ויישומים מונעי אירועים, שתי תכונות בולטות של סביבת זמן הריצה Node.js.
כדי ליצור זרם חדש ב-Node.js, תצטרך להשתמש ב-API של stream, שעובד אך ורק עם מחרוזות ו- נתוני מאגר של Node.js. ל-Node.js יש ארבעה סוגים של זרמים: ניתנים לכתיבה, קריא, דופלקס ושינוי.
כיצד ליצור ולהשתמש בזרם הניתן לכתיבה
זרם ניתן לכתיבה מאפשר לך לכתוב או לשלוח נתונים למיקום מסוים. למודול fs (מערכת קבצים) יש מחלקה WriteStream, שבה אתה יכול להשתמש כדי ליצור זרם חדש עם fs.createWriteStream() פוּנקצִיָה. פונקציה זו מקבלת את הנתיב לקובץ שאליו ברצונך לכתוב נתונים, כמו גם מערך אופציונלי של אפשרויות.
const {createWriteStream} = require("fs");(() => {
const file = "myFile.txt";
const myWriteStream = createWriteStream(file);
let x = 0;
const writeNumber = 10000;
const writeData = () => {
while (x < writeNumber) {
const chunk = Buffer.from(`${x}, `, "utf-8");
if (x writeNumber - 1) return myWriteStream.end(chunk);
if (!myWriteStream.write(chunk)) break;
x++
}
};
writeData();
})();
קוד זה מייבא את createWriteStream() פונקציה, אשר פונקציית החץ האנונימית לאחר מכן משתמש ליצירת זרם שכותב נתונים ל-myFile.txt. הפונקציה האנונימית מכילה פונקציה פנימית הנקראת writeData() שכותב נתונים.
ה createWriteStream() הפונקציה פועלת עם מאגר כדי לכתוב אוסף של מספרים (0-9,999) לקובץ היעד. עם זאת, כאשר אתה מפעיל את הסקריפט שלמעלה, הוא יוצר קובץ באותה ספרייה שמכיל את הנתונים הבאים:
אוסף המספרים הנוכחי מסתיים ב-2,915, אך הוא היה צריך לכלול מספרים עד 9,999. אי התאמה זו מתרחשת מכיוון שכל WriteStream משתמש במאגר המאחסן כמות קבועה של נתונים בכל פעם. כדי ללמוד מהו ערך ברירת המחדל הזה, תצטרך להתייעץ עם סימן מים גבוה אוֹפְּצִיָה.
console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");
הוספת שורת הקוד למעלה לפונקציה האנונימית תפיק את הפלט הבא בטרמינל:
פלט הטרמינל מראה כי ברירת המחדל סימן מים גבוה הערך (שניתן להתאמה אישית) הוא 16,384 בתים. המשמעות היא שאתה יכול לאחסן רק מתחת ל-16,384 בתים של נתונים במאגר הזה בכל פעם. אז, עד מספר 2,915 (בתוספת כל הפסקים והרווחים) מייצג את כמות הנתונים המקסימלית שהמאגר יכול לאחסן בבת אחת.
הפתרון לשגיאת המאגר הוא שימוש באירוע זרם. זרם פוגש אירועים שונים בשלבים שונים של תהליך העברת הנתונים. ה לנקז אירוע הוא האפשרות המתאימה למצב זה.
בתוך ה writeData() הפונקציה למעלה, הקריאה ל- הכתיבה של WriteStream() הפונקציה מחזירה true אם גוש הנתונים (או המאגר הפנימי) נמצא מתחת ל- סימן מים גבוה ערך. זה מציין שהאפליקציה יכולה לשלוח נתונים נוספים לזרם. עם זאת, ברגע שה לִכתוֹב() הפונקציה מחזירה false הלולאה נשברת כי אתה צריך לנקז את המאגר.
myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});
הכנסת ה לנקז קוד האירוע לעיל לתוך הפונקציה האנונימית ירוקן את המאגר של WriteStream כאשר הוא בתפוסה. ואז, זה מזכיר את writeData() שיטה, כך שהוא יכול להמשיך לכתוב נתונים. הפעלת האפליקציה המעודכנת תפיק את הפלט הבא:
עליך לשים לב שהיישום היה צריך לנקז את מאגר WriteStream שלוש פעמים במהלך ביצועו. קובץ הטקסט חווה גם כמה שינויים:
כיצד ליצור ולהשתמש בזרם קריא
כדי לקרוא נתונים, התחל ביצירת זרם קריא באמצעות ה fs.createReadStream() פוּנקצִיָה.
const {createReadStream} = require("fs");
(() => {
const file = "myFile.txt";
const myReadStream = createReadStream(file);myReadStream.on("open", () => {
console.log(`The read stream has successfully opened ${file}.`);
});myReadStream.on("data", chunk => {
console.log("The file contains the following data: " + chunk.toString());
});
myReadStream.on("close", () => {
console.log("The file has been successfully closed.");
});
})();
התסריט למעלה משתמש ב- createReadStream() שיטה לגשת לקובץ שיצר הקוד הקודם: myFile.txt. ה createReadStream() הפונקציה מקבלת נתיב קובץ (שיכול להיות בצורת מחרוזת, מאגר או כתובת URL) וכמה אפשרויות אופציונליות כארגומנטים.
בפונקציה האנונימית, ישנם מספר אירועי זרם חשובים. עם זאת, אין סימן ל- לנקז מִקרֶה. הסיבה לכך היא שזרם קריא מאחסן נתונים רק כאשר אתה קורא ל- stream.push (נתח) לתפקד או להשתמש ב קָרִיא מִקרֶה.
ה לִפְתוֹחַ אירוע מופעל כאשר fs פותח את הקובץ שממנו אתה רוצה לקרוא. כאשר אתה מצרף את נתונים אירוע לזרם מתמשך באופן מרומז, הוא גורם לזרם לעבור למצב זורם. זה מאפשר לנתונים לעבור ברגע שהם הופכים זמינים. הפעלת האפליקציה למעלה מייצרת את הפלט הבא:
כיצד ליצור ולהשתמש בזרם דופלקס
זרם דופלקס מיישם גם את ממשק הזרם הניתן לכתיבה וגם את ממשק הקריאה, כך שתוכל לקרוא ולכתוב לזרם כזה. דוגמה אחת היא שקע TCP המסתמך על מודול הרשת ליצירתו.
דרך פשוטה להדגים את המאפיינים של זרם דופלקס היא ליצור שרת ולקוח TCP שמעבירים נתונים.
הקובץ server.js
const net = require('net');
const port = 5000;
const host = '127.0.0.1';const server = net.createServer();
server.on('connection', (socket)=> {
console.log('Connection established from client.');socket.on('data', (data) => {
console.log(data.toString());
});socket.write("Hi client, I am server " + server.address().address);
socket.on('close', ()=> {
console.log('the socket is closed')
});
});
server.listen(port, host, () => {
console.log('TCP server is running on port: ' + port);
});
הקובץ client.js
const net = require('net');
const client = new net.Socket();
const port = 5000;
const host = '127.0.0.1';client.connect(port, host, ()=> {
console.log("connected to server!");
client.write("Hi, I'm client " + client.address().address);
});client.on('data', (data) => {
console.log(data.toString());
client.write("Goodbye");
client.end();
});
client.on('end', () => {
console.log('disconnected from server.');
});
תבחין שגם השרת וגם סקריפטים של הלקוח משתמשים בזרם קריא וניתן לכתיבה כדי לתקשר (העברה וקבלה של נתונים). באופן טבעי, יישום השרת פועל ראשון ומתחיל להאזין לחיבורים. ברגע שאתה מפעיל את הלקוח, הוא מתחבר לשרת באמצעות מספר יציאת TCP.
לאחר יצירת חיבור, הלקוח יוזם העברת נתונים על ידי כתיבה לשרת באמצעותו WriteStream. השרת רושם את הנתונים שהוא מקבל למסוף, ואז הוא כותב נתונים באמצעות ה-WriteStream שלו. לבסוף, הלקוח רושם את הנתונים שהוא מקבל, כותב נתונים נוספים ואז מתנתק מהשרת. השרת נשאר פתוח עבור לקוחות אחרים להתחבר.
כיצד ליצור ולהשתמש בזרם טרנספורמציה
זרמי טרנספורמציה הם זרמים דופלקסים שבהם הפלט קשור, אך שונה ממנו, לקלט. ל-Node.js שני סוגים של זרמי טרנספורמציה: זרמי zlib וקריפטו. זרם zlib יכול לדחוס קובץ טקסט ואז לפרוק אותו לאחר העברת הקבצים.
היישום compressFile.js
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');(() => {
const source = createReadStream('myFile.txt');
const destination = createWriteStream('myFile.txt.gz');
source.pipe(zlib.createGzip()).pipe(destination);
})();
הסקריפט הפשוט הזה לוקח את קובץ הטקסט המקורי, דוחס אותו ומאחסן אותו בספרייה הנוכחית. זהו תהליך פשוט הודות לזרם הקריא צינור() שיטה. צינורות זרמים מסירים את השימוש במאגרים ובנתוני צינור ישירות מזרם אחד למשנהו.
עם זאת, לפני שהנתונים מגיעים לזרם הניתן לכתיבה בסקריפט, הוא לוקח מעט עיקוף באמצעות שיטת createGzip() של zlib. שיטה זו דוחסת את הקובץ ומחזירה אובייקט Gzip חדש שזרם הכתיבה מקבל לאחר מכן.
היישום decompressFile.js
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');
(() => {
const source = createReadStream('myFile.txt.gz');
const destination = createWriteStream('myFile2.txt');
source.pipe(zlib.createUnzip()).pipe(destination);
})();
סקריפט זה למעלה לוקח את הקובץ הדחוס ומפרק אותו. אם תפתח את החדש myFile2.txt קובץ, תראה שהוא מכיל את אותם נתונים כמו הקובץ המקורי:
מדוע זרמים חשובים?
זרמים משפרים את היעילות של העברת נתונים. זרמים ניתנים לקריאה ולכתיבה משמשים כבסיס המאפשר תקשורת בין לקוחות ושרתים, כמו גם דחיסה והעברה של קבצים גדולים.
זרמים גם משפרים את הביצועים של שפות תכנות. ללא זרמים, תהליך העברת הנתונים הופך מורכב יותר, דורש קלט ידני גדול יותר מהמפתחים וכתוצאה מכך ליותר שגיאות ובעיות ביצועים.