Integrando Redis en aplicaciones Symfony2

105
Monday, June 24, 13

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.

Transcript of Integrando Redis en aplicaciones Symfony2

Page 1: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 2: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 3: Integrando Redis en aplicaciones Symfony2

¿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

Page 4: Integrando Redis en aplicaciones Symfony2

Hablemos de algunos de nuestros desafíos

como desarrolladores

Monday, June 24, 13

Page 5: Integrando Redis en aplicaciones Symfony2

Tenemos aplicaciones que por su naturaleza no es posible usar una cache

Monday, June 24, 13

Page 6: Integrando Redis en aplicaciones Symfony2

Tenemos cientos, miles, de usuarios concurrentes y necesitamos una solución

escalable para almacenar las sesiones

Monday, June 24, 13

Page 7: Integrando Redis en aplicaciones Symfony2

Queremos saber quién y cómo se está usando

nuestra aplicación

Monday, June 24, 13

Page 8: Integrando Redis en aplicaciones Symfony2

Redis WTF?

Redis FTW!

Monday, June 24, 13

Page 9: Integrando Redis en aplicaciones Symfony2

Agenda

• Redis y sus características

• Entendiendo Redis

• Conectando desde PHP

• Integrando Redis en Symfony2

• Casos de uso

Monday, June 24, 13

Page 10: Integrando Redis en aplicaciones Symfony2

REDIS Y SUS CARACTERÍSTICAS

Monday, June 24, 13

Page 11: Integrando Redis en aplicaciones Symfony2

¿Qué es Redis?

• REmote DIctionary Server

• Creado en 2009 por Salvatore Sanfilipo (@antirez)

• Open source

Monday, June 24, 13

Page 12: Integrando Redis en aplicaciones Symfony2

Mejor definido como:

NoSQL

Monday, June 24, 13

Page 13: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 14: Integrando Redis en aplicaciones Symfony2

advancedin-memorykey-value

data-structure server

Redis

Monday, June 24, 13

Page 15: Integrando Redis en aplicaciones Symfony2

Data Structure Server

• Cadenas

• Listas

• Conjuntos

• Conjuntos ordenados

• Hashes (hash maps)

Monday, June 24, 13

Page 16: Integrando Redis en aplicaciones Symfony2

In-memory Database

• Datos deben caber en memoria

• Persistencia configurable

• “Memory is the new disc, disc is the new tape”

Monday, June 24, 13

Page 17: Integrando Redis en aplicaciones Symfony2

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

Page 18: Integrando Redis en aplicaciones Symfony2

Ideal para

• Analíticas real-time

• Tracking

• Caching server (memcached on steroid)

• Colas de trabajo

• Escritura/Lectura intensiva (sesiones)

Monday, June 24, 13

Page 19: Integrando Redis en aplicaciones Symfony2

ENTENDIENDO REDIS

Monday, June 24, 13

Page 20: Integrando Redis en aplicaciones Symfony2

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

Page 21: Integrando Redis en aplicaciones Symfony2

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

Page 22: Integrando Redis en aplicaciones Symfony2

No es un RMDBS

• No hay consultas (queries)

• No hay índices

• No hay esquemas

Monday, June 24, 13

Page 23: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 24: Integrando Redis en aplicaciones Symfony2

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

Page 25: Integrando Redis en aplicaciones Symfony2

Tipos de datosCadenas

Listas

Conjuntos

Conjuntos ordenados

Hashes

Data structure serverMonday, June 24, 13

Page 26: Integrando Redis en aplicaciones Symfony2

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

Page 27: Integrando Redis en aplicaciones Symfony2

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

Page 28: Integrando Redis en aplicaciones Symfony2

Casos de uso cadenas

• Contadores atómicos con:INCR, DECRINCRBY, DECRBYINCRFLOATBY

INCR dowloads:item:123=> 450INCR dowloads:item:123=> 451

Monday, June 24, 13

Page 29: Integrando Redis en aplicaciones Symfony2

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

Page 30: Integrando Redis en aplicaciones Symfony2

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

Page 31: Integrando Redis en aplicaciones Symfony2

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

Page 32: Integrando Redis en aplicaciones Symfony2

Operaciones de conjuntos

SINTERSECT SUNION

SDIFF

Monday, June 24, 13

Page 33: Integrando Redis en aplicaciones Symfony2

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

Page 34: Integrando Redis en aplicaciones Symfony2

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

Page 35: Integrando Redis en aplicaciones Symfony2

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

Page 36: Integrando Redis en aplicaciones Symfony2

Casos de uso Conjuntos Ordenados

• Leaderboards

• Rankings

• Tracking basado en tiempo

Monday, June 24, 13

Page 37: Integrando Redis en aplicaciones Symfony2

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

Page 38: Integrando Redis en aplicaciones Symfony2

Hashes

• Puede verse como un arreglo asociativo en PHP:clave => [ campo1 => valor1, campo2 => valor2, campo3 => valor3]

• Operaciones sobre campos individuales

Monday, June 24, 13

Page 39: Integrando Redis en aplicaciones Symfony2

Casos de uso Hashes

• Almacenamiento de objetos compuestos por varios campos

• Mappings

Monday, June 24, 13

Page 40: Integrando Redis en aplicaciones Symfony2

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

Page 41: Integrando Redis en aplicaciones Symfony2

CONECTANDO DESDE

PHP

Monday, June 24, 13

Page 42: Integrando Redis en aplicaciones Symfony2

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

Page 43: Integrando Redis en aplicaciones Symfony2

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

Page 44: Integrando Redis en aplicaciones Symfony2

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

