Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Construyendo aplicaciones escalables con Tornado y MongoDB

Construyendo aplicaciones escalables con Tornado y MongoDB

Esta presentación tuvo lugar en:
- UNICA por motivo de la Semana Tecnológica de Ica.
- UNS por motivo de la Semana del Ingeniero en Chimbote.

Jorge Puente-Sarrín

June 01, 2012
Tweet

More Decks by Jorge Puente-Sarrín

Other Decks in Technology

Transcript

  1. Tornado • Non-blocking web server • Escrito en Python •

    Creado por FriendFeed y liberado por Facebook • Resuelve C10k problem
  2. Aplicación import tornado.ioloop import tornado.web import tornado.httpserver class MiHandler(tornado.web.RequestHandler): def

    get(self): self.write("Corriendo Tornado") class MiAplicacion(tornado.web.Application): def __init__(self): settings = { 'template_path': "templates", 'debug': True } urls = [(r"/", MiHandler)] tornado.web.Application.__init__(self, urls, **settings) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(MiAplicacion()) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
  3. Handlers y parámetros class MiHandler(tornado.web.RequestHandler): def get(self): self.write("Corriendo Tornado") def

    post(self): nombre = self.get_argument("nombre") apellidos = self.get_argument("apellidos", "") self.write("Hola %s %s" % (nombre, apellidos)) def delete(self): raise tornado.web.HTTPError(403, "Acceso denegado") #self.redirect("/") #------------------------------------------------------------ #urls = [(r"/persona/([a-zA-Z]+)", MiHandler2)] class MiHandler2(tornado.web.RequestHandler): def get(self, nombre): self.write("Hola %s" % (nombre))
  4. Plantillas class MiHandler(tornado.web.RequestHandler): def get(self): nombre = self.get_argument("nombre") apellidos =

    self.get_argument("apellidos") email = self.get_argument("email", "") self.render("personal.html", nombre=nombre, apellidos=apellidos, email=email) <html> <head> <title>Tornado</title> </head> <body> <dl> <dt>Nombre:</dt><dd>{{ nombre }}</dd> <dt>Apellidos:</dt><dd>{{ apellidos }}</dd> {% if email != "" %} <dt>E-mail:</dt><dd>{{ email }}</dd> {% end %} </dl> </body> </html>
  5. Bloques base.html <html> <head> <title>Título</title> </head> <body> <header> {% block

    header %} {% end %} </header> <div > {% block content %} {% end %} </div> <footer> {% block footer %} {% end %} </footer> </body> </html> page.html {% extends "base.html" %} {% block header %} Cabecera {% end %} {% block content %} {{ parametro }} {% end %} {% block footer %} <span> Contenido del <strong>bloque<strong> </span> {% end %}
  6. Módulos """settings = { "template_path": "templates", "ui_modules": modules, }""" #modules.py

    import tornado.web class Menu(tornado.web.UIModule): def render(self): self.render_string("modules/menu.html", usuario=usuario) <ul> <a href="profile">Hola {{ usuario }}</a> <a href="logout">Cerrar sesión</a> </ul>
  7. Internacionalización #app.py tornado.locale.load_translations("translations") tornado.locale.set_default_locale("es_ES") idioma = locale.get("en_US") self.write(idioma.translate("Nombre")) #self.write(idioma.translate("Nombre", "Nombres",

    len(personas))) self.render("persona.html", nombre=nombre) <label for="name">{{ _("Nombre") }}</label> <input type="text" id="name" /> Cadena Traducción Indicador Nombre Name singular Nombres Names plural ... ... ...
  8. Cookies """settings = { "cookie_secret": base64.b64encode(os.urandom(32)) }""" class MiHandler3(tornado.web.RequestHandler): def

    get(self): #self.set_cookie("nombre") #valor_cookie = self.get_cookie("nombre", "valor") self.set_secure_cookie("nombre_seguro", "valor", 20) valor_cookie = self.get_secure_cookie("nombre_seguro") self.clear_cookie("nombre") #self.clear_all_cookies() #self.create_signed_value("email", "mi email")
  9. Escaping y protección a XSRF """settings = { "xsrf_cookies": True,

    "cookie_secret": base64.b64encode(os.urandom(32)) }""" <form action="/comment" method="post"> <input type="text" name="texto" /> {% raw xsrf_from_html() %} <input type="submit" value="Enviar" /> </form> <ul> {% for comentario in comentarios %} <li>{{ escape(comentario) }}</li> {% end %} </ul>
  10. Autenticación """settings = { "login_url": "/login", "cookie_secret": base64.b64encode(os.urandom(32)) }""" class

    AdminHandler(tornado.web.RequestHandler): def get(self): if not self.current_user: self.redirect("/login") else: self.render("admin.html") @tornado.web.authenticated def post(self): self.render("admin.html")
  11. Autenticación con servicios externos class FacebookHandler(tornado.web.RequestHandler, tornado.auth.FacebookGraphMixin): @tornado.web.asynchronous def get(self):

    ... def _on_auth(self, user): ... Todos los Mixin desarrollados son: OpenIdMixin, OAuthMixin, OAuth2Mixin, TwitterMixin, FriendFeedMixin, GoogleMixin, FacebookMixin y FacebookGraphMixin
  12. Web services asíncronos class AsyncHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): ... client.fetch(response,

    callback=self.on_response) def on_response(self, response): self.write(" ... ") self.finish()
  13. MongoDB • Base de datos NoSQL basado en documentos BSON

    • Escrito en C++ • Creado y mantenido por 10gen • Multiplataforma • Escalable • RESTful API • Fire-and-forget
  14. Analogía con los RDBMS RDBMS MongoDB Tabla Colección Fila Documento

    Índice Índice Constraints (No existen) Triggers (No existen) Procedimientos almacenados (No existen) Funciones (No existen)
  15. Comandos básicos db.listCommands() //Muestra todos los comandos db.runCommand() /*Ejecuta el

    comando ingresado como parámetro Por ejemplo: db.runCommand({ listDatabases:1 }) */ db.getLastError() db.stats() db.serverStatus() db.repairDatabase() db.getCollection('[collection]') db.getCollectionNames() db.[collection].getIndexes() db.help() db.addUser(usuario, contraseña) db.copyDatabase(existente, nueva) Entre muchos otros...
  16. Operaciones básicas CREATE TABLE Empleado ( IdEmpleado BIGINT IDENTITY(1,1) NOT

    NULL, Nombre VARCHAR(15) NULL, Email VARCHAR(MAX) NULL, Posicion CHAR(4) NULL ) GO INSERT INTO Empleado(Nombre, Email, Posicion) VALUES('Jorge Puente', '[email protected]', 'Programador'); --------------------------------------------------------------- /*{ "nombre": "Jorge Puente", "email": "[email protected]", "posicion": "Programador" }*/ db.empleado.insert({ "nombre": "Jorge Puente", "email": "jorge@puentesarr. in", "posicion": "Programador" });
  17. Operaciones básicas SELECT IdEmpleado, Nombre, Email, Posicion FROM Empleado; ---------------------------------------------------------------

    db.empleado.find(); db.empleado.find({ “email”: /^j/ }); UDPATE Empleado SET Posicion='Analista de calidad' WHERE Email='[email protected]'; --------------------------------------------------------------- db.empleado.update({ "email": "[email protected]" }, { "$set": { "posicion": "Analista de calidad" } }); db.empleado.update({ "email": "[email protected]" }, { "$set": { "posicion": "Analista de calidad" } }, false); //upsert parameter db.empleado.update({ "email": "[email protected]" }, { "$set": { "posicion": "Analista de calidad" } }, false, true); //multi parameter
  18. Operaciones básicas DELETE FROM Empleado WHERE Email='[email protected]'; DELETE FROM Empleado;

    TRUNCATE TABLE Empleado; ---------------------------------------------------------- db.empleado.remove({ "email":"[email protected]" }); db.empleado.remove();
  19. Colecciones con tope db.createCollection("historial", { capped:true, size: 5000000 }); db.runCommand({

    create:"historial", capped:true, size: 5000000 }); // Tamaño máximo en bytes
  20. Modelado con Docs. vinculados Colección de posts: { "_id": ObjectId("

    4f9c31753d32831252000000"), "text": "Mi primer post", "date": ISODate("2012-04-28T13:05:41.137Z"), "likes": 5 } Colección de comentarios: { "_id": ObjectId("4f9c31753d32781252000000"), "postid": ObjectId(" 4f9c31753d32831252000000"), "date": ISODate("2012-04-28T13:49:33.512Z"), "text": "Cool" }, { "_id": ObjectId("4f9c31753d32565465000000"), "postid": ObjectId(" 4f9c31753d32831252000000"), "date": ISODate("2012-04-28T13:49:38.719Z"), "text": "Genial" }
  21. Modelado con Docs. embebidos Colección de posts: { "_id": ObjectId("

    4f9c31753d32831252000000"), "text": "Mi primer post" "comments": [ { "date": ISODate("2012-04-28T13:49:33.512Z"), "text": "Cool" }, { "date": ISODate("2012-04-28T13:49:38.719Z"), "text": "Genial" } ], "date": ISODate("2012-04-28T13:05:41.137Z"), "likes": 5 }
  22. Índices // Orden ascendente db.empleado.ensureIndex({ email:1 }); // Orden ascendente

    y descendente db.empleado.ensureIndex({ email:-1 },{ unique:true }); // Índice disperso, se ignora el documentos al poseer el campo db.empleado.ensureIndex({ email:1 },{ unique:true, sparse:true }); // Índice compuesto db.empleado.ensureIndex({firstname: 1, lastname: 1}, {unique: true}); // Consultar los índices creados db.empleado.getIndexes(); db.system.indexes.find();
  23. RESTful API ~# mongod --dbpath /var/lib/mongodb/ --rest ~# mongod --fork

    --logpath /var/log/mongodb.log --logappend --rest http://localhost:28017/test/empleado http://localhost:28017/test/empleado?limit=100 http://localhost:28017/test/empleado?limit=100&skip=50 http://localhost:28017/test/empleado?filter_posicion=Programador http://localhost:28017/admin/$cmd/?filter_listDatabases=1
  24. GridFS GridFS myFS = new GridFS(myDatabase); myFS.storeFile(new File("/tmp/largething.mpg")); GridFS myContracts

    = new GridFS(myDatabase, "contracts"); myFS.retrieveFile("smithco", new File("/tmp/smithco_20090105.pdf")); { "_id" : <unspecified>, "length" : data_number, "chunkSize" : data_number, "uploadDate" : data_date, "md5" : data_string } { "filename" : data_string, "contentType" : data_string, "aliases" : data_array of data_string, "metadata" : data_object } http://www.mongodb.org/display/DOCS/GridFS
  25. Drivers Mantenidos por 10gen: • C • C++ • Erlang

    • Haskell • Java • Javascript • .NET (C# F#, PowerShell, etc) • Perl • PHP • Python • Ruby • Scala Mantenidos por la comunidad: • ActionScript3 • Clojure • D • Dart • Delphi • Erlang • F# • Go • Lisp • Lua • node.js • Objective-C • R • Scala • Otros...
  26. El caso Bit.ly Bit.ly utiliza MongoDB para almacenar y analizar

    el historial de los usuarios, para ello creó su propio módulo que trabaja con Tornado. http://blip.tv/mongodb/bit-ly-user-history-auto-sharded- 3723147
  27. Enlaces de interés Documentación: http://www.tornadoweb. org/documentation/index.html Bug tracking: https://github. com/facebook/tornado/issues

    Lista de interés: https://groups.google. com/group/python-tornado Documentación: http://docs.mongodb.org/manual Bug tracking: http://jira.mongodb.org Lista de interés: https://groups.google. com/group/mongodb-user