A Javascript programozási nyelv

Szerver oldali JavaScript



A JavaScriptet szerver oldali scriptelésre is használhatjuk, ilyenkor a PHP-hez hasonlóan működik: a HTML oldalba ágyazzuk a kódot, amit a szerver az oldal lekérésekor lefuttat, és a kód helyére beszúrja annak kimenetét a HTML oldalba. A szerver oldali script kapcsolódhat egy adatbázishoz, vagy hozzáférhet a szerver fájlrendszeréhez is. Ezzel helyettesíthetjük a CGi programozást, sőt, egy annál rugalmasabb eszközt kapunk.

A szerver oldali scriptek futtatása a klienssel szemben semmilyen követelményt nem állít, viszont a szerver webkiszolgálóját fel kell készíteni a futtatására, azaz szükség van egy ún. JavaScript runtime engine-re. Ilyen található a Netscape Enterprise Serverben, Application Manager néven.

Ezek a scriptek egy bytekódban tárolódnak, hogy futtatásuk gyorsabb legyen. A HTML és JavaScript forrásokból a jsac nevű fordító készíti el ezt a bytekódot, amit installálni is kell, hogy futtatni tudjuk. Az installálás csak abból áll, hogy a futtató rendszernek megadjuk az alkalmazás helyét, és néhány, a futtatást befolyásoló paramétert. Ezután az alkalmazás bármikor leállítható és újraindítható, sőt módosíthatunk is rajta újratelepítés nélkül.

Szkript beágyazása HTML-be

A szerver oldali scriptet a <server></server> tagok közé kell írnunk. Az itt lévő kódot a szerver nem fogja elküldeni, hanem lefuttatja, és annak kimenete kerül a kód helyére.

Kommunikáció a szerver és a kliens oldali JavaScript között

Szerver oldali szolgáltatások

Ezeket a szolgáltatásokat nem tárgyaljuk részletesen, csak a lehetőségeket soroljuk fel. Bővebb információ a http://docs.iplanet.com/docs/manuals/ssjs/1_4/ssjs.pdf címen található leírásban érhető el.

NoSQL adatbázisok

A NoSQL egyes értelmezések szerint a „nem csak SQL”, más értelmezések szerint „nem SQL” adatbázisok gyűjtőneve. Ezek az adatbázisok többnyire nem táblázatokban tárolják az adatokat és nem nagyon használnak SQL-t az adatok lekérdezésére. A műveleteket illetően, a NoSQL adatbázisok az írás és olvasás műveletekre vannak kihegyezve, mivel hatalmas mennyiségű adatok kezelésére tervezték őket.

A NoSQL adatbázis rendszereket több típusba sorolhatjuk az alapján, hogy hogyan tárolják az adatokat.

Az egyik legelterjedtebb ilyen típus, az ún. dokumentum alapú adatbázisok, dokumentumtárolók. A központi szerepet a dokumentum tölti be. A dokumentum valamilyen formában tárolt információ, amelyeknek formátuma gyakran XML vagy JSON. A dokumentumok abban térnek el a relációs adatbázisok tábláitól, hogy a dokumentumok teljesen eltérő struktúrával rendelkezhetnek.

JSON adatformátum

{ "name": "document", "type": "JSON", "data": [1, 2, 3], "database": "CouchDB", "version": 2 }

CouchDB

CouchDB logó

A CouchDB egy nyílt forráskódú NoSQL adatbázis rendszer. A dokumentum alapú tárolási szemléletet követi. A dokumentumokat JSON formátumban tárolja és a JavaScript nyelvet használja, mint lekérdező nyelvet.

Dokumentumtárolás

Az adatbázis az adatokat, mint dokumentumokat tárolja, amelyek mező/érték párokkal vannak reprezentálva JSON formában. Az értékek típusa lehet string, szám, dátum, de akár rendezett lista és asszociatív tömb is. Minden dokumentumnak van egy egyedi azonosítója és egy verzió száma, amely minden egyes módosításkor változik. Lehetőség van az előző verziók megtekintésére.

