استریم یا جریان داده در NodeJs بخش اول
استریم (stream) یا جریان داده دقیقا چیه؟
استریم مجموعه ای از داده ها مانند آرایه ها و استرینگ ها می باشد. اما تفاوت استریم این است که تمامی دیتا ها یکجا در دسترس نیستند و این نقطه ی قوت و مثبت استریم میباشد، زمانی که با حجم زیادی از دیتا یا دیتا هایی در یک منبع خارجی کار میکنیم دیتاها به صورت chunk-chunk یا تکه-تکه می آیند که باعث بالا رفتن سرعت پردازش می شود.
استریم فقط مخصوص کار کردن با دیتا های بزرگ و زیاد نیست بلکه می توانیم ورودی و خروجی کد هایمان را باهم ترکیب کنیم دقیقا مانند یک piping (لوله کشی، لوله گذاری) که این کار با دستور pipe قابل انجام است و همچنین رابط یا اینترفیس استریم در اکثر ماژول های داخلی Node Js قابل استفاده و پیاده سازی می باشد
در لیست بالا چند نمونه از ماژول ها و اشیاء داخلی (نیتیو) Node Js وجود دارد که قابلیت خواندن یا نوشتن جریان ها را دارند اما بعضی از آن ها میتوانند به طور همزمان هم جریان خواندی و هم نوشتنی باشند (readable , Writable streams) مانند:
Tcp Sockets, zlib and crypto
نکته: یک http response در واقع یک جریان قابل خواندن(readable) از سمت کلاینت میباشد و یک جریان نوشتنی (writable) از سمت سرور میباشد.
مثال عملی از استریم کردن:
میخواهم با کمک استریم کردن دیتا در فایل یک مثال کاملا واقعی انجام بدم خب میخوام یک میلیون خط دیتا را در یک فایل متنی بنویسم :
const fs = require('fs');
const file = fs.createWriteStream('./big.file');
for(let i=0; i<= 1e6; i++) {
file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n');
}
file.end();
ماژول fs می تواند خواندن و نوشتن روی فایل ها را با stream interface انجام دهد. ما در تکه کد بالا یک فایل بزرگ را با حجم حدودا ۴۰۰ مگابایت ایجاد کردیم و میخواهیم یک وب سرور با nodejs ایجاد کنیم که این فایل بزرگ را به سمت کلاینت استریم کنیم :
const fs = require('fs');
const server = require('http').createServer();
server.on('request', (req, res) => {
fs.readFile('./big.file', (err, data) => {
if (err) throw err;
res.end(data);
});
});
server.listen(8000);
زمانی که این سرور یک ریکوئست یا درخواست را دریافت میکند به صورت نامتقارن (asynchronous) با کمک متد fs.readFile فایل بزرگ را سرو میکند و نکته ی قابل توجه این است که event Loop بلاک یا مسدود نمی شود قبل از ارسال ریکوئست به سمت سرور مرورگر حدود 8MB رم مصرف میکند اما بعد از ارسال می بینید که رم مرورگر تا 434.8MB بالا میرود و در شکل زیر نحوه ی خواندن دیتا را میبینیم:
دلیل اینکه مصرف رم مرورگر بالا میرود این است که ما کل محتوا را در پاسخ ریکوئست قرار میدهیم و در حافظه قرار میگیرد که این عمل اصلا کارآمد یا efficient نیست.
اما این تنها روش ارسال دیتا به سمت کلاینت نیست بلکه آبجکت یا شئ response قابلیت نوشتن جریان را دارد یا writable stream می باشد که میتوانیم فایل بزرگمون که تمامی فایل ها در node js به صورت readable stream هستند را به همدیگر pipe کنیم یعنی فایل را به response بدون اینکه بخوایم 400MB رم مصرف کنیم همان عملیات را انجام بدیم دقیقا مانند انتقال آب با لوله که دیگر نیاز نیست آن حجم زیاد از اب را با تانکر یا بشکه جابجا کنیم.
ماژول fs می تواند یک جریان قابل خواندن یا readable stream را برای هر فایلی با استفاده از متد createReadStream را ایجاد کند و ما میتوانیم این جریان را مستقیما به شئ response لوله یا pipe کنیم
const fs = require('fs');
const server = require('http').createServer();
server.on('request', (req, res) => {
const src = fs.createReadStream('./big.file');
src.pipe(res);
});
server.listen(8000);
حالا وقتی به سرور ریکوست میفرستیم میبینیم که میزان مصرف حافظه چقدر کارآمد و بهتر شده است:
زمانی که کلاینت درخواست را برای خواندن فایل ارسال میکند، سرور ما آن را به تکه های کوچک یا chunk تبدیل میکند و تکه تکه آن را به سمت کلاینت ارسال میکند.
حتی اگر شما یک فایل را با پنج میلیون خط دیتا بازسازی کنید و حجم فایل را پنج برابر کنید مصرف حافظه توی همین محدوده ای (۲۰MB) که دیدید باقی میماند.
اما اگر سعی کنید با استفاده از fs.readFile دیتا را ارسال کنید مصرف حافظه زیاد میشود و باعث به وجود آمدن محدودیت میشود اما هیچ مشکلی برای استریم کردن یک فایل 2GB با کمک createReadStream به سمت کلاینت وجود ندارد و از همه مهمتر در این روش استفاده و مصرف رم و پردازنده با فایل 400MB هیچ تفاوتی ندارد و یکسان خواهد بود.
نتیجه گیری:
توی این بخش از مقاله با مفهوم اولیه و کاربرد استریم یا جریان داده آشنا شدیم در بخش بعد با انواع مختلف جریان ها آشنا خواهیم شد پس منتظر بخش بعدی مقاله باشید
از سال ۸۹ - ۹۰ وارد حوزه ی برنامه نویسی شدم و انواع زمینه ها و شاخه های مختلف رو کار کردم تا اینکه سال ۹۵ توی حوزه ی بک اند (نود جی اس) ماندگار شدم، تجربیات خیلی زیادی رو توی این مسیر کسب کردم. شکست ها و موفقیت هایی رو هم داشتم که همه ی این موارد رو در قالب مقاله، دوره و پادکست در اختیارتون خواهم گذاشت خلاصه که وبسایت کدنایت رو سال ۱۴۰۲ توسعه دادیم که یک پلتفرم آموزشی با گروهی از اساتید خفن هستش که قراره کلی محتوا در اختیارتون بذاریم.