Amigo lector

Se han escrito ríos de tinta sobre la muerte de Google Reader. Una de las mayores víctimas silenciosas, son los usuarios de Blackberry. Hasta el momento, yo usaba la web de iPhone; un vestigio raro ya que hay “app”, pero que era la mar de usable en mi móvil.

De entre todos los reemplazos de Google Reader que se barajan, aún no he encontrado ninguno que me solucione el uso móvil en la Blackberry- no parece haber nadie que tenga una web móvil usable [la de Netvibes ya os digo yo que no se puede usar] y de momento no se huele ninguna aplicación (y por mucho que esto me estigmatice, preferiría no pagar si es necesario [aunque en el caso del lector de feeds, podría hacer una excepción]).

Explorando alternativas, me he puesto un poco en plan hágaselo usted mismo. Feedly dice que implementará Normandy, un clon del backend de Google Reader y de momento apostaré por esta reimplementación. De hecho, Google Reader no tiene API “oficial”, pero mucha gente la ha destripado y documentado, y de hecho goza de cierta popularidad. Así pues, me he puesto a buscar librerías que implementen la API.

Dado que uno trabaja mayoritariamente Python/Django y Java, esto me ha limitado un poco la búsqueda. Para Java, el mejor candidato parece greader-unofficial, pero parece estar un poco muerto con tan solo 47 commits en su repositorio, el último de octubre de 2011. En cambio, naturalmente parece que para Python existe libgreader, con commits de anteayer y con mucha mejor pinta.

Así pues me he puesto manos a la obra y he iniciado un proyecto Django publicado en Github, GROLM (Google Reader on Lightweight Mobile). Es un proyecto Django 1.5 estándar sobre una base de datos PostgreSQL.

libgreader proporciona cuatro alternativas a la hora de manejar la autenticación contra Google Reader:

  • proporcionarle el usuario/contraseña de la cuenta Google y que él mismo autentique
  • OAuth
  • OAuth2
  • una variante de OAuth para Google App Engine

; obviamente la menos usable y/o poco segura es la de la contraseña, y tampoco quiero desarrollar sobre App Engine, así que me he decantado por OAuth2 (al ser más moderna obviamente que OAuth). El mecanismo de autenticación de OAuth2 de libgreader parece completo, pero deseaba integrarlo con el sistema de autenticación de Django para ahorrarme faena.

Para ello, he localizado django-social-auth, que integra en el sistema de autenticación de Django diversos mecanismos de login social entre los que se incluye OAuth2. La configuración de django-social-auth con OAuth2 es relativamente sencilla, simplemente tenemos que añadir el siguiente fragmento a nuestro settings.py:

INSTALLED_APPS = (
  # ...
  'social_auth',
)
AUTHENTICATION_BACKENDS = (
  'social_auth.backends.google.GoogleOAuth2Backend',
)
GOOGLE_OAUTH_EXTRA_SCOPE = [
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/userinfo.profile',
  'https://www.google.com/reader/api/',
 ]
LOGIN_URL = '/login/google-oauth2'
LOGIN_REDIRECT_URL = '/logged-in/'
LOGIN_ERROR_URL = '/login-error/'
GOOGLE_OAUTH2_CLIENT_ID = 'xxxx'
GOOGLE_OAUTH2_CLIENT_SECRET = 'xxxx'

, donde los dos últimos parámetros los podemos obtener de la Google API Console (donde también deberemos añadir las URLs de nuestra aplicación para validar el proceso de login)

Con esto, con anotar una vista como @login_required, al visitarla se nos redirigirá a la página de autenticación de las cuentas Google donde haremos login y se nos redirigirá de nuevo a la vista ya autenticados (se creará un usuario de Django correspondiente al usuario de Google y se vinculará a la sesión del usuario).

Finalmente, para “propagar” esta autenticación a libgreader, el mecanismo que he encontrado es extraer el token de acceso que recoge django-social-auth y almacena en una base de datos y pasárselo a libgreader, algo que he encapsulado en un módulo, pero que en realidad es bastante sencillo:

usa = UserSocialAuth.objects.get(user=user)
 auth = OAuth2Method(settings.GOOGLE_OAUTH2_CLIENT_ID, settings.GOOGLE_OAUTH2_CLIENT_SECRET)
 auth.authFromAccessToken(usa.extra_data['access_token'])
reader = GoogleReader(auth)

, donde user es el usuario (que desde una vista podemos obtener por ejemplo haciendo request.user). Una vez tenemos esto, ya obtenemos el objeto GoogleReader que constituye el punto de acceso a la API.

El resto de código implementado por el momento tan sólo obtiene la reading-list (los elementos sin leer) y muestra sus 20 primeras entradas.

