ugrás a tartalomhoz

Node.js alapok II.

Poetro · 2010. Dec. 27. (H), 13.23
Node.js alapok II.

A cikksorozat előző részében megismerkedtünk a Node.js rendszer telepítésével és alapvető működésével. Most újabb részletekbe ásunk bele, és megnézzük, hogyan tudunk hatékonyabb alkalmazásokat írni.

Többszálúság

A sorozatban megjelent

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

Mit látható a 0.3.0 verziójú változatot telepítettem, mivel ez kompatibilis a Node 0.2-es változatával, a legfrissebb változathoz már 0.3-as Node kell.

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.

Tisztító alkalmazás

Valamelyik nap egy érdekes feladatot tűztem ki magam elé. Van egy viszonylag rendezetlen könyvtár struktúrában egy halom HTML és egyéb fájl. Nekem a HTML fájlok kellenek, viszont a tartalmukból nem kell minden. Például a <script> elemekre, vagy például a különböző menükre sincsen szükségem, szinte csak a fő tartalomra.

Rekurzív könyvtárbejárás

Előbb írjuk meg az alkalmazás azon részét, amivel be tudjuk járni a könyvtárstruktúrát, és a talált fájlokból ki tudjuk szűrni a .html kiterjesztésűeket. A Node szinte minden fájlrendszer művelet esetén kínál egy aszinkron és egy szinkron megvalósítást. Hogy ez mit jelent? Aszinkron megoldás esetén a parancs visszatértekor egy callback függvényt hív meg, de addig párhozamosan futtathatunk több dolgot. Szinkron kérés esetén a programunk futása megszakad, addig amíg parancs fut. Ez a szinkron működés eléggé tipikus a programozási nyelvek között. Ismerkedés okán előbb valósítsuk meg a szinkron működést. Erről tudni kell, hogy valamivel lassabb, mint az aszinkron, mivel minden egyes műveletet meg kell várni, hogy elkezdhessük a következőt.

var fs = require('fs');

/**
 * Rekurzív szinkron könyvtár bejárás
 *
 * @param {String} path
 *   Az induló könyvtár elérési útja.
 * @param {RegEx} fileMatch
 *   Reguláris kifejezés, ami meghatározza,
 *   hogy a fájlra meg kell-e hívni a callback függvényt.
 * @param {Function} callback
 *   Callback függvény, amit minden találatra meghív paraméterként
 *   a fájl elérési újtát és statisztikáit átadva.
 */
function parseDirSync(path, fileMatch, callback) {

  /**
   * Az fs.readdirSync függvény által adott eredményt dolgozza fel.
   *
   * @param {Array} files
   *   Fájlok és könyvtárak nevének listája, melyek az aktuális
   *   könyvtárban vannak.
   * @param {String} dpath
   *   Az aktuálisan elemezendő könyvtár elérési útja.
   */
  function parse(files, dpath) {
    if (files) {
      // Nem történt hiba, végig megyünk a `files` tömbön.
      files.forEach(function (name, index) {
        // A fájl / könyvtár elérési útja.
        var fpath = dpath + '/' + name,
            stats;
        // Lekérdezzük a fájl / könyvtár statisztikáját
        stats = fs.statSync(fpath);
        if (stats) {
          // Nem történt hiba, lássuk az információt.
          if (stats.isDirectory()) {
            // Menjünk végig rekurzívan a könyvtáron.
            parse(fs.readdirSync(fpath), fpath);
          }
          else if (stats.isFile()) {
            // Megnézzük, hogy a fájl illeszkedik-e.
            if (fileMatch.test(fpath)) {
              // Meghívjuk a `callback` függvényt, átadva a fájl elérési
              // útját, és statisztikáit.
              callback(fpath, stats);
            }
          }
        }
      });
    }
  }

  parse(fs.readdirSync(path), path);
}

Ezt a következőképpen használhatjuk, például hogy kiírjuk az összes directory könyvtár alatt levő .html fájlnak az elérési újtát:

parseDirSync(directory, /\.html$/i, function (path, stats) {
  console.log(path);
});

Most nézzük meg ugyanezt aszinkron módon:

var fs = require('fs');

/**
 * Rekurzív aszinkron könyvtár bejárás
 *
 * @param {String} path
 *   Az induló könyvtár elérési útja.
 * @param {RegEx} fileMatch
 *   Reguláris kifejezés, ami meghatározza,
 *   hogy a fájlra meg kell-e hívni a `callback` függvényt.
 * @param {Function} callback
 *   Callback függvény, amit minden találatra meghív paraméterként
 *   a fájl elérési útját és statisztikáit átadva.
 */