Page 45: Integrando Redis en aplicaciones Symfony2

¿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

Page 46: Integrando Redis en aplicaciones Symfony2

La latencia de red sigue siendo el principal “performance killer”, no el cliente

Monday, June 24, 13

Page 47: Integrando Redis en aplicaciones Symfony2

INTEGRANDO REDIS EN

SYMFONY2

Monday, June 24, 13

Page 48: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 49: Integrando Redis en aplicaciones Symfony2

SncRedisBundle

{ "require": { "snc/redis-bundle": "1.1.*" }}

https://github.com/snc/SncRedisBundle

Monday, June 24, 13

Page 50: Integrando Redis en aplicaciones Symfony2

SncRedisBundle

• Integra Predis y phpredis en Symfony2

• Soporte para:

• Session storage

• Monolog logging handler

• SwiftMailer Spooling

• Doctrine caching

Monday, June 24, 13

Page 51: Integrando Redis en aplicaciones Symfony2

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

Page 52: Integrando Redis en aplicaciones Symfony2

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

Page 53: Integrando Redis en aplicaciones Symfony2

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

Page 54: Integrando Redis en aplicaciones Symfony2

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

Page 55: Integrando Redis en aplicaciones Symfony2

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

Page 56: Integrando Redis en aplicaciones Symfony2

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

Page 57: Integrando Redis en aplicaciones Symfony2

Sesiones

Monday, June 24, 13

Page 58: Integrando Redis en aplicaciones Symfony2

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

Page 59: Integrando Redis en aplicaciones Symfony2

Estado inconsistente en cada nodo(no sticky sessions)

Monday, June 24, 13

Page 60: Integrando Redis en aplicaciones Symfony2

• Difícil de escalar con mucho tráfico

• Escrituras en cada request

• Replication lag

Monday, June 24, 13

Page 61: Integrando Redis en aplicaciones Symfony2

• In-memory sessions

• Tiempo de acceso constante y predecible

• Escala horizontalmente

Monday, June 24, 13

Page 62: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 63: Integrando Redis en aplicaciones Symfony2

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

Page 64: Integrando Redis en aplicaciones Symfony2

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

Page 65: Integrando Redis en aplicaciones Symfony2

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

Page 66: Integrando Redis en aplicaciones Symfony2

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

Page 67: Integrando Redis en aplicaciones Symfony2

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

Page 68: Integrando Redis en aplicaciones Symfony2

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

Page 69: Integrando Redis en aplicaciones Symfony2

Monolog logging

Monday, June 24, 13

Page 70: Integrando Redis en aplicaciones Symfony2

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

Page 71: Integrando Redis en aplicaciones Symfony2

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

Page 73: Integrando Redis en aplicaciones Symfony2

SwiftMailer Spooling

Monday, June 24, 13

Page 74: Integrando Redis en aplicaciones Symfony2

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

Page 75: Integrando Redis en aplicaciones Symfony2

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

Page 76: Integrando Redis en aplicaciones Symfony2

Otros casos de uso en Symfony2

Monday, June 24, 13

Page 77: Integrando Redis en aplicaciones Symfony2

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

Page 78: Integrando Redis en aplicaciones Symfony2

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

Page 79: Integrando Redis en aplicaciones Symfony2

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

Page 80: Integrando Redis en aplicaciones Symfony2

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

Page 81: Integrando Redis en aplicaciones Symfony2

FlujoRequest/futbol

Request/sport/123

RouterMapperListener

Controller/Action

Routing System

Monday, June 24, 13

Page 82: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 83: Integrando Redis en aplicaciones Symfony2

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

Page 84: Integrando Redis en aplicaciones Symfony2

Integración con el profiler y web debug toolbar

Monday, June 24, 13

Page 85: Integrando Redis en aplicaciones Symfony2

Integración en el profiler

Monday, June 24, 13

Page 86: Integrando Redis en aplicaciones Symfony2

OTROS CASOS DE USO GENERALES

Monday, June 24, 13

Page 87: Integrando Redis en aplicaciones Symfony2

Presencia de usuarios(who is online?)

Monday, June 24, 13

Page 88: Integrando Redis en aplicaciones Symfony2

¿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

Page 89: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 90: Integrando Redis en aplicaciones Symfony2

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

Page 91: Integrando Redis en aplicaciones Symfony2

Usuarios online

Amigos

AmigosOnline

Monday, June 24, 13

Page 92: Integrando Redis en aplicaciones Symfony2

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

Page 93: Integrando Redis en aplicaciones Symfony2

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

Page 94: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 95: Integrando Redis en aplicaciones Symfony2

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

Page 96: Integrando Redis en aplicaciones Symfony2

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

Page 97: Integrando Redis en aplicaciones Symfony2

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

Page 98: Integrando Redis en aplicaciones Symfony2

CONCLUSIONES

Monday, June 24, 13

Page 99: Integrando Redis en aplicaciones Symfony2

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

Page 100: Integrando Redis en aplicaciones Symfony2

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

Page 101: Integrando Redis en aplicaciones Symfony2

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

Page 102: Integrando Redis en aplicaciones Symfony2

Monday, June 24, 13

Page 103: Integrando Redis en aplicaciones Symfony2

Referencias

http://redis.io/commandshttp://redis.io/documentation

Monday, June 24, 13

Page 104: Integrando Redis en aplicaciones Symfony2

Muchas Graciashttps://joind.in/8844

@ronnylt

https://github.com/ronnylt

Monday, June 24, 13

Page 105: Integrando Redis en aplicaciones Symfony2

We are hiring!

Monday, June 24, 13