¿Cuánto rato se tarda en montar un entorno de desarrollo web Java?

Una de las quejas más comunes sobre desarrollar en Java es lo complejo que resulta montarse el entorno de desarrollo. Ciertamente, la situación hace años era un poco rollo; grandes descargas del JDK perdido entre las tinieblas, Eclipse por piezas difícil de instalar…

¿Cuál es la situación actual? ¿Sigue siendo tan complejo? ¿Los anchos de banda más anchos de hoy en día ayudan?

Para el propósito de este ejercicio, cogeré un Windows sin nada instalado e instalaré todo lo necesario para poder desarrollar un servlet “Hola, Mundo”, con Eclipse (el entorno de desarrollo gráfico gratuito para Java más común).

Los pasos que deberemos realizar son los siguientes

  1. Ir a la página de Oracle, Downloads, Popular Downloads, Java for Developers, JDK, Aceptar la licencia y escoger la versión adecuada (en mi caso Windows x64)… 1 minuto
  2. Descargar los 92mb… 7 minutos
  3. Ejecutar el instalador… 3 minutos (+2 minutos de JavaFX que instala por defecto)
  4. Ir a la página de Eclipse, Download Eclipse, Eclipse IDE for Java EE Developers, escoger la versión adecuada (Windows 64 bits)… 1 minuto
  5. Descargar los 221mb… 16 minutos
  6. Abrir el .zip y extraerlo (en mi caso, a mi carpeta de usuario c:\users\alex, creándose una carpeta dentro c:\users\alex\eclipse)… 4 minutos
  7. Arrancar Eclipse (en mi caso c:\users\alex\eclipse\eclipse.exe),
    escoger la ubicación por defecto del workspace (y marcar que no vuelva a preguntar),
    crear un “dynamic web project”,
    escoger crear un nuevo runtime de Tomcat 7.0 (y darle una ruta, se lo baja y lo instala [unos 8mb]),
    decirle que nos genere un web.xml,
    hacer un click con el botón derecho y sobre la sección de servlets y decirle que nos cree uno, todo por defecto,
    implementar el método doGet haciendo un response.getOutputStream().print(“Hello world”);,
    hacer click derecho en el proyecto,  run as, run on server, hacer que corra en el Tomcat 7 que hemos creado anteriormente,
    editar la URL del navegador que se nos abra para que se corresponda a la ruta donde hemos mapeado el servlet y cargarlo
    … todo unos 7 minutos

En total, unos 40 minutos de los que unos 25 son unos 320mb de descargas. Lógicamente con una conexión más rápida (tengo una de 3 megabits) y sin la virtualización, que obviamente penaliza el rendimiento, podría reducirse un poco.

A destacar que el proceso tradicionalmente más complejo, instalar el Tomcat y configurarlo para que se pueda usar desde el Eclipse, ha sido simplificado por la gente de Eclipse haciendo que instalarlo y autoconfigurarlo directamente al crear un proyecto sea bastante sencillo (el interfaz no es 100% intuitivo y me ha costado un pelín entenderlo, pero bueno).

Por supuesto, este es un proceso mínimo usable; yo preferiría añadirle:

  • Instalación de egit, que sorprendentemente en la versión Java EE de Eclipse no viene cuando otras variantes de Eclipse lo traen por defecto (4 minutos de instalación desde Eclipse Marketplace, incluyendo un reinicio)
  • Instalación de m2eclipse y su plugin para WTP, que nos permite configurar proyectos usando Maven, un sistema bastante estándar de gestión de dependencias (6 minutos de instalación desde Eclipse Marketplace, con otro reinicio)

, y por supuesto, que esto nos instala un sistema de desarrollo “pelado” sin framework, cuando lo recomendable es no desarrollar servlets “a pelo” si no usar algún framework como Spring. Pero esto lo trataremos en otra entrega…

O sea, que podemos montar un entorno de desarrollo, con servidor de aplicaciones y entorno gráfico integrado en menos de una hora. ¿Es mucho, poco? ¿Cuánto se tarda con otras plataformas de desarrollo? ¿Es realmente un factor que eche para atrás de desarrollar con Java? ¿O son otros factores?

Desarrollo web como Dios manda

Con la cantidad de faena a hacer en el mundo del desarrollo web, es natural preguntarse cosas como qué tecnología elegir y por dónde comenzar si uno quiere dedicarse a esto.

No son preguntas triviales- la espectacularidad diversidad de plataformas, filosofías y frameworks intimida y lleva a pensar que no es posible tomar una buena decisión- es impracticable probar todas las alternativas y en las etapas iniciales de aprendizaje es difícil formarse una opinión razonada.

En este post pretendo dar unos apuntes que espero sirvan para colocar a alguien en el buen camino.