function parseDir(path, fileMatch, callback) {
  /**
   * Rekurzívan beolvassa a megadott könyvtárat.
   *
   * @param {String} dpath.
   *   A feldolgozandó könyvtár elérési útja.
   */
  function read(dpath) {
    /**
     * Az fs.readdir függvény által adott eredményt dolgozza fel.
     *
     * @param {Array} files
     *   Fájlok és könyvtárak nevének listája, melyek az aktuális
     *   könyvtrában vannak.
     * @param {String} dpath
     *   Az aktuálisan elemezendő könyvtár elérési útja.
     */
    function parse(err, files) {
      if (!err) {
        // Nem történt hiba, végig megyünk a `files` tömbön.
        files.forEach(function (name, index) {
          // A fájl / könyvtár elérési útja.
          var fpath = dpath + '/' + name;
          // Lekérdezzük a fájl / könyvtár statisztikáját
          fs.stat(fpath, function (err, stats) {
            if (!err) {
              // Nem történt hiba, lássuk az információt.
              if (stats.isDirectory()) {
                // Menjünk végig rekurzívan a könyvtáron.
                read(fpath);
              }
              else if (stats.isFile()) {
                // Megnézzük, hogy a fájl illeszkedik-e.
                if (fileMatch.test(fpath)) {
                  // Meghívjuk a `callback` függvényt, átadva a fájl elérési
                  // útját, és statisztikáit.
                  callback(fpath, stats);
                }
              }
            }
          });
        });
      }
    }

    fs.readdir(dpath, parse);
  }

  read(path);
}

Amint látszik, hatalmas különbségek nincsenek, ugyanakkor a különbségek igenis lényegesek. Ez utóbbi függvény a könyvtárakat szinte párhuzamosan dolgozza fel. Ezért fordulhat elő, hogy ha az előző kódhoz hasonlóan csak kiíratjuk a fájlok elérési útját, akkor azok nem folytonosan, hanem közel össze-vissza jelennek majd meg a kimeneten.

Ez a párhuzamos feldolgozás gyorsabb tud lenni, ugyanakkor lehetnek hátrányai, ha nem bánunk vele óvatosan. Például, ha a feldolgozó függvény sok memóriát fogyaszt, akkor a sok párhuzamosan futtatott feldolgozás eredményezheti azt, hogy elfogy a memória. Ilyen feladatokra érdemes lehet egy úgynevezett poolt készíteni, vagy használni, amibe belepakoljuk a kívánt feladatainkat, és egyszerre csak bizonyos mennyiségűt futtatunk párhuzamosan.

Párhuzamos futtatás

Párhuzamos futásra alkalmas egyik általánosan használható modul a generic-pool. Lényegében nem más, mint az Object pool pattern egy megvalósítása.

A Pool egy gyárat vár paraméterként. A gyár egy objektum, aminek van egy create() és egy destroy() függvénye, valamint egy max értéke. A create() függvény egy új objektumot állít elő, a destroy() a megszűntetést készíti elő – például adatbázis kapcsolat esetén bezárja a kapcsolatot –, a max pedig az egyszerre futó objektumok számát határozza meg. Az Object Pool a következőképp működik:

  1. Kérünk tőle egy elemet.
  2. Kapunk egy ígéretet (promise), hogy amint lesz ilyen szabad elem, akkor meg fogjuk kapni.
    1. Amennyiben még nem értük el a maximális készíthető elemszámot, legyártunk egy elemet (create()), és kielégítjük a következő ígéretet.
    2. Amennyiben van szabad elem, akkor azzal elégítjük ki a következő ígéretet.
    3. Várunk, hogy valamelyik elem felszabaduljon, és amikor felszabadult, kielégítjük vele az ígéretet.
  3. A megkapott objektummal dolgozunk, majd ha már nincs rá szükség, visszahelyezzük a Pool-ba.
  4. Ha már nincs kielégítendő ígéret, akkor idővel megszüntetjük az elemet, ezzel memóriát, és egyéb erőforrást szabadítunk fel (destroy()).

Nézzük is meg, hogyan használhatjuk:

var Pool = require('generic-pool').Pool,
    pool;

//...

