## Szerver oldali JavaScript **9. hét** Core modulok: cluster, events, fs, stream, http -- PAGE -- ### Miért most? Elegendő tapasztalat és tudás gyűlt össze bennetek, hogy ne csak egy "api listát" lássunk, hanem ténylegesen meg is maradjon valami. :D Pár hasznos funkciót mutatok, amit nem mindenki fog használni. -- PAGE -- ### Miről lesz szó - Cluster module - Events - FS - Stream - HTTP, HTTPS - OS -- PAGE -- ### Cluster Node egy threadon fut - mit tegyünk ha skáláznánk több magra: **cluster** Saját magát tudja forkolni egyszerűen ```js const cluster = require('node:cluster'); const http = require('node:http'); const numCPUs = require('node:os').cpus().length; if (cluster.isMaster) { // Fork workers. for (const i = 0; i < numCPUs; i++) { cluster.fork(); } } else { // Workers can share any TCP connection http.createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); } ``` -- PAGE -- ### Cluster Hogyan kommunikáljak threadek között? Master oldalán: ```js worker.on('message', (msg) => { worker.send('Hey worker'); }); ``` Worker oldalán: ```js process.on('message', (msg) => { console.log('From master', msg); }); process.send('To master: Hey'); ``` -- PAGE -- ### Events A node egyik működési köve: eventek Feliratkozás: ```js const EventEmitter = require('node:events'); const server = new EventEmitter(); server.on('connection', (stream) => { console.log('someone connected!'); }); ``` Esemény meghívása: ```js server.emit('connection',theStream); ``` -- PAGE -- ### Events Egyszeri meghívásra is fel lehet iratkozni: ```js server.once('connection', (stream) => { console.log('only once thanks'); }); ``` Le is lehet iratkozni: ```js const cb = (stream) => { console.log('someone connected!'); }; server.on('connection', cb); // ... server.removeListener('connection', cb); ``` -- PAGE -- ### Events - adminisztráció Listenerek lekérdezése: ```js console.log(server.listeners('connection')); // [ [Function] ] ``` Összes feliratkozás eltávolítása ```js server.removeAllListeners('connection'); ``` Feliratkozás limitálása ```js server.setMaxListeners(10); ``` -- PAGE -- ### Events Ez is **szinkron**, így lényegében csak "szépítés"!!! Sosem hívódik meg a console.log ```js var EventEmitter = require('node:events'); setTimeout(() => console.log('im here'), 10); const emitter = new EventEmitter(); emitter.on('msg',(message) => { }); while (true){ emitter.emit('msg','boo'); } ``` -- PAGE -- ### Events Ha meg akarsz törni egy event loopot, akkor a setImmediate, nextTick amit hívnod kell. ```js const EventEmitter = require('node:events'); setTimeout(() => console.log('im here'), 10); const emitter = new EventEmitter(); emitter.on('msg',(message) => {}); function loop(){ emitter.emit('msg','boo'); setImmediate(loop); } loop(); ``` -- PAGE -- ### FS Minden ami fájlrendszer. Nézzük meg miket tud: #### Könytárak szintjén - **könyvtár létrehozás:** fs.mkdir(path[, mode], callback) - mode unix alapokon izgalmas: 0777 - **könyvtár own:** fs.chown(path, uid, gid, callback) - **könyvtár átnevezés:** fs.rmdir(path, callback) - **könyvtár törlés:** fs.unlink(path, callback) - **könyvtár tartalmának betöltése:** fs.readdir(path, callback) - callback: (err, files) a . és .. nélkül -- PAGE -- ### FS #### Fájl szintjén - **fájl megnyitás:** fs.open(path, flags[, mode], callback) - C++ alapok alapján lényegében: - flags: r, w, a stb. - mode: fájl mód - 0666 - **fájl olvasás:** fs.read(fd, buffer, offset, length, position, callback) - position: null ha az aktuális cursortól - callback: (err, bytesRead, buffer) -- PAGE -- ### FS #### Fájl szintjén - **fájl írás - bináris adatok:** fs.write(fd, buffer, offset, length[, position], callback) - callback: (err, written, buffer) - **fájl írás - szöveges mód:** fs.write(fd, data[, position[, encoding]], callback) - **fájl bezárása:** fs.close(fd, callback) - **fájl végére írás egy lépésben:** fs.appendFile(file, data[, options], callback) - options: encoding, mode, flag vagy kombinációi -- PAGE -- ### FS.watch Fel lehet iratkozni fájlrendszer változásaira: - **fájlok/könyvtárak figyelése:** fs.watch(filename[, options][, listener]) - options: { persistent: true, recursive: false, encoding: ... } - listener: (event, filename) - change code/watch.js - rename code/watch2.js (ez a mentés pl) -- PAGE -- ### FS.watch - **fájlok figyelése:** fs.watchFile(filename[, options], listener) - callback: (currStat, prevStat) fájl statisztikák (létrehozás, módosítás, stb) - **fájl figyelés felfüggesztése:** fs.unwatchFile(filename[, listener]) -- PAGE -- ### Stream Olvasásra / irásra képes abstract interface. 4 típus van: - Readable - Writable - Duplex: az előző 2 - Transform Fájlok, udp / tcp kapcsolatok, bármi absztakt. -- PAGE -- ### Stream A streameke esetében fel kell iratkozni az egyes eseményekre (eventek). #### Readable stream eventek ```js const readable = getReadableStreamSomehow(); readable.on('data', (chunk) => { console.log('got %d bytes of data', chunk.length); }); readable.on('end', () => { console.log('there will be no more data.'); }); ``` Lehetséges az adatok küldését leállítani / újraindítani: ```js readable.pause(); readable.resume(); ``` -- PAGE -- ### Stream #### Writeable stream Írni lehet... ```js writable.write(chunk[, encoding][, callback]) writable.end([chunk][, encoding][, callback]) ``` Itt is vannak eventek: ```js writable.on('drain', () => { console.log('erdemes ujra irni'); }); writable.on('finish', () => { console.log('minden bit odaert'); }); ``` -- PAGE -- ### Stream Viszont ami még izgalmasabb, ezeket egymásba lehet **drótozni**. `file.txt`-ból készítsünk egy másolatot `filecopy.txt` néven ÉS ugyanennek a fájlnak a tömörített változatát mentsük el `file.txt.gz` néven. ```js const r = fs.createReadStream('file.txt'); const z = zlib.createGzip(); const w = fs.createWriteStream('filecopy.txt'); const wzip = fs.createWriteStream('file.txt.gz'); const zout = r.pipe(z); zout.pipe(wzip); r.pipe(w); r.on('end',() => console.log('olvasas vege')); ``` -- PAGE -- ### HTTP és HTTPS Listen része már ismerős: ```js const http = require("http"); const server = http.createServer(function(request, response) { //.... }); ``` -- PAGE -- ### HTTP és HTTPS De hívást kezdeményezni is lehet! ```js const req = http.request({ hostname: 'www.google.com',port: 80, path: '/', method: 'GET' }, (res) => { res.setEncoding('utf8'); console.log(res.statusCode, res.headers); const chunk = ''; res.on('data', part => chunk+=part); res.on('end', () => console.log('BODY: ' + chunk)); }); req.on('error', (err) => console.log('problem with request: ' + e.message)); req.end(); ``` -- PAGE -- ### HTTP és HTTPS POST is működik, csak nevezebb összehozni :) ```js const postData = querystring.stringify({ 'msg' : 'Hello World!' }); const options = { hostname: 'www.google.com', port: 80, path: '/upload', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': postData.length } }; const req = http.request(options, (res) => { /* ... */ }); req.write(postData); req.end(); ``` -- PAGE -- ### Axios !!!! ```js axios.post('http://www.google.com/upload', querystring.stringify({msg: 'Hello World!'}), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(response => { /* ... */ }) .catch(error => { /* ... */ }); ``` https://axios-http.com/ -- PAGE -- ### OS OS szintű, inkább izgalmas mint hasznos funkciók: - **könyvtárak:** os.tmpdir(), os.homedir() - **egyszerű statisztikák:** os.uptime(), os.loadavg(), os.totalmem(), os.freemem(), os.cpus(), os.networkInterfaces() - **hasznos tulajdonságok**: os.hostname(), os.type(), os.platform(), os.arch(), os.release() - os.endianness(), os.EOL -- PAGE -- ### Error.stack trükk Ha szeretnéd megtudni, hogy egy adott pontra "hogyan jutott" az irányítás: ```js console.log ((new Error()).stack) ``` Hasznos tud lenni, ha egy hibával / izgalmas működéssel találkozunk, és tudni szeretnénk milyen callstack van mögötte -- PAGE -- ### Mit fogunk gyakorlaton csinálni? Ha lesz - valami random dolgot.