Adrianistán

Tutorial de Neon - Combina Node.js con Rust

27/01/2017
Hoy en día muchas webs se diseñan con Node.js. Es una solución fantástica para respuestas rápidas pero numerosos benchmarks han demostrado que su rendimiento empeora en respuestas complejas. Estos mismos benchmarks recomiendan usar Java o .NET si preveemos que nuestra aplicación web va a generar respuestas complejas. Sin embargo renunciar a las ventajas de Node.js no es del agrado de muchos. Afortunadamente hay otra solución, usar Rust. Todo ello gracias a Neon.

Con Neon podemos generar módulos para Node.js que son escritos y compilados en Rust con las ventajas que supone desde un punto de vista de rendimiento y con la certeza de que en Rust la seguridad está garantizada.

Usando Neon puedes desarrollar tu aplicación en Node.js y si alguna parte tiene problemas de rendimiento sustituirla por su equivalente en Rust. Para el ejemplo voy a hacer un módulo de Markdown.

Instalando Neon


En primer lugar instalamos la herramienta de Neon desde npm.


npm install -g neon-cli


Una vez esté instalado podemos usar la herramienta de Neon para construir un esqueleto de módulo. Este esqueleto tendrá dos partes, un punto de entrada para Node.js y otro para Rust.


neon new PROYECTO


Hacemos un npm install como nos indica. Esto no solo obtendrá dependencias de Node.js sino que también se encargará de compilar el código nativo en Rust.

El código Node.js


Nuestro módulo contiene un archivo de Node.js que sirve de punto de entrada. Allí lo único que se debe hacer es cargar el módulo en Rust y hacer de pegamento. Puede ser algo tan simple como esto:


var addon = require("../native");

module.exports = addon; // se exportan todos los métodos del módulo nativo


Aunque si queremos añadir un tratamiento específico también es posible.


var addon = require("../native");

module.exports = {
render: function(str){
return addon.render(str);
}
}


El código en Rust


Nos dirigimos ahora al archivo native/src/lib.rs. Ahí definimos los métodos nativos que va a tener el módulo. Lo hacemos a través de la macro register_module!.


register_module!(m,{
m.export("render",render)
});


Ahora vamos a implementar la función render, que toma el texto en Markdown y lo devuelve en HTML.


fn render(call: Call) -> JsResult<JsString> {
let scope = call.scope; // obtener el contexto
let md: Handle<JsString> = try!(try!(call.arguments.require(scope,0)).check::<JsString>()); // obtener primer argumento como JsString. aquí puede hacerse tratamiento de fallos
let string = md.value(); // Pasar JsString a String
let html: String = markdown::to_html(&string); // usamos la crate markdown para renderizar a html
Ok(JsString::new(scope, &html).unwrap()) // devolvemos un JsString con el contenido del HTML
}



Las funciones que interactuan con Node deben devolver un JsResult de un tipo JsXXX, por ejemplo, JsString, JsUndefined o JsInteger. Siempre aceptan un argumento llamado de tipo Call que nos da toda la información necesaria y que podemos usar para sacar argumentos. El scope o contexto es muy importante y lo deberemos usar en las funciones que interactúen con Node.

Código completo del fichero Rust




#[macro_use]
extern crate neon;
extern crate markdown;

use neon::vm::{Call, JsResult};
use neon::js::JsString;
use neon::mem::Handle;

fn render(call: Call) -> JsResult<JsString> {
let scope = call.scope;
let md: Handle<JsString> = try!(try!(call.arguments.require(scope,0)).check::<JsString>());
let string = md.value();
let html: String = markdown::to_html(&string);
Ok(JsString::new(scope, &html).unwrap())
}

register_module!(m, {
m.export("render", render)
});


Y no te olvides de añadir la dependencia markdown al fichero Cargo.toml.

Probándolo


Es muy fácil probarlo. Con el REPL de Node podemos probar partes del módulo antes de publicarlo a npm.

Para abrir el REPL ejecuta Node sin argumentos


node


E introduce línea por línea lo que quieras probar:


var md = require("./");
md.render("__Esto es Markdown__");


Verás el resultado por la pantalla:



Ahora que ya sabemos que funciona podemos publicarlo a npm si queremos con:


npm publish


Aunque recuerda revisar antes el fichero package.json para especificar la licencia y la descripción. Una vez esté publicado su uso en un nuevo proyecto será muy sencillo y de forma transparente se compilará el código nativo.


var md = require("rust-markdown");
var http = require('http');
var fs = require("fs");

var server = http.createServer((req, res) => {
fs.readFile("index.md","utf-8",function(err,data){
var html = md.render(data);
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(html);
});
});

server.listen(8080, "127.0.0.1", () => {
console.log("Server running");
});




 

 
Tags: npm programacion javascript linux rust markdown neon nodejs node-js