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

Integrando Redis en aplicaciones Symfony2

Integrando Redis en aplicaciones Symfony2

deSymfony – Madrid 22 de junio de 2013

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.

Ronny López

June 22, 2013
Tweet

More Decks by Ronny López

Other Decks in Programming

Transcript

  1. Monday, June 24, 13

    View full-size slide

  2. Monday, June 24, 13

    View full-size slide

  3. ¿Quién soy?
    • Backend Core Tech Lead @ Socialpoint
    Arquitectura 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

    View full-size slide

  4. Hablemos de algunos
    de nuestros desafíos
    como desarrolladores
    Monday, June 24, 13

    View full-size slide

  5. Tenemos aplicaciones que
    por su naturaleza no es
    posible usar una cache
    Monday, June 24, 13

    View full-size slide

  6. Tenemos cientos, miles,
    de usuarios concurrentes y
    necesitamos una solución
    escalable para almacenar las
    sesiones
    Monday, June 24, 13

    View full-size slide

  7. Queremos saber quién
    y cómo se está usando
    nuestra aplicación
    Monday, June 24, 13

    View full-size slide

  8. Redis WTF?
    Redis FTW!
    Monday, June 24, 13

    View full-size slide

  9. Agenda
    • Redis y sus características
    • Entendiendo Redis
    • Conectando desde PHP
    • Integrando Redis en Symfony2
    • Casos de uso
    Monday, June 24, 13

    View full-size slide

  10. REDIS Y SUS
    CARACTERÍSTICAS
    Monday, June 24, 13

    View full-size slide

  11. ¿Qué es Redis?
    • REmote DIctionary Server
    • Creado en 2009 por Salvatore Sanfilipo
    (@antirez)
    • Open source
    Monday, June 24, 13

    View full-size slide

  12. Mejor definido como:
    NoSQL
    Monday, June 24, 13

    View full-size slide

  13. Monday, June 24, 13

    View full-size slide

  14. advanced
    in-memory
    key-value
    data-structure server
    Redis
    Monday, June 24, 13

    View full-size slide

  15. Data Structure Server
    • Cadenas
    • Listas
    • Conjuntos
    • Conjuntos ordenados
    • Hashes (hash maps)
    Monday, June 24, 13

    View full-size slide

  16. In-memory Database
    • Datos deben caber en memoria
    • Persistencia configurable
    • “Memory is the new disc, disc is the new
    tape”
    Monday, June 24, 13

    View full-size slide

  17. 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

    View full-size slide

  18. Ideal para
    • Analíticas real-time
    • Tracking
    • Caching server (memcached on steroid)
    • Colas de trabajo
    • Escritura/Lectura intensiva (sesiones)
    Monday, June 24, 13

    View full-size slide

  19. ENTENDIENDO
    REDIS
    Monday, June 24, 13

    View full-size slide

  20. 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

    View full-size slide

  21. 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

    View full-size slide

  22. No es un RMDBS
    • No hay consultas (queries)
    • No hay índices
    • No hay esquemas
    Monday, June 24, 13

    View full-size slide

  23. Monday, June 24, 13

    View full-size slide

  24. 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

    View full-size slide

  25. Tipos de datos
    Cadenas
    Listas
    Conjuntos
    Conjuntos ordenados
    Hashes
    Data structure server
    Monday, June 24, 13

    View full-size slide

  26. 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

    View full-size slide

  27. 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

    View full-size slide

  28. Casos de uso cadenas
    • Contadores atómicos con:
    INCR, DECR
    INCRBY, DECRBY
    INCRFLOATBY
    INCR dowloads:item:123
    => 450
    INCR dowloads:item:123
    => 451
    Monday, June 24, 13

    View full-size slide

  29. 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 s2
    s1 s3
    ...
    http://redis.io/commands#list
    Monday, June 24, 13

    View full-size slide

  30. 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

    View full-size slide

  31. Conjuntos
    • Colección de elementos únicos donde el
    orden no importa
    • Operaciones típicas de conjuntos sobre los
    datos
    key
    blue
    green
    red
    black
    Monday, June 24, 13

    View full-size slide

  32. Operaciones de
    conjuntos
    SINTERSECT SUNION
    SDIFF
    Monday, June 24, 13

    View full-size slide

  33. 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

    View full-size slide

  34. 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

    View full-size slide

  35. 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

    View full-size slide

  36. Casos de uso
    Conjuntos Ordenados
    • Leaderboards
    • Rankings
    • Tracking basado en tiempo
    Monday, June 24, 13

    View full-size slide

  37. 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

    View full-size slide

  38. Hashes
    • Puede verse como un arreglo asociativo en PHP:
    clave => [
    campo1 => valor1,
    campo2 => valor2,
    campo3 => valor3
    ]
    • Operaciones sobre campos individuales
    Monday, June 24, 13

    View full-size slide

  39. Casos de uso Hashes
    • Almacenamiento de objetos compuestos
    por varios campos
    • Mappings
    Monday, June 24, 13

    View full-size slide

  40. 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

    View full-size slide

  41. CONECTANDO
    DESDE
    PHP
    Monday, June 24, 13

    View full-size slide

  42. 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

    View full-size slide

  43. 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

    View full-size slide

  44. 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

    View full-size slide

  45. ¿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

    View full-size slide

  46. La latencia de red sigue
    siendo el principal
    “performance killer”,
    no el cliente
    Monday, June 24, 13

    View full-size slide

  47. INTEGRANDO REDIS
    EN
    SYMFONY2
    Monday, June 24, 13

    View full-size slide

  48. Monday, June 24, 13

    View full-size slide

  49. SncRedisBundle
    {
    "require": {
    "snc/redis-bundle": "1.1.*"
    }
    }
    https://github.com/snc/SncRedisBundle
    Monday, June 24, 13

    View full-size slide

  50. SncRedisBundle
    • Integra Predis y phpredis en Symfony2
    • Soporte para:
    • Session storage
    • Monolog logging handler
    • SwiftMailer Spooling
    • Doctrine caching
    Monday, June 24, 13

    View full-size slide

  51. 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

    View full-size slide

  52. 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

    View full-size slide

  53. 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

    View full-size slide

  54. Clientes registrados
    como servicios
    php app/console container:debug
    snc_redis.default
    Information for service snc_redis.default
    Service Id snc_redis.default
    Class Predis\Client
    Tags -
    Scope container
    Public yes
    Synthetic no
    Required File -
    Monday, June 24, 13

    View full-size slide

  55. 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

    View full-size slide

  56. Inyectando Redis como
    dependencia
    class="Acme\DemoBundle\Service\DownloadCounter">
    ...

    ...

    Monday, June 24, 13

    View full-size slide

  57. Sesiones
    Monday, June 24, 13

    View full-size slide

  58. 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

    View full-size slide

  59. Estado inconsistente en cada nodo
    (no sticky sessions)
    Monday, June 24, 13

    View full-size slide

  60. • Difícil de escalar con
    mucho tráfico
    • Escrituras en cada
    request
    • Replication lag
    Monday, June 24, 13

    View full-size slide

  61. • In-memory sessions
    • Tiempo de acceso
    constante y predecible
    • Escala horizontalmente
    Monday, June 24, 13

    View full-size slide

  62. Monday, June 24, 13

    View full-size slide

  63. 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

    View full-size slide

  64. 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

    View full-size slide

  65. 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

    View full-size slide

  66. 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

    View full-size slide

  67. 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

    View full-size slide

  68. redis 127.0.0.1:6379> MONITOR
    OK
    "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

    View full-size slide

  69. Monolog logging
    Monday, June 24, 13

    View full-size slide

  70. 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

    View full-size slide

  71. Monolog config
    snc_redis:
    clients:
    monolog:
    type: predis
    alias: monolog
    dsn: redis://localhost/1
    logging: false
    monolog:
    client: monolog
    key: monolog
    monolog:
    handlers:
    main:
    type: service
    id: monolog.handler.redis
    level: debug
    Monday, June 24, 13

    View full-size slide

  72. Referencias
    • http://blog.lusis.org/blog/2012/01/31/load-
    balancing-logstash-with-redis/
    Monday, June 24, 13

    View full-size slide

  73. SwiftMailer Spooling
    Monday, June 24, 13

    View full-size slide

  74. 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

    View full-size slide

  75. 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

    View full-size slide

  76. Otros casos de uso en
    Symfony2
    Monday, June 24, 13

    View full-size slide

  77. 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

    View full-size slide

  78. Router dinámicos
    Idioma
    Ruta
    “amigable”
    _controller
    es /futbol
    Bundle:SportController:sportPageAction,
    array(‘sport’ => 123)
    es /madrid
    Bundle:CityController:cityPageAction
    array(‘city’ => 456)
    Monday, June 24, 13

    View full-size slide

  79. 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

    View full-size slide

  80. 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

    View full-size slide

  81. Flujo
    Request
    /futbol
    Request
    /sport/123
    RouterMapperListener
    Controller/Action
    Routing System
    Monday, June 24, 13

    View full-size slide

  82. Monday, June 24, 13

    View full-size slide

  83. 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

    View full-size slide

  84. Integración con el profiler
    y web debug toolbar
    Monday, June 24, 13

    View full-size slide

  85. Integración en el profiler
    Monday, June 24, 13

    View full-size slide

  86. OTROS CASOS DE USO
    GENERALES
    Monday, June 24, 13

    View full-size slide

  87. Presencia de usuarios
    (who is online?)
    Monday, June 24, 13

    View full-size slide

  88. ¿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

    View full-size slide

  89. Monday, June 24, 13

    View full-size slide

  90. 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

    View full-size slide

  91. Usuarios online
    Amigos
    Amigos
    Online
    Monday, June 24, 13

    View full-size slide

  92. 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

    View full-size slide

  93. 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

    View full-size slide

  94. Monday, June 24, 13

    View full-size slide

  95. 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

    View full-size slide

  96. 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

    View full-size slide

  97. 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

    View full-size slide

  98. CONCLUSIONES
    Monday, June 24, 13

    View full-size slide

  99. 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

    View full-size slide

  100. 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

    View full-size slide

  101. 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

    View full-size slide

  102. Monday, June 24, 13

    View full-size slide

  103. Referencias
    http://redis.io/commands
    http://redis.io/documentation
    Monday, June 24, 13

    View full-size slide

  104. Muchas Gracias
    https://joind.in/8844
    @ronnylt
    https://github.com/ronnylt
    Monday, June 24, 13

    View full-size slide

  105. We are hiring!
    Monday, June 24, 13

    View full-size slide