node-db: La excusa perfecta para hablar de C++ y Node.js

3,036 views

Published on

Un ejemplo de amistad entre C++ y Node.jsEsta charla introduce a node-db, una libreria para Node.js que busca ofrecer un soporte unificado a multiples bases de datos relacionales (MySQL y Oracle entre ellas), como una excusa para entrar al tema de fondo: el desarrollo de plugins para Node. js utilizando C++ y el motor V8

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,036
On SlideShare
0
From Embeds
0
Number of Embeds
23
Actions
Shares
0
Downloads
15
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • - Douglas Crockford: cuando hablas de JS, tenes que hablar de el no? Chuck norris de JS? - db-mysql.js: JS land. Extiende Query y Database de EventEmitter. Alli se llevaran los helper methods para escribir SQL - binding.cc: exports.Database - query.cc: exports Query - events.cc: usa el EventEmitter extendido de db-mysql.js
  • * Librerias: una cosa es implementar un cliente Paypal Payments Pro en Node, otra cosa es implementar un cliente Oracle puro en JS * Codigo proprietario: integracion con Ideafix
  • * Isaac Schlueter
  • * V8: Lo usamos para “convertir” datos JS en C++. Para exportar funciones. * libuv: nace como wrapper alrededor de libev, solo unix, para abstraer IOCP en Windows. Tiene thread pool, DNS asincrono, spawn de procesos, non blocking TCP * libeio: parecido a libev, manejo asincrono de file resources (open, close, read, write), ev_timer para timeouts * waf: via node-waf, hecho en Python. Syntaxis sencilla. * gyp: tb Python, mucho mas piola para cross-platform. En particular node-gyp es el equivalente a node-waf
  • * NODE_ADD_PROTOTYPE_METHOD: Define en cerveza.h que crea un nuevo prototype en base a un callback
  • * THROW_EXCEPTION: #define que RETORNA un v8::ThrowException
  • * NODE_MODULE: #define de node, Crea la estructura del modulo, dandole un nombre al modulo, y definiendo la funcion de inicio * EXTERN “C”: Le decimos a C++ que hay funciones C adentro
  • * uvComprar: en el thread pool * uvComprado: en el thread de V8
  • * Ref(): metemos una referencia asi no nos borra el garbage collector
  • * El callback se ejecuta cada vez que el evento sucede: en este caso cada vez que el FD es leible * revents: bitset de los eventos recibidos: EV_READ, EV_TIMEOUT, EV_ERROR, y otros
  • node-db: La excusa perfecta para hablar de C++ y Node.js

    1. 1. node­db La excusa perfecta para hablar de C++ y  Node.js    @mgiglesias
    2. 2. Se dice de mi Que soy nerd Que me gusta Apple Sí, ¡ni te imaginás cuánto! Que soy de opinar @mgiglesias es una mescolanza preocupante Que todos los lenguajes me quedan bien Duh! ¿De qué era esta charla? Que está en algún que otro proyecto FOSS   
    3. 3. Se dice de mi Le sobraba el tiempo, y arrancó WORKANA Che, es http://workana.com Le gusta Seinfeld Y los zombies :­/ Se mandó a mudar a Miramar Se encerró en un baño de un avión   
    4. 4. Cortá el chamullo ¿De qué voy a hablar? Node­db Node.js + C++   
    5. 5. Había una vez un Alemán @felixge   
    6. 6. Propuesta node­db API C++ unificada Separar lo intrínseco de los protocolos Librerías DB blocking y non blocking API en JS land sencillo Performance Solo relacionales MySQL Oracle   
    7. 7. A laburar$ npm install db­mysqlvar dbmysql = require(db­mysql);new dbmysql.Database({ hostname: localhost, user: root, password: password, database: node}).connect(function(error, server) { if (error) { throw new Error(ERROR:  + error); } console.log(server);});{ version: 5.5.24,  hostname: localhost,  user: root,      database: node }
    8. 8. Pooleando conexionesvar mysql = require(db­mysql),    generic_pool = require(generic­pool);var pool = generic_pool.Pool({    name: mysql,    max: 10,    create: function(callback) {        new mysql.Database({ … }).connect(function(err, server) {  callback(err, this);  });    },    destroy: function(db) {        db.disconnect();    }});pool.acquire(function(err, db) { pool.release(db);});    
    9. 9. A consultar se ha dichodb.query()        .select(["id", "user", "email"])        .from("users")        .where("role IN ?", [ ["administrator", "user"] ])        .and("created > ?", [ new Date(2011, 1, 1) ])        .execute(function(error, rows, columns){            if (error) {                console.log(ERROR:  + error);                return;            }            // Hacer algo mas interesante        }); http://nodejsdb.org   
    10. 10. ¿Qué hay detrás del telón? db­mysql.js exports.Database exports.Query lib/node­db binding.cc, query.cc, events.cc connection.cc, result.cc, exception.cc src/ mysql.cc, query.cc  connection.cc, result.cc  
    11. 11. En el fondo...  Javascript  C++ V8: un amigo   
    12. 12. ¿¿¿¿¿¿C++??????   
    13. 13. C++ en Node.js Performance Ojo, ¡no siempre! Librerias C/C++ Código proprietario Integración con otros lenguajes Python en Node.js via boost:python?   
    14. 14. C++ en Node.js Ya que estamos, le damos una mano   
    15. 15. C++ en Node.js V8 libuv libeio Y lo que gustes... ¡es C++ después  de todo! waf / gyp   
    16. 16. ¿¿¿¿¿¿WINDOWS??????  Birra para el que adivina de qué película es esta foto  
    17. 17. Muerte a los hola mundosvar binding = require(./build/Release/binding);cerveza = new binding.Cerveza();console.log(cerveza.estado());cerveza.tomar(1);console.log(cerveza.estado());cerveza.tomar(2);console.log(cerveza.estado());cerveza.tomar(5);console.log(cerveza.estado());$ node test.js Arranca a tomar de una vez!Segui tomando tranquiloMas vale que no manejesMe parece que te falto el agua para las macetas   
    18. 18. Qué ganas de una cerveza...#ifndef CERVEZA_H_#define CERVEZA_H_#include <node.h>#include <node_object_wrap.h>class Cerveza : public node::ObjectWrap { public: static void Init(v8::Handle<v8::Object> target); protected: long liters; Cerveza(); static v8::Handle<v8::Value> New(const v8::Arguments& args); static v8::Handle<v8::Value> Tomar(const v8::Arguments& args); static v8::Handle<v8::Value> Estado(const v8::Arguments& args);};#endif   
    19. 19. Qué ganas de una cerveza...void Cerveza::Init(v8::Handle<v8::Object> target) { v8::HandleScope scope; v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New); v8::Persistent<v8::FunctionTemplate> constructorTemplate; constructorTemplate = v8::Persistent<v8::FunctionTemplate>::New(t); constructorTemplate­>InstanceTemplate()­>SetInternalFieldCount(1); NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "tomar", Tomar); NODE_ADD_PROTOTYPE_METHOD(constructorTemplate, "estado", Estado); target­>Set(v8::String::NewSymbol("Cerveza"), constructorTemplate­>GetFunction());}v8::Handle<v8::Value> Cerveza::New(const v8::Arguments& args) { v8::HandleScope scope; Cerveza* cerveza = new Cerveza(); if (cerveza == NULL) { THROW_EXCEPTION("Ops! Tamos en el horno!") } cerveza­>Wrap(args.This());  return scope.Close(args.This());  }
    20. 20. Qué ganas de una cerveza...   
    21. 21. Los métodos expuestosv8::Handle<v8::Value> Cerveza::Tomar(const v8::Arguments& args) { v8::HandleScope scope; if (args.Length() != 1) { THROW_EXCEPTION("Si no me decis cuantos litros que queres que haga?"); } else if (!args[0]­>IsInt32()) { THROW_EXCEPTION("Que te tomaste?"); } else if (args[0]­>ToInt32()­>Value() < 0) { THROW_EXCEPTION("Limpia ese enchastre!") }    Cerveza* cerveza = node::ObjectWrap::Unwrap<Cerveza>(args.This());    assert(cerveza);    cerveza­>liters += (args[0]­>ToInt32()­>Value());    return scope.Close(v8::Undefined());}   
    22. 22. Los métodos expuestosv8::Handle<v8::Value> Cerveza::Estado(const v8::Arguments& args) { v8::HandleScope scope; Cerveza* cerveza = node::ObjectWrap::Unwrap<Cerveza>(args.This()); assert(cerveza); const char* estado; if (cerveza­>liters > 4) { estado = "Me parece que te falto el agua para las macetas"; } else if (cerveza­>liters > 2) { estado = "Mas vale que no manejes"; } else if (cerveza­>liters > 0) { estado = "Segui tomando tranquilo"; } else { estado = "Arranca a tomar de una vez!"; }    return scope.Close(v8::String::New(estado));}   
    23. 23. Y le decimos a Nodeextern “C” void init(v8::Handle<v8::Object> target) { Cerveza::Init(target);}NODE_MODULE(binding, init);{  "targets": [    {      "target_name": "binding",      "sources": [ "cerveza.cc" ]    }  ]}$ node­gyp configure buildinfo it worked if it ends with okmake: Leaving directory `/home/mariano/Documents/Talks/JSConf/src/buildinfo done ok     
    24. 24. libuv“El mundo no es perfecto.  A veces no podemos  hacer non­blocking.......  sin threads”   
    25. 25. libuvcerveza.comprar(function() { cerveza.comprar(function(compradas) { console.log(Ya compre  + compradas +  cervezas); });});$ node test.js Ya compre 2 cervezas   
    26. 26. libuvuv_work_t* request = new uv_work_t();request­>data = // ….uv_queue_work(uv_default_loop(), request, uvComprar, uvComprado);uv_ref(uv_default_loop()); void uvComprar(uv_work_t* uvRequest) void uvComprado(uv_work_t* uvRequest) uv_unref(uv_default_loop());   
    27. 27. A comprar cervezaclass Cerveza : public node::ObjectWrap { protected: long compradas; pthread_mutex_t compradasLock; struct compra_t { Cerveza* cerveza;          v8::Persistent<v8::Function>* callback; v8::Persistent<v8::Object> context; }; static v8::Handle<v8::Value> Comprar(const v8::Arguments& args);         static void uvComprar(uv_work_t* uvRequest);         static void uvComprado(uv_work_t* uvRequest);};Cerveza::Cerveza():litros(0), compradas(0) { pthread_mutex_init(&(this­>compradasLock), NULL);}Cerveza::~Cerveza() {  pthread_mutex_destroy(&(this­>compradasLock));  }
    28. 28. v8::Handle<v8::Value> Cerveza::Comprar(const v8::Arguments& args) { v8::HandleScope scope; Cerveza* cerveza = node::ObjectWrap::Unwrap<Cerveza>(args.This()); assert(cerveza); compra_t* compra = new compra_t(); if (compra == NULL) { THROW_EXCEPTION("Sin espacio!") } cerveza­>Ref(); compra­>context = v8::Persistent<v8::Object>::New(args.This()); compra­>cerveza = cerveza; compra­>callback = NULL; if (args.Length() > 0) { compra­>callback = node::cb_persist(args[0]); } uv_work_t* req = new uv_work_t(); req­>data = compra; uv_queue_work(uv_default_loop(), req, uvComprar, uvComprado); uv_ref(uv_default_loop()); return scope.Close(v8::Undefined());   }
    29. 29. void Cerveza::uvComprar(uv_work_t* uvRequest) { compra_t* compra = static_cast<compra_t*>(uvRequest­>data); assert(compra); pthread_mutex_lock(&(compra­>cerveza­>compradasLock)); compra­>cerveza­>compradas++; pthread_mutex_unlock(&(compra­>cerveza­>compradasLock));}   
    30. 30. void Cerveza::uvComprado(uv_work_t* uvRequest) { v8::HandleScope scope; compra_t* compra = static_cast<compra_t*>(uvRequest­>data); assert(compra); uv_unref(uv_default_loop()); compra­>cerveza­>Unref(); if (compra­>callback != NULL) { v8::Local<v8::Value> argv[1] = { v8::Number::New(compra­>cerveza­>compradas) }; v8::TryCatch tryCatch; (*(compra­>callback))­>Call(compra­>context, 1, argv); if (tryCatch.HasCaught()) { node::FatalException(tryCatch); } } compra­>context.Dispose(); if (compra­>callback != NULL) { compra­>callback­>Dispose(); }  delete compra;  }
    31. 31. libeio “libeio es como si me  teletransportara del laburo  al bar. Cada 5 segundos”   
    32. 32. libeiocerveza.consultar(function(r) { console.log(r);});$ node test.js John Doe   
    33. 33. libeioev_io* ioRequest = new ev_io();ev_init(ioRequest, eioConsultado);ev_io_set(ioRequest, fd, EV_READ);ev_io_start(EV_DEFAULT_UC_ ioRequest); void eioConsultado(EV_P_ ev_io* ioRequest, int revents) ev_io_stop(EV_A_ ioRequest);   
    34. 34. v8::Handle<v8::Value> Cerveza::Comprar(const v8::Arguments& args) { consulta­>connection = mysql_init(NULL); mysql_real_connect(consulta­>connection, "localhost", "root", "password",  "node", 3306, NULL, 0); mysql_send_query(consulta­>connection, "SELECT * FROM users", 20); consulta­>context = v8::Persistent<v8::Object>::New(args.This()); consulta­>cerveza = cerveza; consulta­>callback = NULL; if (args.Length() > 0) { consulta­>callback = node::cb_persist(args[0]); } consulta­>cerveza­>Ref(); ev_io* ioRequest = new ev_io(); ioRequest­>data = consulta; ev_init(ioRequest, eioConsultado); ev_io_set(ioRequest, consulta­>connection­>net.fd, EV_READ); ev_io_start(EV_DEFAULT_UC_ ioRequest);}   
    35. 35. void Cerveza::eioConsultado(EV_P_ ev_io* ioRequest, int revents) { v8::HandleScope scope; consulta_t* consulta = static_cast<consulta_t*>(ioRequest­>data); assert(consulta); ev_io_stop(EV_A_ ioRequest); consulta­>cerveza­>Unref(); mysql_read_query_result(consulta­>connection); MYSQL_RES* result = mysql_store_result(consulta­>connection); assert(result); char** row = mysql_fetch_row(result); // … mysql_free_result(result); mysql_close(consulta­>connection);}   
    36. 36. Conclusión C++ sigue sirviendo para algo libuv y libeio te simplifican la vida ¡No solo para Node.js eh! Ojeá el fuente de Node. Hay cosas interesantes  ahí ;) No minimices el poder de V8. Por algo se llama  V8   
    37. 37. ¿Preguntas? :­)  www.workana.com   @mgiglesias

    ×