pool = new Pool({
  name: 'HTML Parser',
  // Esemény a létrehozás kezelésére
  create: function (callback) {
    // Meg kell hívni a `callback`-et, hogy megtörténjen a létrehozás
    callback('Parser Client');
  },
  destroy : function (client) {
  },
  // Legfeljebb 10 kliens futhat egyszerre
  max: 10,
  // 1 másodpercet várunk, utána megszüntetjük a nem használt klienst.
  idleTimeoutMillis : 1000
});

Vessük is be:

// Megkeressük a .html fájlokat a könyvtárban, majd azokat feldolgozzuk
parseDir(directory, /\.html$/i, function (path) {
  // Kérünk egy feldolgozót, és amint megvan dolgozunk is a fájlon.
  pool.borrow(function (client) {
    // Kaptunk egy új klienst, jelen esetben csak egy stringet,
    // jelezvén szabad a pálya.
    parseFile(path, client);
  });
});

Már csak a fájl tényleges feldolgozása van hátra, azaz a parseFile() függvény.

DOM kezelés

Amennyiben Node.js alatt akarunk DOM-ot manipulálni, úgy lehetőségünk van erre a Common.js alapokon elérhető JSDom modullal. Ehhez szükségünk van a htmlparser modulra is, ami a HTML feldolgozását végzi majd, amennyiben nem nulláról akarunk dokumentumot építeni.

$ npm install htmlparser jsdom

A JSDom a W3C DOM specifikációjának a 3. szintjét is teljesíti, de nekünk csak alapvető függvények fognak kelleni. Kicsit fura lehet, hogy böngésző nélkül is tudunk DOM manipuláló függvényeket futtatni, és azok szinte teljesen ugyan úgy működnek mint a böngészőben. Ráadásul nem tartalmazzák a böngészők DOM specifikus hibáit.

var jsdom = require('jsdom');

/**
 * Eltávolít egy elemet a DOM-ból.
 *
 * @param {Node} el
 *   DOM elem.
 */
 function removeElement(el) {
   if (el && el.parentNode) {
     el.parentNode.removeChild(el);
   }
 }

/**
 * Egy fájl feldolgozása.
 *
 * Eltávolítjuk a felesleges HTML elemeket,
 * majd egy másik könyvtárba elmentjük őket.
 *
 * @param {String} path
 *   A fájl elérési útja.
 * @param {Object} client
 *   A Pool kliens objektuma
 */
function parseFile(path, client) {
  // Beolvassuk a fájlt.
  fs.readFile(path, function (err, data) {
    var document,
        i, els, el, nav = /(?:\s|^)book_nav(?:\s|$)/,
        newPath = path.replace(directory, './output');

    if (!err) {
      // Nem volt hiba a fájl beolvasása során,
      // ezért hozzunk létre a tartalmából egy W3C DOM dokumentumot.
      document = jsdom.jsdom(data.toString());
      // Távolítsuk el, a meghatározott ID-jű elemeket.
      ['screen_banner', 'print_banner', 'site_nav', 'blue', 'right_column', 'footer', 'copyright', 'sidebar'].forEach(function (id) {
        removeElement(document.getElementById(id));
      });

      // Távolítsuk el a SCRIPT elemeket.
      els = document.getElementsByTagName('script');
      for (i = els.length - 1; i >= 0; i -= 1) {
        removeElement(els[i]);
      }
      // Ellenőrízzük, hogy egyes UL-ek tartalmaznak-e egy osztályt,
      // amennyiben igen, ezeket is távolítsuk el.
      els = document.getElementsByTagName('ul');
      for (i = els.length - 1; i >= 0; i -= 1) {
        el = els[i];
        if (el && nav.test(el.className)) {
          removeElement(el);
        }
      }

      // Mentsük el a módosított dokumentumot egy új fájlba.
      fs.writeFile(
        newPath,
        document.innerHTML,
        'utf8',
        function (err) {
          // A mentés befejeződött.
          if (!err) {
            console.log('%s sikeresen elmentve', newPath);
          }
          // Amennyiben van kliens, azt rakjuk vissza a Pool-ba,
          // ezáltal a további várakozó elemeket is fel tudjuk dolgozni.
          if (client) {
            pool.returnToPool(client);
          }
        }
      );
    }
    else {
      // Amennyiben van kliens, azt rakjuk vissza a Pool-ba,
      // ezáltal a további várakozó elemeket is fel tudjuk dolgozni.
      if (client) {
        pool.returnToPool(client);
      }
    }
  });
}