Map/Reduce nézetek

A tárolt adatokat ún. nézeteken keresztül kérdezhetjük le. Minden ilyen nézet egy JavaScript függvényként épül fel, amelyet mi magunk írhatunk meg.

Példa:

function(doc) { emit(doc._id, doc); }

Eredmény:

View példa

REST API

Az összes elemnek van egy egyedi URI címe, amely http elérést biztosít az adatokhoz. A REST olyan HTTP metódusokat használ, mint a POST, GET, PUT, DELETE az négy alap művelethez (Kreálás, Olvasás, Frissítés, Törlés).

Node.js

Node.js logó

Alapok

A Node.js (vagy más néven Node) egy rendszer, melyben JavaScriptben írhatunk szerver oldali alkalmazásokat. Maga a rendszer C/C++-ban íródott, és egy esemény alapú I/O rendszert takar a Google V8 JavaScript motorja felett. A Node célja, hogy egy egyszerű felületet adjon skálázható hálózati alkalmazások írására. Azt tudjuk, hogy a JavaScript a világon a jelenleg legnépszerűbb programozási nyelv, többek között ezért esett erre a nyelvre a választás. A JavaScript nyelvnek vannak más sajátosságai, amelyek szintén előtérbe kerülnek a rendszer használata során. Ahogy a böngészőben, úgy Node alatt is egyetlen esemény hurokban (event loop) fut a program, és ez adja az egyik legfontosabb előnyt.

A Node.js-t úgy írták meg, hogy (szinte) minden esemény aszinkron legyen, ezért a program sosem blokkolódik, azaz nem kell várni, hogy egy művelet befejeződjön, vele párhuzamosan futtathatunk további műveleteket. Ez pontosan úgy működik, mint a böngészőben levő XHR kérések, vagy más események, mint például a click, mouseover. Ez az alkalmazás folyamatosabb futását, több alkalmazás párhuzamosítását, valamint az egész rendszer optimálisabb működését teszi lehetővé.

A közhiedelemmel ellentétben a Node.JS és a böngésző jelentősen különböznek egymástól, annak ellenére hogy mindkettő a JavaScript nyelven alapszik. A node.js veszi az alap nyelvet akárcsak bármelyik böngésző motor, és különböző API-kkal bővíti azt, ezzel lehetőséget nyújtva szerveroldali műveletekre, hálózati feladatok ellátására.

Globális objektum

A böngészőben a window a globális objektum, bármi amit window property-ként definiálunk, elérhető lesz a weboldal teljes futása alatt bárhonnan. Fontos megemlíteni hogy minden globálisan var kulcsszó nélkül hivatkozott és elérhető változó a window objektum egyik attribútuma. Például setTimeout, document, stb. A node is rendelkezik két hasonló objektummal amik biztosítják a könnyű szeparálhatóságot:

Hasznos globális változók

A böngészőben definiált globális változók nagy része, nem a nyelv specifikációja miatt áll rendelkezésre, sok esetben a böngésző vendorok adtak hozzá a webes alkalmazások fejlesztésének megkönnyítéséért. Például a setTimeout nem része az ECMAScript szabványnak.

A node sok olyan API-t tartalmaz amik már proposalként jelen vannak az ECMAScript dokumentációjában, viszont a szerveroldali müködéshez elengedhetetlenek voltak. Ilyen például a setImmediate API-t megvalósító process.nextTick.

Modulok működése / importálása

Böngészőben jelenleg nem rendelkezünk module implementációval (ECMAScript 7-ben már érkezik), tehát ha bármilyen thirdparty scriptet adunk hozzá az oldalhoz akkor globális változóként válik elérhetővé. Fontos megemlíteni, hogy ennek ellenére készültek már egyedi modukezelől implementációk mint például a RequireJS.

A node ezzel ellentétben natív modul rendszerrel rendelkezik, amit a require, module és export globális változó hármas biztosít számunkra.

Absolute és relative modulok