Una manera fácil de comenzar es por el lenguaje. Es conveniente que escojamos un lenguaje que cumpla las siguientes características:

  • Popular
  • Con tracción para el desarrollo web
  • De calidad

Comenzando por el principio, un buen punto de partida es mi querido TIOBE. El TIOBE es un ránking de la popularidad de los lenguajes de programación calculado a partir de su presencia en la web. La metodología es inevitablemente discutible, pero el ránking está bastante alineado con mi percepción, así que para mi, es una opción cómoda.

En el top 20 (a junio de 2012) encontramos tan solo 8 lenguajes utilizados comunmente para el desarrollo web: Java, C#, PHP, Python, Perl, Ruby, Javascript y Visual Basic .NET. Fuera del Top 20 encontramos muy poquitas opciones (Haskell, Scala y poco más), así que nos ceñiremos a estos.

Vamos a descartar unos pocos:

  • PHP: pese a ser un lenguaje explícitamente diseñado para el desarrollo web, en mi opinión PHP nunca debe usarse para desarrollar un proyecto desde 0- a no ser que lo que queramos desarrollar sea extremadamente mínimo- ya sea porque se trate de un desarrollo extremadamente pequeño o bien que pretendamos reutilizar completamente un desarrollo existente como WordPress o Magento. Desarrollar grandes bases de código en PHP es un ejercicio frustrante ya que, sencillamente, no está pensado para ello. Sus limitaciones en cuanto a modelo de ejecución, estructura y modularidad son motivo suficiente para descartarlo, pues el resto de lenguajes que consideramos lo superan ampliamente en estos aspectos, ofreciendo PHP muy poco para compensar (su velocidad para proyectos mínimos).Puede sernos útil conocer PHP, pues existe mucho trabajo manteniendo código PHP (sin embargo, no se trata de un trabajo especialmente gratificante) y en algún momento nos puede ser útil. Pero debe ser erradicado lo antes posible.
  • Perl: durante mucho tiempo fue una de las mejores opciones disponibles, en realidad, una de las pocas viables. Una vez más, el resto de lenguajes de la lista le superan en virtudes sin que Perl ofrezca muchas ventajas propias. El mercado de Perl decae lentamente y cada vez se inician menos proyectos que lo utilicen.
  • JavaScript: si bien deberemos conocer JavaScript para desarrollar efectivamente sobre la web, aún no lo considero una opción viable en el lado servidor. Tendremos que aprender JavaScript, pero el grueso del proyecto deberá ser siempre en otro de los lenguajes. Soy anti-aplicaciones web 100% Javascript, creo que su campo de aplicación es extremadamente limitado y presentan desventajas considerables, pero hay quien les encuentra virtudes

C# y Visual Basic .NET son dos opciones que el lector mismo puede escoger descartar o considerar- desarrollar razonablemente en ambos supone unos costes que yo prefiero no asumir (se necesitan licencias de Windows para el desarrollo y despliegue y las versiones gratuitas de Visual Studio tienen bastantes limitaciones)- a parte de que soy un firme creyente en que las herramientas de desarrollo deben ser libres y gratuitas. Si eso no supone un impedimento para el lector, puede aplicar mi opinión sobre Java, ambas plataformas son extremadamente similares; quizás .NET goce de herramientas más sencillas de utilizar inicialmente, el sistema base es más completo que el de Java pero el ecosistema goza de menor vida.

Por tanto, nos quedamos con Java, Python y Ruby. Los dos factores más diferenciales entre los tres para mi son:

  • Pythoncuenta con el framework Django, que a su vez goza del “Admin”. El Admin es un desarrollo genérico que implementa un sistema de gestión de modelos web genérico muy sofisticado, que nos permite definir las entidades con las que trabaja nuestra aplicación (e.g. en una web de contenidos, artículos, categorías, etc.) y obtener prácticamente sin esfuerzo una interfaz de gestión bastante buena que permite a los usuarios gestionar los objetos, añadiendo un sistema de permisos más que aceptable (i.e. podemos decir que los reporteros pueden crear y editar sus artículos y que los administradores pueden crear y gestionar secciones). Dado que en la mayoría de los proyectos se necesita una funcionalidad así y Django nos la proporciona sin dedicarle apenas tiempo (y desarrollar algo de ese calibre es considerablemente costoso), para muchos proyectos Django nos puede ahorrar muchísimo tiempo de desarrollo tedioso, dándonos una gran ventaja.Puede parecer que otras plataformas cuentan con cosas similares (e.g. Roo en Java, el scaffolding de Rails), pero no están a la altura.
  • Javaes el único de los lenguajes que cuenta con tipado estático. El tipado estático (deber declarar explícitamente el tipo de las variables) parece un problema (debemos “perder tiempo” especificando tipos), pero en realidad no lo es (uno ya razona sobre el tipo que debe tener una variable, escribirlo explícitamente no es costoso) y en cambio permite la existencia de herramientas sofisticadas que trabajen sobre el código. En los lenguajes dinámicos, por poner un ejemplo, es extremadamente laborioso localizar dónde se utiliza un determinado trozo de código, algo necesario para tareas como eliminarlo (debemos asegurarnos que no exista otro código que lo utilice y adaptarlo si existe) o modificarlo (debemos asegurarnos que adaptamos el código que lo utilice), tenemos que buscar las referencias por todo el código y esto puede ser extremadamente problemático si la búsqueda no es muy específica (i.e. si tenemos un campo llamado “página” en un objeto concreto, una búsqueda de texto nos reportará usos de “página” en clases no relacionadas, o que contengan “página”), pues podemos tener una cantidad de falsos positivos muy elevada fácilmente.Esto propicia que los cambios en el código sean mucho más trabajo del que debiera. En cambio, en un lenguaje estático es posible implementar búsquedas muy exactas gracias a la presencia de tipos en el código fuente (básicamente, la herramienta puede discernir si la referencia que buscamos es la que encuentra con exactitud). En Java el compilador nos avisará si un cambio/eliminación rompe código, y las herramientas nos pueden localizar exactamente todas las referencias a un trozo de código. Adicionalmente, las herramientas no se quedan aquí, sino que implementan funcionalidades similares mucho más avanzadas y potentes que no pueden ser implementadas efectivamente sobre lenguajes dinámicos.

En mi opinión, ambas herramientas deben estar en el arsenal de un desarrollador web. Python + Django para proyectos sencillos o en los que el Admin de Django suponga una gran ventaja, y Java para proyectos grandes y complejos. Python nos da agilidad y el Admin y Java nos da la potencia para desarrollar sistemas complejos.

Por supuesto, la cosa no se queda aquí. El desarrollo web requiere unos conocimientos de base; más allá de los conocimientos de programación estándar (que obviamente son necesarios), necesitaremos conocer:

  • El protocolo HTTP, el “alma” de las comunicaciones web
  • HTML + CSS, con los que desarrollamos la capa visual de la aplicación
  • JavaScript para las funcionalidades que lo requieran (con jQuery)

Seguramente también necesitaremos un mecanismo de persistencia (donde SQL es la opción más popular, yo recomendaría usar PostgreSQL y evitar el popularísimo MySQL pero infame) y conocimientos básicos de sistemas (deberíamos saber hacer funcionar un servidor web, la base de datos, etc.).

Con todo esto podríamos desarrollar alguna web sencilla con Django, que nos proporciona una plataforma bastante completa y bien documentada con la que podremos implementar bastantes proyectos.

Una vez asentados los fundamentos del desarrollo web, podemos comenzar a investigar Java. Java no es como Django, que incluye prácticamente todo lo necesario en un sencillo paquete y, una vez más nos plantea unos problemas de decisión importantes. Lamentablemente, desconozco una buena referencia completa y vertical de desarrollo Java que ofrezca un stack completo comparable al de Django. Mi recomendación sería partir de Java, añadir Spring (el mecanismo de inyección de dependencias, su librería de JDBC y el motor MVC) y JSP, todo corriendo sobre un contenedor como Jetty o Tomcat (que se integre en nuestro entorno de desarrollo), utilizando Eclipse y Maven para la compilación. Desgraciadamente es una solución más compleja para la parte inicial (y muchas de ellas se deben aprender prácticamente simultáneamente, incrementando la dificultad), pero adoptando todas estas tecnologías tendremos una base similar a la de Django. El camino es mucho más largo, pero nos ofrece una alternativa más apropiada para grandes proyectos.

Lógicamente, esta es una vía “ideal”. Quizás no debe completarse completamente, si no hemos de trabajar en proyectos muy complejos Java es una exageración, o bien la vida nos lleve a sumergirnos en otras tecnologías, pero es un recorrido completo. Si no llegamos a Java (o incluso si llegamos), puede ser conveniente pasar tiempo usando algún microframework que no nos dé un stack completo como el de Django, Django nos lo da casi todo mascado y seguramente hay detalles interesantes de conocer a bajo nivel que nos perdamos. Hacer desarrollo web sin un stack completo ciertamente puede ayudarnos a completar nuestras habilidades. Podemos hacer esto tanto en Python (usando un microframework de los muchos existentes, o incluso desarrollando el nuestro) como en Java (programando directamente servlets en vez de usar el MVC de Spring) o en cualquier otro lenguaje.

nota: más ediciones y sugerencias del de siempre.