Aki idegenkedik a DOM W3C specifikáció szerinti használatától, az használhat jQuery-t is – ez ismételten furcsa lehet ugyanis még mindig nincs böngésző, csak egy emulált DOM. Ekkor persze szükségünk lesz a BOM-ra is (Browser Object Model).

var document = jsdom.jsdom(data.toString()),
    window = document.createWindow('http://example.com/');

window.jQueryfy(window, 'path/to/jquery.js', function (window, jQuery) {
  // Itt már használhatunk jQuery-t.
});

A document.createWindow() egy böngészőbeli window objektumot emulál, és a paramétere a window.location objektum létrehozásához szükséges. Bármilyen, akár képzeletbeli URL-t meg lehet neki adni, de fontos, hogy szintaktikailag helyes legyen.

Zárszó

Remélem sikerült bemutatni, hogyan lehet a Node.js esemény alapú természetét hatékonyabban kihasználni, és hogyan működnek az aszinkron fájlműveletek. Elsőre talán nehéznek, túlbonyolítottnak tűnhet, de aki foglalkozott már például Ajax programokkal, annak remélem hamar kitisztul a kép. Az erőforrások hatékonyabb kihasználásával alkalmazásunk gyorsabb lesz, és ez nem feltétlen kell, hogy a programunk áttekinthetőségének rovására menjen.

Az előző cikkhez képest, itt azért bonyolultabb javascriptes koncepciók is jelen vannak, ezért JavaScriptben gyakorlatlanabbak valószínűleg idegenkedni fognak a kódtól. Ugyanakkor remélem, hogy a gyakorlottabb programozók meglátják benne a potenciált, és a felhasznált technikák is felkeltik érdeklődésüket.

 
Poetro arcképe
Poetro
1998 óta foglalkozik webfejlesztéssel, amikor is a HTML és a JavaScript világa elvarázsolta. Azóta jópár évet dolgozott reklámügynökségeknél, és nemzetközi hírportálok fejlesztésével. Legfőképpen Drupal fejlesztéssel, site buildinggel és JavaScripttel, azon belül is jQuery-vel és Node.js-sel foglalkozik.
1

Az aszinkron

inf · 2011. Jan. 6. (Cs), 04.37
Az aszinkron könyvtárfa-bejárásnak a 17. sorában a kommentben a "beolvass" helyett "beolvassa" kell, a 24. sorban meg "fs.readdirSync" helyett "fs.readdir" :D :D :D

Viccet félretéve, tetszett a cikk, nekem teljesen érthető volt, tetszik a node.js is, használni is fogom a későbbiekben. A cikksorozat beosztása milyen lesz, mindig egy problémát oldasz majd meg? Én kíváncsi lennék, hogy pl egy szimpla mysql-es blog hogyan készíthető el vele.
2

budapest.js

Poetro · 2011. Jan. 6. (Cs), 13.28
Gyere el a következő budapest.js találkozóra, és pont egy ilyen feladatot fogok megoldani, és ez alapján beszélni a Node.js-ről. Bár nem MySQL lesz az adatbázis hanem SQLite, de ez igazából részletkérdés. A fenti hibákat (bár a megjegyzésekben vannak és nem kódban) remélem hamarosan javítják a szerkesztők.
3

Javítva

Török Gábor · 2011. Jan. 6. (Cs), 13.42
Már javítottuk.
4

Köszönöm a meghívást, mennék

inf · 2011. Jan. 9. (V), 12.20
Köszönöm a meghívást, mennék én szívesen, mentem volna eddig is, de most Herenden lakom. Majd remélhetőleg február elején költözök vissza Budapestre. (Ez még függvénye 1-2 dolognak.)
5

Sziasztok!

Karvaly84 · 2011. Júl. 17. (V), 01.03
Nem sokat olvastam még erről mivel magyarul nem sok anyag érhető el hozzá, de most felmerült pár kérdésem amiket itt feltennék:
  • A NodeJS egy alkalmazást cgi felületen futtat?
  • Van hozzá olyan modul amivel xml transzformációt lehet végezni, és hosszú távon lehet rá alapozni?
  • Attól, hogy nem használunk szervert még megoldható, hogy a szerver konfigurálást ne nekünk keljen végezni? (Itt elsősorban a válasz fejlécekre gondolok), vagy használható Apache modulként? Vagy van hozzá olyan szerver modul amit már csak használni kell?
  • Ti mennyi esélyt láttok arra, hogy ez a történet mondjuk a PHP-hoz, hasonló módon lesz elterjedt és kedvelt? Jelenleg van olyan hosting cég aki nyújt tárhelyet egy NodeJS webalkalmazásoz?
  • Milyen konkurens megoldások léteznek még amik beárnyékolhatják a rendszer elterjedését?
  • Illetve létezik magyar doksi ebben a témában?