Ha például szeretnénk egy globális module-t importálni, akkor ezt egy egyszerű require(example’) paranccsal tehetjük meg, ekkor a globális node könyvtárban lévő modulok között keresi majd az interpreter a modulunkat.

require(‘example’);

Ellenben ha saját mappába telepítettük a modulunkat, akkor lehetőség van relative modul importálásra is, ezt egyszeruen a relative elérés megadásával tehetjük meg:
require(‘./node_modules/example’);

Modul API exportálás

Ha szeretnénk egy saját API-t exportálni az adott modul-ból, ezt a module és az exports globális változók segítségével tehetjük meg. Ekkor a require hívás ezt az API-t kapja vissza.
Alapból minden module egy üres object literált ({}) ad vissza, és ha szeretnénk ehhez új értékeket adni akkor az exports változót kell bővíteni.
Például (example_module):

exports.name = ‘prognyelvek’; exports.data = ‘dokumentacio példa’;
Ekkor require után mindkét változót elérjük:
var a = require(‘example_module’); console.log(a.name); //prognyelvek console.log(a.data); //dokumentáció példa
Ilyen esetekben az exports változó a globális module.exports-nak felel meg. Ha szeretnénk egy egész objektumot, függvényt, vagy akár bármit visszaadni, azt is megtehetjük az egyszerű értékadással.
Például:
module.exports = Person; function Person (name) { this.name = name; }
egy egyszerá require pedig az egész objectet kapja vissza értékül:
var Person = require(‘./person’);

Események

Az egyik legfontosabb API az EventEmmiter. A böngészőhöz hasonlóan a Node.js is az eseményalapú működésen alapszik. A böngészőben az események triggereléséért a DOM felel, viszont Node-ban mi is bármikor emitelhetünk saját eseményeket. Az EventEmmiter a a process-en belül érhető el.

Eseménykezelés és kilövés példa:

var EventEmitter = require(‘events’).EventEmitter, a = new EventEmitter; a.on(‘event’, function () { console.log(‘event called’); }); a.emit(‘event’);

Emellett akár saját objektumokat is felruházhatunk eseménykezeléssel:
var EventEmitter = process.EventEmitter, MyClass = function (){}; MyClass.prototype._proto__ = EventEmitter.prototype;

Ahogy korábban olvashattuk a Node.JS non-blocking rendszerének egyik kulcsfontosságú tagjai az események. A legtöbb esetben szinte soha nem válaszol azonnali adattal egy adott kérésre, minden események formájában kaphatjuk meg azokat.

Példaként gondoljunk egy HTTP szerverre. Amikor beérkezik egy request és meghívódik a request handler esemény, az adat nem azonnal áll rendelkezésre. Az alábbi példában a ServerRequest objektum az EventEmitter-ből származik, és az adatok megérkezése folyamatosan érkezik. A végét az end esemény jelzi. Ennek a működésnek köszönhetően az adatokat bufferbe helyezhetjük, vagy akár folyamatosan fel is dolgozhatjuk.
Példa:
http.Server(function (req, res) { var buf = ‘’; req.on(‘data’, function (data) { buf += data; }); req.on(‘end’, function () { console.log(‘All the data is ready!’); }); });

Bufferek

A Buffer egy globális objemtum ami egy fixen allokált memória területet reprezentál (ezért inicializálásnál szükséges a méret megadása) és úgy viselkedik mint egy octet-ekből álló tömb. Segítségükkel bináris adatokat tárolhatunk.

Az egyik legfontosabb képességük pedig az, hogy az adatot különböző enkódolások között koncertálhatjuk. Például egy készíthetünk egy base64 kép reprezentációt tartalmazó buffert, amit utána bináris formában fájlba írunk.
Példa:

var mybuffer = new Buffer(‘==ii1j2i3h1i23h’, ‘base64’): console .log(mybuffer); require(‘fs’).writeFile(‘logo.png’, mybufffer);

File-ok kezelése (fs modul)