Porqué Django no es La Solución Definitiva

Hace tiempo ya explicaba por aquí las virtudes de Django. En  resumen, se trata de un framework de desarrollo en web Python que implementa un interfaz de administración prácticamente automático a un esquema relacional. Vaya, que defines tus tablas y genera un interfaz dinámico para editar registros que te ahorra una barbaridad de tiempo (como podrá atestiguar cualquiera que haya tenido que hacerse uno).

Tras llevar más tiempo trabajando con Django, sigo convencido que en estos momentos es la mejor solución que existe para desarrollo web basado en CRUD sobre bases de datos; el admin no tiene equivalente alguno que yo conozca, y desarrollarse un sistema similar es extremadamente costoso.

Sin embargo, creo que he aislado unos cuantos defectos clave que hay que tener en cuenta antes de comenzar a usarlo.

1.

Si usamos un esquema donde queramos que la edición de un registro tenga más de un nivel de indirección, el admin no soluciona esto. Pongamos por caso que tenemos una entidad “Empleado” , otra entidad “Proyecto” y una entidad intermedia que nos representa las asignaciones de Empleados a Proyectos (por ejemplo, en esta relación intermedia podríamos querer almacenar el tiempo durante el cual el Empleado está asignado al Proyecto, su porcentaje de dedicación a él, etc.). Podremos añadir un fantástico TabularInline que nos muestre las asignaciones de Empleados dentro de la vista de detalle de Proyecto, pero no hay manera de que se pueda editar el Empleado desde la vista detalle de Proyecto; podremos editar la relación intermedia (primer nivel de indirección), pero la segunda ya no.

Esto nos limita bastante el interfaz sobre esquemas de datos moderadamente complejos que nos podemos encontrar fácilmente en el mundo real; cuando tengamos estos esquemas tendremos que…

  1. Hackear el admin como podamos para que nos permita hacer las ediciones
  2. Buscar a alguien que haya implementado algún plugin que nos añada en esto
  3. Ignorar el admin e implementarlo nosotros mismos
  4. Simplificar nuestro esquema

Ninguna de las tres soluciones es mínimamente satisfactoria

2.

El admin necesita más hipervínculos. En particular, la fantástica funcionalidad de los raw_id_fields, nos permite hacer que los campos clave foránea de nuestras entidades se puedan editar con un popup selector excelente, pero no nos permite saltar a la entidad enlazada. Una de las grandes virtudes de usabilidad de la web son los enlaces, y nos serían extremadamente útiles en más lugares del admin

3.

Django no proporciona suficiente potencia en el SQL subyacente a su ORM. En particular, sería harto conveniente poder disponer de, o bien un inspectdb más potente que nos permita trabajar continuamente con él (añadir campo en nuestra base de datos y que inspectdb añada dinámicamente el campo al modelo), o un mecanismo para poder personalizar automatizadamente el esquema generado; esto principalmente nos debería permitir implementar una “estrategia” de nombrado de tablas y columnas que nos permita, por ejemplo, que los nombres de tablas sean plurales o cambiar el nombre de las claves primarias surrogadas que Django añade automáticamente.

Si no nos gustan los esquemas que genera Django automáticamente (y no deberían gustarnos), las alternativas son o aguantarnos, o especificarle repetidamente los nombres de tablas y columnas que debe usar o hackear Django para que haga lo que queramos. Una vez más, esto no es del todo satisfactorio.

4.

A un nivel más profundo, el código de Django no es muy amigable a la extensión. Es bastante complicado añadir funcionalidad derivando de las clases de Django; el código no siempre es fácil de seguir (ya que usa bastantes metaclases y otras pythonicidades de las que no soy muy fan) ni tiene un diseño orientado a objetos muy elaborado- se echa en falta que ciertas funcionalidades estén, como mínimo, aisladas en un propio método que podamos sobreescribir para cambiarlas (o utilizar el patrón estrategia, idealmente). Siempre nos queda la opción de forkear, pero esto no es muy mantenible, o hacer monkey-patching, lo que tampoco es muy recomendable ni mantenible.

Pese a todos estos puntos, sigo pensando que el admin de Django es realmente algo único que nos puede ahorrar muchísimo trabajo y proporcionar un resultado de gran calidad para muchos, muchísimos proyectos. Pero aún es mejorable- y con unas pocas mejoras localizadas podría mejorar muchísimo.

Mi tema de WordPress

.assistive-text, .screen-reader-text, .menu, #nav-above, #colophon {
 display: none;
}
#page {
 margin-left: auto;
 margin-right: auto;
 max-width: 1000px;
}
#primary {
 max-width: 720px;
 min-width: 320px;
 float: left;
 margin-left: 15px;
 margin-right: 15px;
}
#secondary {
 min-width: 175px;
 width: 240px;
 float: left;
}

