Upload
ronny-lopez
View
1.234
Download
2
Embed Size (px)
DESCRIPTION
Sus múltiples casos de usos y su excepcional rendimiento hacen que Redis sea hoy una pieza clave en la arquitectura de aplicaciones altamente dinámicas. En la charla se expone de forma práctica cómo puede integrarse Redis en una aplicación Symfoy y cómo pueden implementarse varias de las características de las aplicaciones usando Redis, como por ejemplo: Session storage, Monolog logging handlers, Doctrine caching, SwiftMailer spooling, Profiler storage, Data Collector for Symfony2 Profiler. Además de estos casos de uso generales, se comentan otros casos de usos específicos de aplicaciones que por su naturaleza no pueden beneficiarse de una capa de cache y se requiere por tanto una herramienta eficiente y escalable para resolver ciertos tipos de problemas.
Citation preview
Monday, June 24, 13
Monday, June 24, 13
¿Quién soy?
• Backend Core Tech Lead @ SocialpointArquitectura y desarrollo de aplicaciones que eficientemente respondan peticiones de millones de usuarios cada día
• Redis Fan
• Made in Cuba
• Poco original eligiendo memes
• @ronnylt
Monday, June 24, 13
Hablemos de algunos de nuestros desafíos
como desarrolladores
Monday, June 24, 13
Tenemos aplicaciones que por su naturaleza no es posible usar una cache
Monday, June 24, 13
Tenemos cientos, miles, de usuarios concurrentes y necesitamos una solución
escalable para almacenar las sesiones
Monday, June 24, 13
Queremos saber quién y cómo se está usando
nuestra aplicación
Monday, June 24, 13
Redis WTF?
Redis FTW!
Monday, June 24, 13
Agenda
• Redis y sus características
• Entendiendo Redis
• Conectando desde PHP
• Integrando Redis en Symfony2
• Casos de uso
Monday, June 24, 13
REDIS Y SUS CARACTERÍSTICAS
Monday, June 24, 13
¿Qué es Redis?
• REmote DIctionary Server
• Creado en 2009 por Salvatore Sanfilipo (@antirez)
• Open source
Monday, June 24, 13
Mejor definido como:
NoSQL
Monday, June 24, 13
Monday, June 24, 13
advancedin-memorykey-value
data-structure server
Redis
Monday, June 24, 13
Data Structure Server
• Cadenas
• Listas
• Conjuntos
• Conjuntos ordenados
• Hashes (hash maps)
Monday, June 24, 13
In-memory Database
• Datos deben caber en memoria
• Persistencia configurable
• “Memory is the new disc, disc is the new tape”
Monday, June 24, 13
Advanced key-value store database
• Persistencia (snapshot, append-only file)
• Replicación (master/slave)
• Transacciones
• Pipelining
• Publisher/Subscriber (pub/sub)
• Lua scripting
Monday, June 24, 13
Ideal para
• Analíticas real-time
• Tracking
• Caching server (memcached on steroid)
• Colas de trabajo
• Escritura/Lectura intensiva (sesiones)
Monday, June 24, 13
ENTENDIENDO REDIS
Monday, June 24, 13
Claves y valores
• Los datos (values) son refereciados a través de claves (keys)
• Los datos pueden ser recuperados solo si conocemos el nombre de la clave
Monday, June 24, 13
Claves(keys)
• Únicas dentro de la BD
• Binary safe string
• Claves muy grandes pueden impactar en el rendimimiento
• Claves muy pequeñas no aportan mucho (u:123:n vs user:123:name)
Monday, June 24, 13
No es un RMDBS
• No hay consultas (queries)
• No hay índices
• No hay esquemas
Monday, June 24, 13
Monday, June 24, 13
Comandos
• Lenguaje de comandos fácil de usar y de aprender
• Los comandos (en su mayoría) son aplicables a un tipo de datos específico
Monday, June 24, 13
Tipos de datosCadenas
Listas
Conjuntos
Conjuntos ordenados
Hashes
Data structure serverMonday, June 24, 13
Cadenas
• Tipo de dato simple (cualquier cadena binary-safe)
• Tamaño máximo de 512 MB
key string
GET, SET, STRLEN, APPEND, GETRANGE, SETRANGE
http://redis.io/commands#string
Monday, June 24, 13
Casos de uso cadenas
• Almacenamiento de cualquier dato (serializado): GET, SET
• Vector de acceso aleatorio con GETRANGE, SETRANGE
• Mapa de bits usando GETBIT, SETBIT, BITCOUNT
Monday, June 24, 13
Casos de uso cadenas
• Contadores atómicos con:INCR, DECRINCRBY, DECRBYINCRFLOATBY
INCR dowloads:item:123=> 450INCR dowloads:item:123=> 451
Monday, June 24, 13
Listas
• Listado de cadenas donde el orden es importante
• Operaciones de inserción por la izquierda y por la derecha o por posición
• Máxima longitud de 2^32 -1 (+4 billones)
key s2s1 s3...
http://redis.io/commands#list
Monday, June 24, 13
Casos de uso Listas
• Representación de colas (insertando por la derecha, leyendo por la izquierda) RPUSH, LPOP
• Representación de pilas (insertando y leyendo por la izquierda) LPUSH, LPOP
• Comandos blocking BLPOP, BRPOP, BRPOPLPUSH
Monday, June 24, 13
Conjuntos
• Colección de elementos únicos donde el orden no importa
• Operaciones típicas de conjuntos sobre los datos
keyblue greenredblack
Monday, June 24, 13
Operaciones de conjuntos
SINTERSECT SUNION
SDIFF
Monday, June 24, 13
Casos de uso Conjuntos
• Representación de relaciones
• Tracking de sucesos únicos
• Cualquier problema donde por su naturaleza se realicen operaciones sobre conjuntos
Monday, June 24, 13
Conjuntos Ordenados
• Conjuntos de datos, pero ordenados por un score
• Elementos únicos dentro del conjunto, cada uno con un score asignado
key
blue – 520
green – 890
red – 303
black – 680
Monday, June 24, 13
Conjuntos Ordenados
• Operaciones de conjuntos aplicables
• Operaciones de acceso por score y por rango en tiempo constante y predecible
http://redis.io/commands#sorted_set
Monday, June 24, 13
Casos de uso Conjuntos Ordenados
• Leaderboards
• Rankings
• Tracking basado en tiempo
Monday, June 24, 13
Hashes
• Múltiples campo => valor en una misma clave
• Hasta un máximo de 2^32 -1 pares campo => valor
key field1 value1
field2 value2
field3 value3
http://redis.io/commands#hash
Monday, June 24, 13
Hashes
• Puede verse como un arreglo asociativo en PHP:clave => [ campo1 => valor1, campo2 => valor2, campo3 => valor3]
• Operaciones sobre campos individuales
Monday, June 24, 13
Casos de uso Hashes
• Almacenamiento de objetos compuestos por varios campos
• Mappings
Monday, June 24, 13
Resumiendo...
• Tenemos la oportunidad de usar la estructura de datos adecuada para cada tipo de problema
• Tendremos tiempos de ejecución constantes y predecibles, sin importar el tamaño de los conjuntos de datos (dataset)
Monday, June 24, 13
CONECTANDO DESDE
PHP
Monday, June 24, 13
Clientes para PHP
• https://github.com/nrk/predis
• https://github.com/nicolasff/phpredis
Clientes disponibles para la mayoría de los lenguajes de programación (http://redis.io/clients)
Monday, June 24, 13
Predis "require": { "predis/predis": "~0.8.3" },
• Escrito en PHP
• Maduro y activamente mantenido
• Extensible
• Feature-complete (pipelines, client side sharding, server profiles, master/slave config, etc.)
Monday, June 24, 13
phpredis
• Escrito en C como una extensión PHP
• Listo para producción
• Extremadamente rápido
• No backward compatible con anteriores versions de Redis
Monday, June 24, 13
¿Cuál usar?
• Depende...
• Predis cubre la mayoría de las necesidades, fácil de instalar con composer, y nos ofrece un rendimiento aceptable
• phpredis si necesitas un rendimiento excepcional
Monday, June 24, 13
La latencia de red sigue siendo el principal “performance killer”, no el cliente
Monday, June 24, 13
INTEGRANDO REDIS EN
SYMFONY2
Monday, June 24, 13
Monday, June 24, 13
SncRedisBundle
{ "require": { "snc/redis-bundle": "1.1.*" }}
https://github.com/snc/SncRedisBundle
Monday, June 24, 13
SncRedisBundle
• Integra Predis y phpredis en Symfony2
• Soporte para:
• Session storage
• Monolog logging handler
• SwiftMailer Spooling
• Doctrine caching
Monday, June 24, 13
Definiendo clientes
snc_redis: clients: default: type: predis alias: default dsn: redis://redis.example.com
session: type: predis alias: session dsn: - redis://rses1.example.com - redis://rses2.example.com
config.yml / redis.yml
Monday, June 24, 13
Configuración avanzada
snc_redis: clients: cache: type: predis alias: cache dsn: - redis://cache1.example.com - redis://cache2.example.com options: profile: 2.6 connection_timeout: 10 readwrite_timeout: 30
config.yml / redis.yml
Monday, June 24, 13
Obteniendo el cliente a través del container
$redis = $container->get('snc_redis.default');$key = 'downloads:' . $pid . ':count'$downloads = $redis->incr($key);
php app/console container:debug | grep snc_redis
Monday, June 24, 13
Clientes registrados como servicios
php app/console container:debug snc_redis.defaultInformation for service snc_redis.default
Service Id snc_redis.defaultClass Predis\ClientTags -Scope containerPublic yesSynthetic noRequired File -
Monday, June 24, 13
Inyectando Redis como dependencia
namespace Acme\DemoBundle\Service;
use Snc\RedisBundle\Client\Predis as Redis;
class DownloadCounter{ protected $redis;
public function __construct(Redis $redis) { $this->redis = $redis; }
public function count($itemId) { return $this->redis->incr('downloads:' . $itemId . ':count'); }}
Monday, June 24, 13
Inyectando Redis como dependencia
<service id="acme.demo.download_counter" class="Acme\DemoBundle\Service\DownloadCounter">
...
<argument type="service" id="snc_redis.default" />...
</service>
Monday, June 24, 13
Sesiones
Monday, June 24, 13
Sesiones
• Difícil de escalar con la configuración por defecto
• Por naturaleza no cacheable (read-change-write back)
• Se necesita mantener estado consistente
Monday, June 24, 13
Estado inconsistente en cada nodo(no sticky sessions)
Monday, June 24, 13
• Difícil de escalar con mucho tráfico
• Escrituras en cada request
• Replication lag
Monday, June 24, 13
• In-memory sessions
• Tiempo de acceso constante y predecible
• Escala horizontalmente
Monday, June 24, 13
Monday, June 24, 13
Sesiones en Redis
• Sesiones distribuídas (ej. detrás de un balanceador sin sticky sessions)
• Excepcional rendimiento de escritura/lectura
• Tiempo de acceso constante y predecible
• Escalable horizontalmente (client-side sharding)
Monday, June 24, 13
Session handlers
• A través de un session handler implementado en PHP, conectando a través de un cliente Redis
• A través de un session handler implementado en una extensión de PHP (phpredis)
Monday, June 24, 13
Usando Predis
snc_redis: clients: session_cluster: type: predis alias: session dsn: - redis://sess000.example.net - redis://sess001.example.net - redis://sess002.example.net
session: client: session_cluster ttl: 1200 prefix: appsession
config.yml
Monday, June 24, 13
Usando phpredis
framework: session: # Default storage service storage_id: "session.storage.native"
# No handler service, use default handler_id: ~
# The name for the session cookie name: "appsesid"
config.yml
Monday, June 24, 13
php.ini usando phpredis
session.save_handler = redis
session.save_path = " tcp://s000.example.net:6379?weight=1, tcp://s001.example.net:6379?weight=2, tcp://s002.example.net:6379?weight=2"
Monday, June 24, 13
redis 127.0.0.1:6379> MONITOROK
"GET" "appsession:9jmmp11dvh3b4f1bp9trfuqlj3"
"SETEX" "appsession:9jmmp11dvh3b4f1bp9trfuqlj3" "1440" "_sf2_attributes|a:1:{s:5:\"visit\";i:1371678033;} _sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:\"u\";i:1371678033;s:1:\"c\";i:1371678023;s:1:\"l\";s:1:\"0\";}"
session name
session id (cookie)
ttl(expire time)
session data
Monday, June 24, 13
Monolog logging
Monday, June 24, 13
Monolog logging
• Los mensajes de logs son almacenados en una lista
• Ideal cuando se necesita un broker que reciba los logs que serán posteriormente enviados a un agregador (ej. logstash)
Monday, June 24, 13
Monolog configsnc_redis: clients: monolog: type: predis alias: monolog dsn: redis://localhost/1 logging: false monolog: client: monolog key: monologmonolog: handlers: main: type: service id: monolog.handler.redis level: debug
Monday, June 24, 13
Referencias
• http://blog.lusis.org/blog/2012/01/31/load-balancing-logstash-with-redis/
Monday, June 24, 13
SwiftMailer Spooling
Monday, June 24, 13
SwiftMailer Spooling
• Los mensajes no se envian directamente, sino que se mantienen en un “spool” y son enviados por un proceso en background
• Usando redis como “spool”, los mensajes son mantenidos en una lista hasta que son enviados
Monday, June 24, 13
Mailer spooling
snc_redis: clients: emails: type: predis alias: emails dsn: redis://emails-spool-00.example.com logging: false
swiftmailer: client: emails key: swiftmailer
config.yml
Monday, June 24, 13
Otros casos de uso en Symfony2
Monday, June 24, 13
Router dinámicos• Necesitamos convertir URLs amigables a rutas
internas de Symfony2:
Idioma Ruta interna Ruta “amigable”
es /sport/123 /futbol
en /sport/123 /football
Monday, June 24, 13
Router dinámicos
Idioma Ruta “amigable”
_controller
es /futbol Bundle:SportController:sportPageAction, array(‘sport’ => 123)
es /madrid Bundle:CityController:cityPageActionarray(‘city’ => 456)
Monday, June 24, 13
Desventajas
• Difícil de cambiar la configuración de routing una vez creadas
• Es requerido tener copia de la base de datos de routings en los ambientes de desarrollo para que la aplicación funcione
Monday, June 24, 13
Solución alternativa
• Crear un mapping entre rutas amigables y rutas internas
• Subscribirse a KernelEvents::REQUEST y antes que nada, cambiar pathInfo en el objeto Request
Monday, June 24, 13
FlujoRequest/futbol
Request/sport/123
RouterMapperListener
Controller/Action
Routing System
Monday, June 24, 13
Monday, June 24, 13
Usando Redis hashes
/futbol /sport/123
/baloncesto /sport/456
/tenis /sport/789
routes:es
/sport/123 /futbol
/sport/456 /baloncesto
/sport/789 /tenis
alias:es
Monday, June 24, 13
Integración con el profiler y web debug toolbar
Monday, June 24, 13
Integración en el profiler
Monday, June 24, 13
OTROS CASOS DE USO GENERALES
Monday, June 24, 13
Presencia de usuarios(who is online?)
Monday, June 24, 13
¿Quién está online?
• Mantenemos un conjunto con los usuarios que han estado online por cada minuto
• En cada request, agregamos al usuario al conjunto de usuarios online del minuto actual
• Obtenemos los usuarios que han estado online de la unión de los 5 últimos conjuntos
Monday, June 24, 13
Monday, June 24, 13
Amigos online
• Mantenemos un conjunto con los amigos de cada usuario
• Obtenemos los amigos que están online, de la intersección del conjunto de usuarios online con el conjunto de amigos de un usuario
Monday, June 24, 13
Usuarios online
Amigos
AmigosOnline
Monday, June 24, 13
class OnlineUsersManager{ protected $redis;
public function __construct(Redis $redis) { $this->redis = $redis; }
public function trackUser($userId) { $timestamp = time(); $minute = date('i', $timestamp);
$key = 'online_users:' . $minute;
// Add the user to the set of online users in the current minute. $this->redis->sadd($key, $userId);
// Expire in 10 minutes. $this->redis->expire(60 * 10); }}
Monday, June 24, 13
Leaderboards
• Caso de uso típico el cual es fácil de implementar con Redis y díficil de implementar de forma eficiente en otro sistema
• Los conjuntos ordenados son las estructuras de datos perfectas para su implementación
Monday, June 24, 13
Monday, June 24, 13
class RankingManager{ protected $redis;
public function __construct(Redis $redis) { $this->redis = $redis; }
public function onCombatFinished(Combat $combat) { $winner = $combat->getAttacker(); $score = $combat->getScoreResult();
$this->redis->zincr('ranking', $score, $winner->getId()); }
public function getTopScores($limit) { return $this->redis->zrevrange('ranking', 0, $limit); }}
Monday, June 24, 13
Extra Tips
• Redis es single-threaded, todos los comandos son atómicos
• Transacciones pueden usarse para ejecutar múltiples comandos de forma atómica
• Lua scripts se ejecutan de forma atómica también
•
Monday, June 24, 13
Performance Tips
• Usando client-side sharding puede escalarse horizontalmente ganando en capacidad y rendimiento
• Ejecutar múltiples comandos a través de pipelines
Monday, June 24, 13
CONCLUSIONES
Monday, June 24, 13
NO usar Redis
• Cuando el conjunto de datos (dataset) no cabe en memoria
• Datos de naturaleza relacional
• Cuando no se conoce de antemano como van a consultarse los datos
Monday, June 24, 13
Cuándo usar Redis
• Redis para datos temporales, altamente dinámicos y estructuras de datos complejas
• Datos de naturaleza no-relacional
• Ideal para aplicaciones que son write-heavy
• Datos que naturalmente se ajustan a una estructura de Redis
Monday, June 24, 13
Inegrando Redis en un stack PHP/Symfony2
• No necesariamente como la DB principal
• Resolviendo problemas que son difíciles de resolver en un sistema relacional
• Beneficiandonos de las características de Redis de forma incremental
• Usando la herramienta adecuada para cada tarea
Monday, June 24, 13
Monday, June 24, 13
Referencias
http://redis.io/commandshttp://redis.io/documentation
Monday, June 24, 13
Muchas Graciashttps://joind.in/8844
@ronnylt
https://github.com/ronnylt
Monday, June 24, 13
We are hiring!
Monday, June 24, 13