Node.js-ում հոսքերի օգտագործման ներածություն


Node.js-ի հոսքերը կարող են բարդ լինել, բայց արժե ժամանակ տրամադրել դրանք հասկանալու համար:

Հիմնական Takeaways

  • Node.js-ի հոսքերը հիմնարար գործիք են տվյալների մշակման և փոխանցման համար՝ դրանք դարձնելով իդեալական իրական ժամանակի և իրադարձությունների վրա հիմնված հավելվածների համար:
  • Node.js-ում գրելի հոսք ստեղծելու համար կարող եք օգտագործել fs մոդուլի createWriteStream() ֆունկցիան, որը տվյալներ է գրում կոնկրետ վայրում։
  • Ընթեռնելի, գրավոր, դուպլեքս և փոխակերպվող հոսքերի չորս տեսակներն են Node.js-ում, որոնցից յուրաքանչյուրն ունի իր օգտագործման դեպքն ու ֆունկցիոնալությունը:

Հոսքը հիմնարար ծրագրավորման գործիք է, որը զբաղվում է տվյալների հոսքով: Իր հիմքում հոսքը սովորաբար ներկայացնում է բայթերի հաջորդական փոխանցումը մի կետից մյուսը: Node.js-ի պաշտոնական փաստաթղթերը հոսքը սահմանում են որպես վերացական ինտերֆեյս, որը կարող եք օգտագործել տվյալների հետ աշխատելու համար:

Համակարգչով կամ ցանցով տվյալների փոխանցումը հոսքի իդեալական օգտագործում է:

Հոսում է Node.js-ում

Stream-ները էական դեր են խաղացել Node.js-ի հաջողության մեջ: Դրանք իդեալական են իրական ժամանակի տվյալների մշակման և իրադարձությունների վրա հիմնված հավելվածների համար՝ Node.js գործարկման միջավայրի երկու նշանավոր առանձնահատկությունները:

Node.js-ում նոր հոսք ստեղծելու համար դուք պետք է օգտագործեք հոսքի API-ն, որն աշխատում է բացառապես Strings-ի և 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–9999): Այնուամենայնիվ, երբ գործարկում եք վերը նշված սցենարը, այն նույն գրացուցակում ստեղծում է ֆայլ, որը պարունակում է հետևյալ տվյալները.

Թվերի ներկայիս հավաքածուն ավարտվում է 2915-ով, սակայն այն պետք է ներառեր մինչև 9999 թվեր։ Այս անհամապատասխանությունը տեղի է ունենում, քանի որ յուրաքանչյուր WriteStream օգտագործում է բուֆեր, որը պահում է ֆիքսված քանակությամբ տվյալներ միաժամանակ: Իմանալու համար, թե որն է այս լռելյայն արժեքը, դուք պետք է դիմեք highWaterMark տարբերակը:

console.log("The highWaterMark value is: " +
  myWriteStream.writableHighWaterMark + " bytes.");

Անանուն ֆունկցիային վերը նշված կոդի տողը ավելացնելով տերմինալում կստեղծվի հետևյալ ելքը.

Տերմինալի ելքը ցույց է տալիս, որ լռելյայն highWaterMark արժեքը (որը հարմարեցված է) 16384 բայթ է: Սա նշանակում է, որ այս բուֆերում կարող եք միաժամանակ պահել միայն 16384 բայթից ցածր տվյալներ: Այսպիսով, մինչև 2915 թիվը (գումարած բոլոր ստորակետերն ու բացատները) ներկայացնում է տվյալների առավելագույն քանակը, որը բուֆերը կարող է պահել միաժամանակ:

Բուֆերային սխալի լուծումը հոսքային իրադարձություն օգտագործելն է: Հոսքը հանդիպում է տարբեր իրադարձությունների տվյալների փոխանցման գործընթացի տարբեր փուլերում: drain միջոցառումը հարմար տարբերակ է այս իրավիճակի համար:

Վերևի writeData() ֆունկցիայի մեջ զանգը WriteStream-ի write() ֆունկցիային վերադարձնում է true, եթե տվյալների զանգվածը (կամ ներքին բուֆերը) գտնվում է highWaterMark-ից ցածր: արժեքը. Սա ցույց է տալիս, որ հավելվածը կարող է ավելի շատ տվյալներ ուղարկել հոսքին: Այնուամենայնիվ, հենց write() ֆունկցիան վերադարձնում է false, հանգույցը ընդհատվում է, քանի որ դուք պետք է ցամաքեցնել բուֆերը:

myWriteStream.on('drain', () => {
  console.log("a drain has occurred...");
  writeData();
});

Վերևում drain իրադարձության ծածկագիրը անանուն ֆունկցիայի մեջ տեղադրելու դեպքում 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(chunk) ֆունկցիան կամ օգտագործում եք ընթեռնելի իրադարձությունը:

բաց իրադարձությունը գործարկվում է, երբ 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);
});

The 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-ն ունի երկու տեսակի Transform հոսքեր՝ zlib և crypto հոսքեր: 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);
})();

Այս պարզ սցենարը վերցնում է բնօրինակ տեքստային ֆայլը, սեղմում այն և պահում ընթացիկ գրացուցակում: Սա պարզ գործընթաց է ընթեռնելի հոսքի pipe() մեթոդի շնորհիվ: Հոսքի խողովակաշարերը հեռացնում են բուֆերների և խողովակների տվյալների օգտագործումը անմիջապես մի հոսքից մյուսը:

Այնուամենայնիվ, նախքան տվյալները սկրիպտում գրվող հոսքին հասնելը, մի փոքր շրջանցում է կատարվում zlib-ի createGzip() մեթոդի միջոցով: Այս մեթոդը սեղմում է ֆայլը և վերադարձնում նոր 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 ֆայլը, կտեսնեք, որ այն պարունակում է նույն տվյալները, ինչ սկզբնական ֆայլը.

Ինչու՞ են հոսքերը կարևոր:

Հոսքերը բարձրացնում են տվյալների փոխանցման արդյունավետությունը: Ընթեռնելի և գրավոր հոսքերը ծառայում են որպես հիմք, որը հնարավորություն է տալիս հաղորդակցվել հաճախորդների և սերվերների միջև, ինչպես նաև սեղմել և փոխանցել մեծ ֆայլեր:

Հոսքերը նաև բարելավում են ծրագրավորման լեզուների աշխատանքը: Առանց հոսքերի տվյալների փոխանցման գործընթացը դառնում է ավելի բարդ՝ պահանջելով ավելի մեծ ձեռքով մուտքագրում մշակողներից և հանգեցնելով ավելի շատ սխալների և կատարողականի խնդիրների: