Nuestro sistema
no puede ser una caja negra
- Sabemos que existen elementos diferentes que interactuan
- Los usuarios hacen peticiones a nuestro sistema y se responden
- Y bueno... esto... mmmm... creo que ya con eso está todo, ¿no?
¡En realidad no sabemos nada!
Medir y conocer el estado del sistema
Si no tenemos mediciones de todos los aspectos del sistema no sabemos cómo se comporta cuando funciona bien, cómo lo hace cuando funciona mal y mucho menos podemos identificar los problemas que ocurren para hallar soluciones. Las mediciones nos ayudan a detectar tendencias y adelantarnos a los sucesos.
¡Sin mediciones estamos perdidos!
Protocolo SNMP
Facilita el intercambio de información de administración entre dispositivos de red
Comandos: snmpwalk, snmpbulkwalk, snmp*
Logs en tiempo real: Agregación
Si trabajamos con demasiados logs es importante utilizar estrategias de agregación para evitar vernos abrumados
ElasticSearch, Kibana, Sentry, Graphite
Primer cuello de botella:
la base de datos
- Notamos que las peticiones de los usuarios se resuelven muy lento por culpa de la base de datos
- El servidor de aplicaciones pasa la mayor parte del tiempo esperando
- Las peticiones se acumulan a mayor velocidad de lo que se responden
¡Tenemos que resolver el problema de la base de datos!
Las lecturas son lentas (1)
- Descubrimos que los usuarios consumen datos en una proporción mucho mayor a la que los generan
- Una vez publicado un contenido por parte de un usuario, muchos otros usuarios leen ese contenido
- Las operaciones de escritura no son un problema
- ¿Escalamos el sistema para mejorar la lectura de datos?
- Utilizamos un esquema de replicación master/slave en SQL
Las lecturas son lentas (2)
Sincronizamos los servidores de bases de datos para que se hagan las escrituras en el master y las lecturas en los slaves
Todas las bases de datos en el cluster son réplicas exactas y con ellas el rendimiento en lectura se multiplica por 3
Demasiados usuarios para una BD (1)
- Nuestro web, nuestro sistema... ¡todo funcionaba bien...
- ...hasta que llegaron los usuarios!
- Ya no podemos almacenar todos los usuarios en una única base de datos (la tabla de usuarios es enooooooooorme)
- ¿Y si repartimos la tabla de usuarios en múltiples servidores?
- Utilizamos una partición de la tabla en shards
Demasiados usuarios para una BD (2)
Dividimos la tabla de usuarios de acuerdo a alguna estrategia (aleatorio, módulo, hash...) y la repartimos en varios servidores
Ahora podemos almacenar más usuarios y además el rendimiento se multiplica por 4
Decisiones complicadas (1)
- Pensábamos que estaba todo resuelto, pero ahora teníamos un reto nuevo que era difícil de resolver
- Los usuarios se escriben mensajes entre sí y...
- puedo almacenar los mensajes de acuerdo al destinatario y facilitar la operación de lectura de mensajes recibidos
- o puedo almacenar los mensajes según emisor y facilitar la lectura de mensajes enviados
- si no lo hago de alguna de las formas anteriores entonces ambas operaciones serán ineficientes
- Caso de muros o conversaciones de Facebook, Twitter...
- ¿Y qué tal si duplico información y almaceno lo mismo de las dos formas? Sacrifico almacenamiento y gano rendimiento
Decisiones complicadas (2)
La ventaja de esta duplicidad es que la lectura de mensajes enviados y recibidos es eficiente. Además, un usuario puede borrar sus mensajes sin afectar a los demás usuarios.
Alternativas más allá de SQL
- Hasta el momento hemos escalado bases de datos SQL
- Hay muy buenas bases de datos SQL como son MySQL, Percona (buen clustering), MaríaDB y PostgreSQL
- Sin embargo, existen otras alternativas "mejores"
- ¿Qué hay de las bases de datos NoSQL? ¿acaso no permiten escalar más fácil? Además, tenemos muchas opciones: HBase (BigTable de Google), Cassandra (Facebook, Twitter, Digg), MongoDB, CouchDB, Redis, ElasticSearch, Neo4j...
- Ah, y que no se nos olviden los archivos en disco duro, que para ciertos casos pueden ser la mejor opción
Características de nuestros datos
- ¿Conocemos bien cómo son nuestros datos y cómo han de ser manipulados? ¿utilizar X es la mejor forma para almacenarlos?
- ¿Es mejor SQL o NoSQL? (o inclusive un archivo)
- Las bases de datos SQL fueron diseñadas y pensadas con un propósito diferente a las bases de datos NoSQL, aunque pensemos en ambas opciones como equivalentes
Un poco de teoría sobre bases de datos
En informática, el teorema CAP, también llamado Teorema de Brewer, establece que es imposible para un sistema de cómputo distribuido garantizar simultáneamente:
- La consistencia (Consistency), es decir, que todos los nodos vean la misma información al mismo tiempo
- La disponibilidad (Availability), es decir, la garantía de que cada petición a un nodo reciba una confirmación de si ha sido o no resuelta satisfactoriamente
- La tolerancia a fallos (Partition Tolerance), es decir, que el sistema siga funcionando a pesar de algunas pérdidas arbitrarias de información o fallos parciales del sistema
Caché
- Almacenamos todo en la base de datos aunque...
- no todos los datos se consumen
- sólo un subconjunto de datos se consume de forma intensiva (más recientes, más referenciados...)
- la frecuencia de acceso a cada dato es diferente, y todos se sirven de la misma forma → ineficiencia
- Observamos que con el tiempo varían los datos que consumen nuestros usuarios de forma intensiva
- El valor de los datos que almacenamos decrece con el tiempo
¡Optimicemos el acceso a los datos más utilizados!
Utilidad de la caché
- Caché hit: el dato está contenido en la caché y se sirve desde memoria, con el consiguiente aumento de velocidad
- Caché miss: el dato no está contenido en la caché. Se lee el dato desde su orígen, se sirve el dato solicitado y además de actualiza la caché para la próxima lectura.
- Nos podemos permitir políticas complejas de actualización de caché debido al elevado coste de leer el dato de origen
- Heurística para saber cuántos servidores de caché utilizar:
si caché hit < 85%, entonces ponemos más servidores
Utilización de caché en Facebook
A finales de 2008 Facebook utilizaba más de 800 servidores de caché con una capacidad de más de 28 TB de memoria (enlace)
Han desarrollado Claspin, que es una aplicación que representa de forma visual e intuitiva el estado de la caché de su sistema
Escalando en manejo de datos
Tercer cuello de botella:
interacción del usuario
- Todas las peticiones de usuarios se responden rápido, menos aquellas relativas a acciones lentas
- Parece que a veces es inevitable hacer que algunas peticiones tomen su tiempo, y más si se refieren a IO
- El usuario debería concienciarse de que ciertas acciones suyas toman un tiempo para realizarse
¡Hemos de hacer lo que sea para evitar las esperas del usuario!
Un usuario borra una foto (1)
- Recibimos la petición de borrado en un servidor de aplicación
- Realizamos una llamada al servicio de almacenamiento
- El servicio busca la foto consultando una base de datos
- Marca la foto como eliminada para que no esté disponible
- Ordena el borrado de la foto al servidor donde se está almacenando
- El servidor elimina la foto
- Devuelve el resultado de la operación
- Si se borró con éxito elimina la referencia en base de datos
- Recibe la respuesta y notifica al servidor de aplicación
- El servicio responde con el resultado de la operación
- Enviamos el resultado de la operación al usuario
¿Alguien ha dicho Timeout Error
?
Un usuario borra una foto (2)
- A nadie le gusta esperar, ¡reconozcámoslo!
- Los navegadores web (lado usuario) y los servidores web (nuestro lado) lanzan
Timeout Error
- En los servidores web matamos a los procesos bloqueados. ¿Terminará o no el proceso? (problema de la parada)
- Existen procesos o tareas lentas cuyo requisito temporal excede el tiempo de vida de una petición
Un usuario borra una foto (3)
- Al usuario no le gusta esperar, ¡nunca!
- ¿Tenemos que hacer esperar su petición hasta tener la confirmación de que su foto ha sido eliminada del sistema?
- A ver... idealmente, el usuario espera pulsar el botón de borrado, que la foto "le aparezca como que ya no está" y que pueda seguir navegando en nuestra aplicación
- Qué tal si...
Un usuario borra una foto (4)
- Recibimos la petición de borrado en un servidor de aplicación
- Deshabilitamos la foto para no seguir sirviéndola
- Creamos una tarea para borrar realmente la foto
- Respondemos al usuario que su foto ha sido borrada
La creación de la tarea es inmediata y no bloquea el flujo de ejecución. La percepción del usuario es que hemos sido más rápidos borrando su foto. La foto ya no está disponible para otros usuarios, y la tarea de borrado se encarga de eliminarla.
/path/to/command arg1 arg2 & # Tarea en segundo plano
Un usuario borra una foto (5)
- En realidad podemos hacerlo mejor, pues el usuario espera menos pero de todas formas sigue teniendo que esperar
- Desarrollamos todo el proceso mediante llamadas asíncronas para notificar al usuario con el resultado de la tarea
- No bloqueamos el flujo de ejecución y permitimos al usuario seguir navegando mientras está atento a un evento
- En algún momento el usuario recibe un evento con el resultado de la operación que procesamos con un callback
function slowTaskCallback() {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.onload = slowTaskCallback;
oReq.open("get", "http://api.example.com/a/restful/resource", true);
oReq.send();
Un usuario borra una foto (6)
Finalmente, la solución ideal combina dos elementos diferentes en el lado del usuario y en el lado de nuestro sistema:
- Navegador del usuario
- Llamadas asíncronas al servidor. Enviamos la petición y esperamos respuesta en forma de evento asíncrono.
- Nuestro sistema
- Llamadas asíncronas entre servicios internos. Enviamos la petición sin bloquear ni hacer esperar al procesador.
- Para las operaciones costosas creamos una tarea mediante el envío de un mensaje que se encola en alguna parte
- Si no queremos hacer algo ahora tenemos que decirle a alguien que lo haga después, y esto es la mensajería
- El mensaje se encola y cuando llegue su turno se ejecuta la tarea asociada
Un usuario borra una foto (7)
- Llamadas asíncronas en el lado del servidor
- Podemos utilizar JavaScript en el lado del servidor con Node.js. La ventaja principal es que las bibliotecas para este servidor de aplicación son asíncronas y no bloquean.
- También hay opciones en Python, con bibliotecas como Twisted, Tornado o Gunicorn entre otros.
- Las anteriores son en realidad bibliotecas de networking que implementan el patrón reactor
- Procesamiento de tareas en colas
- Existen muchas alternativas para desplegar un sistema de mensajería con colas: AMPQ, JMS y Spread
- Una buena opción es RabbitMQ, que implementa AMPQ con el lenguaje Erlang
Diagrama de eventos
Agregamos servidores de tareas