Abrir links en nueva ventana en infojobs.net

Si estáis hasta las narices de que no podáis usar el botón de la ruedecita para abrir ofertas de trabajo en una ventana nueva en infojobs.net, el siguiente script de Greasemonkey os puede ser útil:

// ==UserScript==
// @name       Open in new tab in Infojobs
// @namespace  http://use.i.E.your.homepage/
// @version    0.1
// @description  enter something useful
// @include    http://www.infojobs.net/jobsearch/search-results/list.xhtml
// @include    https://www.infojobs.net/jobsearch/search-results/list.xhtml
// @copyright  2011+, You
// ==/UserScript==

window.setInterval(function() {
    unsafeWindow.jQuery("[id^='table_results_offer']").each(function() { this.onclick = null; })
}, 1000);

Yo lo uso en Chrome con Tampermonkey. Es cutre y nada correcto, pero funciona.

actualización: pongo la URL https:// en el @include para que también funcione si te has identificado en Infojobs.

El mito de la escalabilidad

Hace mucho mucho tiempo, en una galaxia muy muy lejana, existía una web de noticias informáticas llamada Slashdot. Por motivos oscuros, Slashdot creció brutalmente en popularidad y por tanto, cada vez que aparecía una noticia en portada de Slashdot enlazando a una web, una cantidad elevada de personas hacía click en la noticia, dirigiendo a ésta ingentes cantidades de tráfico. En los inicios de Internet, no muchas webs estaban suficientemente bien preparadas para soportar carga como Slashdot, y por tanto este tráfico tendía a colapsar los sistemas que recibían enlaces de Slashdot- este efecto fue denominado “Slashdot Effect“.

Avanzando un poco hacia el presente, el tráfico de internet se ha multiplicado. De los 70 millones de usuarios de Internet en el 97 (nacimiento de Slashdot), hemos pasado a los 2110 millones de usuarios actuales (fuente); es decir, se ha multiplicado por 30 el número de usuarios, y muy probablemente, el uso de cada usuario de Internet también se ha intensificado- cada vez dedicamos más tiempo y hacemos más cosas en Internet.

Hoy en día, sitios como Facebook presumen de tener 400 millones de usuarios entrando cada día en sus servicios (¡5 veces el número de usuarios de toda Internet en el 97!) y se suben unos 250 millones de fotos diarias. En Twitter, coincidiendo con hechos como la muerte de Osama Bin Laden, llegan a escribirse más de 12 millones de tuits en una hora.

Así pues, si en el 97 el volumen de uso podía ya suponer un problema para los servicios de Internet, con el enorme crecimiento de la red, ¿se acrecentan estos problemas?

Sin duda alguna. A pesar de que la potencia de los ordenadores se ha acrecentado, esta no ha parecido contrarrestar el hecho de que cada vez queremos más datos, más rápidos y más inmediatos de la red. El mencionado Twitter ha sufrido a lo largo de su carrera para poder soportar el uso que se le da; su Fail Whale ha sido enormemente popular y aún hoy se deja ver de cuando en cuando.

¿Por qué es “difícil” tratar este problema?

Comencemos por lo más básico. Un servidor normalito que podemos adquirir, si sabemos buscar, por 50€/mes, puede servir sin muchos problemas y sin grandes esfuerzos, unas 50 peticiones por segundo de una página dinámica sencilla que muestra información almacenada en una base de datos. Esto equivale a servir 4.320.000 peticiones en un día. Supongamos que cada usuario se pasa unos 10 minutos diarios en la página, y hace una petición nueva cada 10 segundos; eso son unas 60 peticiones. El servidor anterior podría atender a unos 72.000 usuarios/diarios, pudiendo llegar a atender a 3000 usuarios/hora.

Estas cifras parecen respetables, y la buena noticia es que adquiriendo servidores más caros, obtenemos una mejor relación precio/peticiones/s, con lo que podemos fácilmente soportar más carga de este tipo simplemente echando un poco de dinero al problema.

Lógicamente, nunca podríamos llegar a soportar una carga brutal- del orden de magnitud de Facebook, o incluso mucho menos buscando servidores más caros- no existen servidores de tal potencia. Pero seguimos teniendo opciones sencillas. Si en vez de coger un servidor, cogemos más de uno y copiamos toda la base de datos y todo el sistema en estos servidores, las carga se puede repartir en ellos prácticamente a la perfección; si compramos 7 servidores, multiplicaremos las peticiones que podemos atender casi por 7.

Jugando con servidores más o menos costosos y en mayor o menor cantidad de ellos, podemos atender “tantas peticiones como queramos”, a un coste usualmente bastante razonable.