Az egyik legtöbbet használt Node modul az fs, aminek segítségével fájlműveleteket hajthatunk végre. Sok esetben szükség van szinkron és aszinkron műveletekre egyaránt, ezért mindkét módszerhez szükséges metódusok rendelkezésünkre állnak.

Példa:

//modul importálása var fs = require(‘fs’); //aszinkron mappa olvasás fs.readdir(‘./example’, function (err, files) { console.log(files); }); //Ugyanez teljesen szinkron működéssel var files = fs.readdirSync(‘./example’);

Streamek

A streamek eredete egészen a UNIX rendszerek létezésének első napjaiig vezethető vissza, és azóta mindenhol jelen vannak. Ha sima unix parancsokra gondolunk akkor ott a | (pipe) jel segítségével használhattuk őket. A Node.js egy belső stream modul implementációval rendelkezik ami a unix rendszerhez hasonlóan szintén a pipe() metódus használatára épül. Segítségükkel komplex adatfolyamokat hozhatunk létre, ahol minden kezelő osztály a folyamat egy újabb állapotát kapja bemenetül, és az elvégzett műveletek után, a módosított állapotot adja tovább a következő kezelőnek.

Miért használjuk a streameket?

Példa:

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(data); }); }); server.listen(8000);

Ez a példa egy file-t szolgál ki adott http request esetében, viszont eléggé nagy memóriahasználattal jár ha nagy file-ról beszélünk, ugyanis minden egyes request-nél a teljes data.txt-t betölti a memóriába mielőtt visszaküldi azt a kliens-nek. Ez egy kliens esetében nem okoz gondot, viszont ha párhuzamosan több klienst próbálunk kiszolgálni ez óriási memóriahasználathoz vezethet.

Szerencsére itt is jelen vannak a streamek, ugyanis a req és a res callback paraméterek ebbe a csoportba tartoznak. Ennek köszönhetően használhatjuk a fs.createReadStream() -t fs.readFile helyett:
var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { var stream = fs.createReadStream(__dirname + '/data.txt'); stream.pipe(res); }); server.listen(8000);

Itt a pipe kezeli a data és end eseményeket az fs.createReadStream-ből. Nem tisztább a kód, viszont mostmár a data.txt egyenesen a kliens-hez lesz irva chunk-okra bontva. A pipe használata más pozitív hozadékkal bír, ugyanis az eseményalapú működésnek köszönhetően, nem bufferel feleslegesen új adatokat a memóriába ha a kliens lassu, vagy rossz válaszidejű.

Ha tömörítést szeretnénk akkor azt is könnyen megoldhatjuk a stream modullal, hiszen bármilyen műveletet elvégezhetunk a kapott adatokon:
Példa:
var http = require('http'); var fs = require('fs'); var oppressor = require('oppressor'); var server = http.createServer(function (req, res) { var stream = fs.createReadStream(__dirname + '/data.txt'); stream.pipe(oppressor(req)).pipe(res); }); server.listen(8000);

Stream típusok

Olvasható streamek
A Readable streamek adatok állítanak elő amit minden writeable, transform és duplex stream olvashat a pipe segítségével.

Példa:
var Readable = require('stream').Readable; var rs = new Readable; rs.push('beep '); rs.push('boop\n'); rs.push(null); //ezzel jelezzük hogy vége az adat ouputnak rs.pipe(process.stdout);
Írható streamek
Olyan streamek amelyekbe tudunk pipe-olni, viszont belőlük már nem lehet.

Példa:
var Writable = require('stream').Writable; var ws = Writable(); ws._write = function (chunk, enc, next) { console.dir(chunk); next(); }; process.stdin.pipe(ws);

Az első paraméter a chunk az adat amit az előállító írt. a második paraméter az enc ami az enkódolást tartalmazza a harmadik paraméter pedig a next(err) egy callback ami elmondja az íróknak hogy írhatnak-e több adatot.
Klasszikus streamek
A streamek több implementációval is rendelkeztek a node készítése során, viszont a klasszikus változat még most is elérhető. Ismeretük fontos, hiszen rengeteg helyen használják még, és a törlésük nem tervezett a jövőben.
Klasszikus olvasható streamek
Olyan event emmiterek amik data és end eseményeket tudnak kilőni.

