1. التحليل الافتتاحي
الدفق هو واجهة مجردة تنفذها العديد من الكائنات في العقدة. على سبيل المثال ، يكون هناك طلب إلى خادم HTTP عبارة عن دفق ، كما أن stdout هو دفق أيضًا. التدفقات قابلة للقراءة أو قابلة للكتابة أو كليهما.
بدأ الاتصال الأول مع Stream مع unix المبكر. لقد أثبتت عقود من الممارسة أن فكرة Stream يمكنها ببساطة تطوير بعض الأنظمة الضخمة.
في UNIX ، يتم تنفيذ الدفق من خلال "|". في العقدة ، كوحدة دفق مدمجة ، يتم استخدام العديد من الوحدات الأساسية والوحدات النمطية ثلاثية الحزبية.
مثل UNIX ، فإن التشغيل الرئيسي لتيار العقدة هو .pipe () ، ويمكن للمستخدمين استخدام آلية الضغط المضاد للتحكم في التوازن بين القراءة والكتابة.
يمكن أن يوفر الدفق للمطورين واجهة موحدة يمكنها إعادة استخدام التوازن والتحكم في القراءة والكتابة بين التدفقات من خلال واجهات الدفق المجردة.
اتصال TCP هو دفق قابل للقراءة ودفق قابل للكتابة ، في حين أن اتصال HTTP مختلف. كائن طلب HTTP هو دفق قابل للقراءة ، في حين أن كائن استجابة HTTP هو دفق قابل للكتابة.
يتم إرسال عملية نقل الدفق في شكل مخزن مؤقت افتراضيًا ، ما لم تقم بتعيين نماذج ترميز أخرى لها ، فإن ما يلي مثال:
نسخة الكود كما يلي:
var http = require ('http') ؛
var server = http.createserver (function (req ، res) {
Res.WriteHeader (200 ، {'content-type': 'text/plain'}) ؛
res.end ("مرحبًا ، Big Bear!") ؛
}) ؛
server.listen (8888) ؛
console.log ("HTTP Server قيد التشغيل على المنفذ 8888 ...") ؛
بعد الجري ، سيظهر الرمز المشتعل لأنه لم يتم تعيين مجموعة الأحرف المحددة ، مثل: "UTF-8".
فقط قم بتعديله:
نسخة الكود كما يلي:
var http = require ('http') ؛
var server = http.createserver (function (req ، res) {
Res.WriteHeader (200 ، {
"نوع المحتوى": "النص/عادي ؛ charset = utf-8 '// إضافة charset = utf-8
}) ؛
res.end ("مرحبًا ، Big Bear!") ؛
}) ؛
server.listen (8888) ؛
console.log ("HTTP Server قيد التشغيل على المنفذ 8888 ...") ؛
نتائج التشغيل:
لماذا تستخدم الدفق
إن الإدخال/الإخراج في العقدة غير متزامن ، لذا فإن القراءة والكتابة على القرص والشبكة تتطلب قراءة البيانات وقراءةها من خلال وظائف رد الاتصال. فيما يلي مثال على تنزيل ملف
على الرمز:
نسخة الكود كما يلي:
var http = require ('http') ؛
var fs = require ('fs') ؛
var server = http.createserver (function (req ، res) {
fs.readfile (__ dirname + '/data.txt' ، function (err ، data) {
res.end (البيانات) ؛
}) ؛
}) ؛
server.listen (8888) ؛
يمكن للرمز تنفيذ الوظائف المطلوبة ، ولكن تحتاج الخدمة إلى تخزين بيانات الملف بأكملها إلى الذاكرة قبل إرسال بيانات الملف. إذا كان ملف "data.txt" جدًا
إذا كانت كبيرة ولديها تزامن كبير ، فسيتم إهدار الكثير من الذاكرة. نظرًا لأن المستخدم يحتاج إلى الانتظار حتى يتم تخزين الملف بالكامل إلى الذاكرة لقبول بيانات الملف ، فإن هذا يؤدي إلى
تجربة المستخدم سيئة للغاية. لحسن الحظ ، كل من المعلمات (REQ ، RES) هي دفق ، حتى نتمكن من استخدام Fs.CreeCreeDstream () بدلاً من fs.readfile (). على النحو التالي:
نسخة الكود كما يلي:
var http = require ('http') ؛
var fs = require ('fs') ؛
var server = http.createserver (function (req ، res) {
var dream = fs.creeTeadStream (__ dirname + '/data.txt') ؛
Stream.pipe (res) ؛
}) ؛
server.listen (8888) ؛
تستمع طريقة .pipe () إلى أحداث "البيانات" و "النهاية" لـ Fs.CreeCreeDstream () ، بحيث لا يحتاج ملف "data.txt" إلى تخزينه مؤقتًا.
يمكن إرسال ملف إلى العميل فور اكتمال اتصال العميل. فائدة أخرى لاستخدام .pipe () هي أنه يمكن حلها عند عميل
اقرأ وكتابة الخلل الناجم عن تأخير نهاية كبيرة للغاية.
هناك خمسة تدفقات أساسية: قابلة للقراءة ، قابلة للكتابة ، تحويل ، دوبلكس ، و "كلاسيكية". (يرجى التحقق من واجهة برمجة التطبيقات للحصول على التفاصيل)
2. إدخال أمثلة
عندما لا يمكن تحميل البيانات التي يجب معالجتها في الذاكرة في وقت واحد ، أو عندما تكون المعالجة أكثر كفاءة أثناء القراءة ، نحتاج إلى استخدام تدفقات البيانات. يوفر NodeJS عمليات على تدفقات البيانات من خلال تدفقات مختلفة.
أخذ برنامج نسخ الملفات الكبير كمثال ، يمكننا إنشاء دفق بيانات للقراءة فقط لمصدر البيانات ، المثال هو كما يلي:
نسخة الكود كما يلي:
var rs = fs.createadStream (pathName) ؛
Rs.on ('Data' ، function (chunk) {
شيء (قطعة) ؛ // استخدم التفاصيل المحددة كما تريد
}) ؛
rs.on ('end' ، function () {
تنظيف() ؛
}) ؛
سيتم تشغيل أحداث البيانات في الكود بشكل مستمر ، بغض النظر عما إذا كان يمكن معالجة وظيفة dosomething. يمكن تعديل الكود على النحو التالي لحل هذه المشكلة.
نسخة الكود كما يلي:
var rs = fs.createadStream (SRC) ؛
Rs.on ('Data' ، function (chunk) {
rs.pause () ؛
dosomething (chunk ، function () {
Rs.Resume () ؛
}) ؛
}) ؛
rs.on ('end' ، function () {
تنظيف()؛
}) ؛
تتم إضافة رد الاتصال إلى وظيفة dosomething ، حتى نتمكن من إيقاف قراءة البيانات قبل معالجة البيانات ومواصلة قراءة البيانات بعد معالجة البيانات.
بالإضافة إلى ذلك ، يمكننا أيضًا إنشاء دفق بيانات الكتابة فقط لهدف البيانات ، على النحو التالي:
نسخة الكود كما يلي:
var rs = fs.createadStream (SRC) ؛
var ws = fs.createwRiteStream (dst) ؛
Rs.on ('Data' ، function (chunk) {
ws.write (قطعة) ؛
}) ؛
rs.on ('end' ، function () {
ws.end () ؛
}) ؛
بعد استبدال Dosomething بكتابة البيانات في دفق الكتابة فقط ، يبدو الرمز أعلاه وكأنه برنامج نسخ الملفات. ومع ذلك ، فإن الكود أعلاه لديه المشاكل المذكورة أعلاه. إذا لم تتمكن سرعة الكتابة من مواكبة سرعة القراءة ، فستنفجر كتابة ذاكرة التخزين المؤقت داخل دفق البيانات فقط. يمكننا الحكم على ما إذا كان قد تمت كتابة البيانات الواردة إلى الهدف أو وضعت مؤقتًا في ذاكرة التخزين المؤقت بناءً على قيمة الإرجاع لطريقة .Write ، والحكم على بيانات الكتابة فقط إلى الهدف بناءً على حدث التصريف ، وتمرير البيانات التالية المراد كتابة. لذلك فإن الرمز كما يلي:
نسخة الكود كما يلي:
var rs = fs.createadStream (SRC) ؛
var ws = fs.createwRiteStream (dst) ؛
Rs.on ('Data' ، function (chunk) {
if (ws.write (chunk) === false) {
rs.pause () ؛
}
}) ؛
rs.on ('end' ، function () {
ws.end () ؛
}) ؛
ws.on ('drain' ، function () {
Rs.Resume () ؛
}) ؛
أخيرًا ، يتحقق نقل البيانات من دفق البيانات للقراءة فقط إلى دفق البيانات للكتابة فقط ، ويتم تضمين التحكم في المستودعات المقاومة للانفجار. نظرًا لوجود العديد من سيناريوهات الاستخدام ، مثل برنامج نسخ الملفات الكبير أعلاه ، توفر Nodejs مباشرة طريقة .pipe للقيام بذلك ، وتشبه طريقة التنفيذ الداخلية الخاصة به الرمز أعلاه.
فيما يلي عملية أكثر اكتمالا لنسخ الملفات:
نسخة الكود كما يلي:
var fs = require ('fs') ،
المسار = يتطلب ('المسار') ،
out = process.stdout ؛
var filepath = '/bb/bigbear.mkv' ؛
var readStream = fs.createadStream (FilePath) ؛
var writestream = fs.createwRiteStream ('file.mkv') ؛
var stat = fs.statsync (filePath) ؛
var totalsize = stat.size ؛
var passedlength = 0 ؛
var lastsize = 0 ؛
var startTime = date.now () ؛
readstream.on ('Data' ، function (chunk) {
passedLength += chunk.length ؛
if (writestream.write (chunk) === false) {
readstream.pause () ؛
}
}) ؛
readstream.on ('end' ، function () {
Writestream.end () ؛
}) ؛
Writestream.on ('drain' ، function () {
readstream.resume () ؛
}) ؛
setTimeOut (وظيفة العرض () {
var ٪ = math.ceil ((passedLength / totalsize) * 100) ؛
var size = math.ceil (passedLength / 1000000) ؛
var diff = size - lastsize ؛
يدوم = الحجم ؛
out.clearline () ؛
out.cursorto (0) ؛
out.write ('exced' + size + 'mb ،' + ٪ + '٪ ، السرعة:' + diff * 2 + 'mb/s') ؛
if (passedLength <totalsize) {
setTimeout (show ، 500) ؛
} آخر {
var endtime = date.now () ؛
console.log () ؛
console.log ('عند المشاركة:' + (endtime - startTime) / 1000 + 'Seconds.') ؛
}
} ، 500) ؛
يمكنك حفظ الرمز أعلاه باسم "copy.js". التجربة: لقد أضفنا setTimeOut متكرر (أو استخدم SetInterval مباشرة) ليكون متفرجًا.
راقب تقدم الإكمال كل 500 مللي ثانية ، واكتب الحجم والنسبة المئوية وسرعة النسخ إلى وحدة التحكم معًا. عند الانتهاء من النسخ ، يتم حساب الوقت الإجمالي.
ثلاثة ، دعنا نلخص
(1) فهم مفهوم الدفق.
(2) كفاءة في استخدام واجهة برمجة تطبيقات الدفق ذات الصلة
(3) انتبه إلى التحكم في التفاصيل ، مثل: نسخ الملفات الكبيرة ، وذلك باستخدام شكل "بيانات قطعة" للتخطي.
(4) ، استخدام الأنابيب
(5) ، التأكيد على مفهوم مرة أخرى: اتصال TCP هو دفق قابل للقراءة ودفق قابل للكتابة ، في حين أن اتصال HTTP مختلف. كائن طلب HTTP هو دفق قابل للقراءة ، في حين أن كائن استجابة HTTP هو دفق قابل للكتابة.