Adicionalmente, la mayor parte de “mostrar información almacenada” en la red hoy en día tiene una característica- es enormemente repetitiva; la página principal de un diario puede necesitar mostrarse a millones de usuarios, pero es esencialmente la misma para todos- así que por muy costoso que sea recuperar y formatear la información, es un trabajo que sólo tenemos que hacer la primera vez- luego podemos guardarnos el trabajo realizado y simplemente copiarlo para el resto de peticiones subsiguientes.

Pero el problema es que no todo es mostrar información almacenada. El problema realmente duro es almacenar información.

El primer problema es que nos surge es la persistencia. Normalmente querremos que la información que se guarde en nuestra web sea persistente; es decir, que no desaparezca. Eso nos obliga para cada escritura a realizar el trabajo duro, registrar esta información en un disco duro o un SSD. Esto es algo que no nos podemos ahorrar de ninguna manera y que de hecho, como veremos ahora, constituye un verdadero límite doloroso al rendimiento que podemos ofrecer.

Un sistema básico puede perfectamente realizar 1500 transacciones de escritura por segundo. Como en el caso de las lecturas, simplemente gastándonos dinero podemos ampliar este número muy fácilmente.

El problema, es que una vez alcanzado el límite del sistema, aplicar el mismo truco que antes (poner dos servidores en vez de uno)… no funciona excesivamente bien.

En el caso de las lecturas, asumíamos que podíamos replicarlo todo a todos los servidores para que cualquiera de ellos pueda atender cualquier petición. Esto nos lleva a que cada escritura que realicemos se tendrá que replicar a todos los servidores, con lo que… ¡no ganamos nada! Como cada escritura tiene que “pagarse” en todos los servidores, nuestra velocidad de escritura haciendo esto nunca aumentará.

Así pues, un replicado simple y obvio nos permite escalar la velocidad de lectura, pero no la de escritura. Si nos topamos con nuestro límite de velocidad de escritura… ¿cómo lo superaremos?

Hay varias alternativas.

Una bastante obvia es no replicar las escrituras a todos los servidores. Eso hará que cada servidor pueda escribir independientemente, con lo que combinaremos la velocidad de escritura de todos los servidores y aumentaremos nuestro rendimiento. Pero obviamente, no todos los datos estarán escritos en todos los servidores, y por tanto no podremos usar la estrategia para acelerar lecturas que comentábamos antes.

Lo que podemos hacer entonces es, en vez de repartir todas las peticiones entre todos los servidores, las repartiremos de otra manera, “particionando” la información. Por ejemplo, pondremos a los usuarios de cada país en un servidor diferente. Esto obviamente nos incrementará el rendimiento, siempre y cuando podamos hallar una partición adecuada.

El problema es que muchas veces, esto no es posible. Todos los usuarios de Twitter quieren acceder a los tuits de cualquier usuario, los usuarios están conectados entre sí de maneras arbitrarias, de manera que es imposible particionarlos de ninguna manera.

Cuando nos hallamos en esta situación, tenemos un problema, obviamente. La solución menos costosa es sacrificar la calidad de nuestros datos. Escribamos los datos a no todos los servidores, y repliquémoslo al resto “cuando podamos”. Aceptemos que algún servidor tendrá información desactualizada (pero que al menos podremos dar un servicio más o menos potable, pero rápido)… hagamos aproximaciones y aceptemos no dar datos 100% correctos e incluso aceptemos que algún dato puede perderse.

Lógicamente, esto no es factible de aplicar para cosas que requieran exactitud, como compras y pagos y cosas de este tipo… ¿pero para tuits y mensajes en Facebook? Pues probablemente sí.

En esto se fundamentan los sistemas NoSQL que prometen escalabilidad barata- una de las cosas en las que se fundamentan es en no proteger los datos con la paranoia habitual- hecho que debemos aceptar como sacrificio cuando los usamos como atajo a la escalabilidad.

En conclusión- escalar un sistema tiene un coste razonable para las cargas de consultas y visualización de información… los sistemas cuyo punto crítico sea este se pueden escalar mucho de una manera relativamente económica. En cambio, los sistemas cuyo cuello de botella sea la escritura son mucho más complicados de escalar adecuadamente- y uno de los costes que podemos pagar es el de la integridad de esos datos.

actualizado con los comentarios de mi editor habitual

IE sin Windows

Algunas personas tienen en ocasiones el impulso de usar ese navegador vintage que es IE6, por oscuros motivos. Aquí explicamos cómo hacerlo gratis.

Lo primero, descargar las imágenes de compatibilidad de Internet Explorer que Microsoft graciosamente proporciona (incluyen versiones de Windows de XP hasta 7, y Internet Explorer 6 a 8). Si disponemos de una instalación de Windows cualquiera, ya estamos, podemos descargar gratuitamente Virtual PC y utilizar las imágenes sin ningún problema.