Példa:
var Stream = require('stream'); var stream = new Stream; stream.readable = true; var c = 64; var iv = setInterval(function () { if (++c >= 75) { clearInterval(iv); stream.emit('end'); } else stream.emit('data', String.fromCharCode(c)); }, 100); stream.pipe(process.stdout);
Olvasás példa:
process.stdin.on('data', function (buf) { console.log(buf); }); process.stdin.on('end', function () { console.log('__END__'); });
Klasszikus írható streamek
használatuk egyszerű, .write(buf), .end(buf), .destory metódusokkal rendeleznek.
transform streamek
Sokszor átmeneti streameknek is nevezzük, olvasható és írható streamek amelyiket átalakítják az inputot és továbbadják az outputban.
duplex streamek
Írható és olvasható streamek, viszont mindkét irányban történhet interakció akárcsak egy telefon vonal.

Példa:
a.pipe(b).pipe(a)

A CLI használata

Köszönhetően annak hogy egy szerveroldali nyelvről beszélünk a command-line használata elengedhetetlen, ezért az itt átadok argumentumok olvasásának ismerete is fontos.

argv
A process.argv tartalmazza a futtatáskor átadott értékeket a megfelelő kulcs alatt. Az első elem mindig ‘node’ a másik pedig a path ahol a file-t futtatjuk. Sokszor érdemes ezeket már az elején eltávolítani a tömbből vizsgálat előtt egy egyszerű process.argv.slice(2) hívással.

Express js

Az express js egy olyan nodejs framework ami igyekszik egyszerűvé tenni a webalkalmazások fejlesztését node alapokon. A connect modul-ra épülve egy olyan API-t definiál ami elfedi számunkra a nyelv nehézségeit. A telepítése megegyezik bármely más node moduléval:

npm install -g express
Példa egy egyszer hello world-öt kiíró weboldalra express-ben:
//modulok importálása var express = require("express"); var http = require("http"); //az app elkészítése var app = express(); app.use(function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello world!\n"); }); //Elindítás és az 1337-es porton figyelés http.createServer(app).listen(1337);
Routing
Express-ben lehetőségünk van a framework általt nyújtott routing használatára. Ezzel könnyen vezérelhetjük az alkalmazást, illetve külön modulokra, fájlokra bonthatjuk a funkcionalitást.

Példa:
app.get("/", function(request, response) { response.end("Welcome to the homepage!"); }); app.get("/about", function(request, response) { response.end("Welcome to the about page!"); });
Átirányítás
Adott esetben a response objektumot használhatjuk átirányításra is a redirect metódus meghívásával.

Példa:
response.redirect("/example/url"); response.redirect(301, "http://www.elte.hu"); // HTTP státusz 301

Node telepítés

Szinte akármilyen operációs rendszerre telepíthetjük a Node-ot, Linuxtól kezdve a Mac OS X-en át a Solarisra. Fordíthatjuk ezen kívül Windows alatt Cygwin-en, FreeBSD-re és OpenBSD-re. A rendszer telepítése elég egyszerű, letöltjük az aktuális stabil változatot, konfiguráljuk, és fordítjuk (make). A fordításhoz szükségünk van egy C++ (gcc) fordítóra, és a Python 2.4 vagy újabb változatára.

$ mkdir node-latest-install $ cd node-latest-install $ curl http://nodejs.org/dist/node-latest.tar.gz | tar xz --strip-components=1 $ ./configure $ make install

Ez lefordítja a Node.js-t valamint a benne található motort is, azaz azt nem kell külön letölteni és telepíteni.

Node futtatás

Amennyiben a telepítés sikeresen lezajlott, mentsük el a következő JavaScript alkalmazást hello.js néven:

console.log('Hello'); setTimeout(function () { console.log('World!'); }, 2000)