6

A NodeJS egy alkalmazást cgi

Poetro · 2011. Júl. 17. (V), 02.17
A NodeJS egy alkalmazást cgi felületen futtat?

Nem. Sima processként futtat minden alkalmazást. Kb. pont úgy ahogy PHP-ban exec / shell_exec-kel futtatnád, vagy félreértettem a kérdést.
Van hozzá olyan modul amivel xml transzformációt lehet végezni, és hosszú távon lehet rá alapozni?

Ez alatt mit értesz? Lehet XML-t parsolni és generálni is. Nem tudom pontosabban mit szeretnél.
Attól, hogy nem használunk szervert még megoldható, hogy a szerver konfigurálást ne nekünk keljen végezni? (Itt elsősorban a válasz fejlécekre gondolok), vagy használható Apache modulként? Vagy van hozzá olyan szerver modul amit már csak használni kell?

Mivel te magad írod a HTTP szerveredet, nem. Ugyanakkor van több keretrendszer és modul, ami sokat könnyít ezen.
Ti mennyi esélyt láttok arra, hogy ez a történet mondjuk a PHP-hoz, hasonló módon lesz elterjedt és kedvelt?

Ha majd eltelik annyi év, mint amennyi idő alatt a PHP elterjedt, majd válaszolok :). Egyenlőre még csak két év telt el, és még nincs is 1.0-s változat sem, úgyhogy nehéz bármit mondani.
Jelenleg van olyan hosting cég aki nyújt tárhelyet egy NodeJS webalkalmazásoz?

Van pár szolgáltató.
Milyen konkurens megoldások léteznek még amik beárnyékolhatják a rendszer elterjedését?

Milyen nyelven? Van több Erlang, Python és Ruby megoldás, ami hasonló célokat kerget, sőt egyesek gyorsabbak is, mint a Node.js.

Talán ha megnézed a témában tartott előadásom, kitisztul a kép.
7

A cgi dolgot úgy értettem,

Karvaly84 · 2011. Júl. 17. (V), 08.53
A cgi dolgot úgy értettem, hogy pl egy web app-ra beérkezett kérdéseket hogy dolgoz fel... tehát hogy a script-eket a cgi-bin mappába kel e tenni a perl-hez hasonlóan vagy nem, ha nem nem baj. illetve a xml-nél a xslt-re gondolok, hogy xml-böl html-t tudjak generálni könnyen. Ha haza értem megnéznem a előadásodat köszönöm.
8

Démon

Poetro · 2011. Júl. 17. (V), 11.36
Te egy démont írsz, ami a háttérben fut, nem egy démon futtatja a te alkalmazásodat. Kb ugyanaz, mintha egy valamilyen szervert írnál Java alatt. Azaz neked kell a bejövő kéréseket feldolgozni, ugyan a HTTP kérés egy része például már eleve fel van dolgozva. Egyes modulok, mint például az express / connect pedig feldolgozzák már a HTTP kérés body részét is (POST üzenetek). XSLT feldolgozás nincs, ugyan létezik valamilyen tisztán JavaScript alapon, de nem teljes. De amennyiben tudsz C / C++ alapon, azt könnyen tudod integrálni az alkalmazásodba.
9

Köszi az infokat!

Karvaly84 · 2011. Júl. 17. (V), 22.01
Köszi az infokat!
10

xalan

Poetro · 2011. Júl. 17. (V), 23.16
Bocs, én tévedtem. A Xalan XSLT feldolgozóhoz már létezik, ugyan még kicsit kezdetleges, de működő modul node-xalan néven. Pedig már éppen azon voltam, hogy megírom én magam :) .
11

Van már éles oldalad nodejs

inf · 2011. Júl. 18. (H), 00.06
Van már éles oldalad nodejs alapon?
12

Még nincs

Poetro · 2011. Júl. 18. (H), 01.08
Még nincs, de rajta vagyok, hogy majd legyen. De jó lenne, ha lenne hozzá valami jó projekt, és nem csak saját magamnak és fejlesztési segédalkalmazásokat csinálnék / használnék.