Desafortunadamente, Virtual PC sólo funciona sobre Windows, que cuesta dinero. Si no disponemos de Windows podemos usar VirtualBox, disponible para Linux, OS X y Solaris. Lamentablemente, no es tan sencillo, pero se puede hacer fácilmente.

Lo primero, sacar la imagen .vhd del .exe que nos habremos descargado de la web de Microsoft. Esto lo podemos hacer con unrar, por ejemplo.

Siguiente, debemos crear una máquina virtual con VirtualBox, utilizando el .vhd como imagen de disco. Como es harto probable que querramos que la máquina virtual se conecte a nuestra red, deberemos habilitar una tarjeta de red en la configuración de la máquina virtual.

Veremos en la configuración que se pueden escoger diversas tarjetas de red. La versión de XP que lleva la imagen de Explorer 6 lamentablemente no incluye drivers para ninguna de las tarjetas de red que podemos escoger, por lo que tendremos que introducirlos en la instalación. Esto se puede hacer de diversas maneras, la más sencilla de las cuales consiste en escoger en este paso la tarjeta de red paravirtualizada.

En este momento, y antes de arrancar la máquina, puede sernos conveniente crear un snapshot del disco.

Arrancando la imagen, nos pedirá activar Windows. De momento saltémonos este paso. Cancelemos todas las peticiones de instalar hardware.

Es un buen momento para instalar las “guest additions” de VirtualBox, que entre otras cosas mejoran la integración de teclado y ratón con la máquina virtual y nos permiten activar el “seamless mode”, que hace que el fondo de escritorio de Windows desaparezca y las ventanas de aplicación queden integradas en nuestro escritorio nativo.

Tras hacer esto, es el momento de instalar los drivers de la tarjeta de red. La tarjeta de red paravirtualizada (que no emula “tontamente” una tarjeta de red física real, simplemente utiliza un poco de magia en la máquina virtual para comunicarse con la red nativa) que usa Virtualbox está soportada por estos drivers convenientemente proporcionados en forma de .iso. Simplemente podemos bajarnos la .iso y usarla para simular un CD virtual dentro de la máquina. Podemos entonces realizar la instalación de los drivers de la tarjeta de red utilizando el CD.

En este momento, es conveniente reiniciar. Al reiniciar, se nos volverá a pedir activar Windows. Dado que ya tenemos la red funcionando, podremos realizar la activación por Internet sin ningún problema.

El último paso es ir al administrador de dispositivos y deshabilitar todos los dispositivos que no funcionan, con lo que nos evitaremos las peticiones de instalar drivers.

Bonus 1: si consultáis la EULA de Windows 7 Professional OEM, veréis que es legal utilizarlo para la máquina que se compró pero virtualizado

Bonus 2: en la web de VirtualBox se puede 1) encontrar una fuente apt para Debian/Ubuntu que contiene las versiones más actualizadas para instalar automáticamente y 2), “descargar el VirtualBox 4.1 Oracle VM VirtualBox Extension Pack” que habilita funcionalidades como poder utilizar en la máquina virtual dispositivos USB conectados al ordenador que corre VirtualBox (lo que por ejemplo, permite utilizar iTunes para gestionar un iPhone/iPad desde dentro de una máquina virtual). Esto último sólo es legal si la persona que instala las extensiones es la misma persona que las va a usar (i.e. no se instalan automatizadamente) o se está en un entorno académico.

actualización: la última imagen proporcionada por Microsoft el 16/8 no funciona. Puede seguirse la incidencia en los foros de VirtualBox.

actualización II: en el foro de VirtualBox hay una solución. Simplemente hay que clonar la imagen .vhd a formato VDI y todo funciona. Nota, la imagen actualizada debe activarse antes de que la máquina arranque tres veces, si no no funciona, con lo que los pasos que recomiendo son:

  1. VBoxManage createhd –filename wxpie6.vdi –size 65536 # para crear la imagen de disco
  2. VBoxManage clonehd –existing ~/Downloads/Windows\ XP.vhd wxpie6.vdi # para convertir el .vhd al .vdi
  3. Configurar la máquina virtual en VirtualBox, muy importante poner la tarjeta de red paravirtualizada
  4. Arrancar la máquina, instalar la tarjeta de red paravirtualizada desde el CD y activar
  5. … instalar Guest Additions, deshabilitar dispositivos hardware problemáticos, etc.

Galletosquizofrenia

Me complace anunciar que tras mi última actualización de la versión inestable de Google Chrome, la funcionalidad de utilizar varios perfiles simultáneamente está disponible en Linux.