Majd futtassuk a következőképpen:

$ node hello.js

Ekkor előbb a konzolon megjelenik, hogy Hello, majd 2 másodperccel később, hogy World!

Az egyik lényeges különbség például a PHP-hoz képest – azon kívül, hogy JavaScript-ben írjuk az alkalmazásunkat –, hogy nincsen szükségünk HTTP szerverre (mint amilyen az Apache httpd), mivel mi magunk írjuk a HTTP szerverünket. Írjuk is meg az első hasonló alkalmazásunkat httpszerver.js néven:

var http = require('http'); http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World\n'); }).listen(8124); console.log('Server running at http://127.0.0.1:8124/');

A következő módon tudjuk futtatni:

$ node httpszerver.js

Ekkor a programunk a 8124-es porton várakozik a HTTP kérésekre, és csak annyit ír ki a böngészőben a http://127.0.0.1:8124/ URL-re látogatva, hogy Hello World.

Modulok

NPM logó

A Node.js alapértelmezetten is rengeteg modullal érkezik, hasonlóan az előbb említett http modulhoz. A modulok a CommonJS specifikáció szerint íródtak, és ennek megfelelően a require('modulneve'); paranccsal tudjuk behúzni őket az alkalmazásunkba. Ezek egy objektumot tesznek elérhetővé, és egy változóhoz rendelve később kényelmesen tudjuk használni is őket. Az alap telepítésben elérhető modulokról a Node hivatalos API dokumentációjában olvashatunk.

A Node hivatalos csomagkezelője az npm, amellyel kényelmesen listázhatjuk, telepíthetjük, frissíthetjük és eltávolíthatjuk az elérhető modulokat, valamint mi magunk is publikálhatjuk az általunk fejlesztett modulokat az npm rendszerébe. Rendszerkövetelményei azonosak a Node-éval, telepítése egyszerű:

$ curl http://npmjs.org/install.sh | sh

Jelenleg több mint 600 modul érhető el az npm rendszerében. Természetesen a modulok minősége változó, de azért könnyű találni a konkrét problémát megoldó modulok közül a nekünk megfelelőt.

Az elérhető modulok listáját az npm ls paranccsal érhetjük el, és ezek nevében a split() parancsnak megfelelő paramétert átadva szűrhetünk is, tehát reguláris kifejezéseket is használhatunk. Például:

$ npm ls xml

Ez szűri azon modulokat, melyek nevében az „xml” szerepel.

Amennyiben további részletekre vagyunk kíváncsiak egy modullal kapcsolatban, használhatjuk a view parancsot, mely egy JSON objektumban írja le a modult, például nevét, leírását, szerzőjét, a repository-t, ahol elérhető a forráskód, a modul weboldalát stb.

A modulokat az install paranccsal telepíthetjük.

$ npm install underscore

A frissítésre az update, eltávolításra az uninstall parancs szolgál. További segítséghez használjuk a help utasítást.

Többszálúság

Egy Node alkalmazás egyetlen szálon fut, ami a többmagos processzorok korában talán furcsának tűnhet. Ugyanakkor nem is olyan bonyolult egy már létező alkalmazást többszálúra átalakítani. Az egész lényege, hogy az alkalmazásunkat úgy írjuk meg, hogy a parancssori paraméterekben fogadni tudja, hogy melyik porton is fogadja a kéréseket, valamint írunk egy pár soros proxy alkalmazást, ami ezekhez a most már párhuzamosan futtatható alkalmazásokhoz továbbítja a kéréseket.

Álljon itt egy minimális alkalmazás, ami csak annyit ad vissza válaszként a HTTP kérésekre, hogy Hello World @ [port]. Legyen ez az app.js:

var http = require('http'), // Vesszük az alkalmazásunknak átadott paraméterket, // és abból levesszük az első kettőt (`node` és `app.js`). args = process.argv.slice(2), // Ezen a porton fog az alkalmazásunk várakozni port = args.length && parseInt(args[0], 10) || 8080; http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World @ '+ port +'\n'); }).listen(port); console.log('Server running at http://127.0.0.1:%d/', port);

Írjuk meg hozzá a proxy.js alkalmazásunkat, ami ezeket a futó alkalmazásokat fogja hívogatni. Ehhez a http-proxy modult kell telepíteni. Sajnos ennek a modulnak rengeteg követelménye van, de npm-mel egyszerű telepíteni:

$ npm install http-proxy@0.3.0

var http = require('http'), httpProxy = require('http-proxy'), args = process.argv.slice(2), // Ezen a porton fog az alkalmazásunk várakozni port = args.length && parseInt(args[0], 10) || 8080, // Az terhelés elosztásának algoritmusa. spread = args.length && args[1] || 'random', servers; /** * Egy index generálása 0 és `max` között.. * * @param {HTTPRequest} request * Az aktuális HTTP lekérdezés. * @param {Number} max * Az számunk ennél kisebb legyen. * * @returns {Number} * Egy szám 0 és `max` között, `max`-ot nem beleszámolva. */ function hashIp(request, max) { var ip = request.socket.remoteAddress.split('.'); return (ip[0] * 0x10000 + ip[1] * 0x100 + ip[2]) % max; } /** * Kérések kezelése proxyn keresztül. * * Visszaad egy függvényt, aminek hozzáférése van a `servers` tömbhöz és a * szórás típusához. Ez a függvény a beérkező HTTP kéréseket * a szórásnak megfelelően továbbítja a szerverek egyikéhez. * * @param {Object[]} servers * Szerverek listája a következő formában: * {name: ip_or_domain, port: 123} * @param {String} [spread="random"] * Az eloszlás típusa. (random, single, ip) * * @returns {Function} * A függvény ami kezeli a HTTP kéréseket, úgy hogy továbbítja * azokat a megadott szervereknek. */ function handleRequest(servers, spread) { var getId; // Egyszerű Logika az aktuális szerver kiválasztására. switch (spread) { case 'single': getId = function (request, servers) { return 0; }; break; case 'ip': getId = function (request, servers) { return hashIp(request, servers.length); }; break; case 'random': default: getId = function (request, servers) { // Gyors véletlen szám lefele kerekítéssel return (Math.random() * servers.length) | 0; }; } return function (req, res) { // Lekérdezzük a szerver számát, amit használni fogunk. var id = getId(req, servers), // Készítünk egy új proxy-t, ami kezeli a kéréseket. proxy = new httpProxy.HttpProxy(req, res), // Ehhez a szerverhez kapcsolódunk. server = servers[id]; // Proxyzzuk a lekérdezést. proxy.proxyRequest(server.port, server.name, req, res); }; } servers = [ { name: '127.0.0.1', port: 8081 }, { name: '127.0.0.1', port: 8082 } ]; // Várakozunk a kérésekre, és azokat továbbítjuk a szervereknek. http.createServer(handleRequest(servers, spread)).listen(port); console.log('Proxy running at http://127.0.0.1:%d/', port);

Ezután az alkalmazásunkat a következőképpen futtathatjuk:

$ node app.js 8081 & node app.js 8082 & node proxy.js

Ekkor elindul az app.js a 8081-es és 8082-es porton, amikhez a proxy.js fog majd csatlakozni. A proxy.js-nek is megadhatjuk hogy mely porton várakozzon, illetve hogy milyen szórási metódust használjon az elérhető 3 közül.

Ha úgy érezzük, hogy a HTTP kérések proxyn keresztüli továbbítása hátrányosan érinti az alkalmazásunk sebességét – ebben eléggé kételkedek, ugyanis majdnem egyenesen arányos a növekedés új szerverek hozzáadásakor (természetesen csak bizonyos határokig) –, akkor alkalmazhatunk például Unix socketeket. Ekkor azonban az alkalmazásunkat át kell írni, és nehezebb a hibakeresés is, ugyanis nem lehet önállóan futtatni.