Post on 21-Jun-2015
CÓMO CONSTRUIR UNA PLATAFORMA DE LIBRO
ELECTRÓNICO Y
NO MORIR EN EL INTENTO
Los speakers
Alberto Vilches • Programando desde 1998
• 5 años de experiencia con Grails
• Soy el que organiza todo esto
• TwiIer: @albertovilches
Roberto Mar=n • Programador desde 2008
• 4 años de experiencia con Grails
• Miembro de plataforma Java en BQ
• TwiIer: @roberto_mf
De que vamos a hablar
10% Negocio
20% Experiencia 30% Arquitectura 40% Grails (con algo de código!)
Donde trabajamos
Donde trabajamos
Mundoreader – BQ Readers • Empresa 100% española (sedes en todo el mundo)
• 2º fabricante y vendedor de tablets en España (después de Apple)
• Primera plataforma completa de libro electrónico.
• Y otros negocios: memorias USB, accesorios, etc.
Negocio
Negocio principal: venta de disposiNvos (tablets, ereaders)
Siguiente objebvo: venta de contenidos
Beneficios €
Inversión
Negocio
Venta de contenido
• Diseño, creación y distribución de E-‐readers
• Acuerdos con editoriales
• Creación de plataforma de venta de libros
Modelo de negocio
1. BQReaders NO es una benda de libros
2. Los libros los venden las Nendas grandes librerías y superficies / pequeños comercios no podemos decir quienes…
3. Lo hacen a través de nuestra plataforma
4. Y nos llevamos comisión por libro vendido -‐> !!!
5. ¿Tienes una benda? ¿Eres una editorial? Habla con nosotros!
Modelo de negocio
1. Las bendas usan nuestra plataforma para crear una web y vender libros
aunque a veces se la hacemos también nosotros… !
2. Las bendas venden E-‐readers a sus usuarios
3. Lo hacen con su marca, pero nos los compran a nosotros… !
4. Vendemos la solución completa
Modelo de negocio
Y más cosas
1. Suscripción Esblo Spobfy: cuota mensual y barra libre
2. Bibliotecas Préstamos, adquisición de licencias
3. Aplicaciones en IOS y Android
Cómo funciona
Tienda WEB
App nabva IOS
App nabva Android
Compra y lee libros
Consumen Servicios (muchos!)
Plataforma contenidos
E-‐Reader
Plataforma de contenidos
Tecnologías usadas
• Grails 3 wars: services, content, backoffice
• MySql 2 esquemas en 2 máquinas disbntas
1. SERVICES (usuarios, compras, disposibvos) 2. CONTENT (libros, editoriales, categorías, catálogos)
Plataforma de contenidos
Tecnologías usadas
• ElasbcSearch • Búsqueda de libros, facetas • Evita acceder a la base de datos • Si se cae, usamos la base de datos (pero no tenemos facetas)
• Mahout • Recomendaciones de libros en función de las compras y visitas
• MongoDB • Auditoría y tablas maestras (paises-‐ips, reports, )
Plataforma de contenidos
Componentes
• API para bendas: JSON 100% (rest 90%) Listados de libros, búsquedas, categorías, compras
• API para Ereader, IOS y Android Sincronizar librería, descargar libro, anotaciones, bookmarks
• Carga de contenidos Proceso nocturno desde editoriales (tp, epubs, pdf, onix, carátulas)
• Backoffice Administración de libros, categorías, catálogos, reports, liquidaciones
Problemas y…
“nuestras” soluciones
Plataforma de contenidos
API En caso de error: • Debe seguir devolviendo JSON
• Si es un error incontrolado, debe nobficar del error • Log, auditoria, email
Plataforma de contenidos
API
Plataforma de contenidos
API
Plataforma de contenidos
Seguridad API
“No queremos usar login/token/sesión por cliente”
Plataforma de contenidos
Seguridad API
“No queremos usar login/token/sesión por cliente”
1. Las password son siempre débiles 2. El cliente no maneja sesión
3. El servidor tampoco (stateless, más fácil de replicar)
Solución: pebciones firmadas
Plataforma de contenidos
Seguridad API Firmar una pebción
• Se concatena la url y todos sus parámetros (get o post, todos)
• Se obbene su hash • Se encripta la hash con la clave privada = firma • Se envía la firma en una cabecera
hex( crypt( sha1( url+params, key ) ) )
• El servidor hace el paso inverso. Si los parámetros coinciden, ejecuta la operación.
Plataforma de contenidos
Seguridad API
Al estar basada en una hash, la firma cambiará cada vez que cambie la url y/o los parámetros.
Interceptar la firma no permite manipular la
pebción
¡Pero una pebción interceptada se puede replicar N veces!
Plataforma de contenidos
Seguridad API Evitar que se repliquen las pebciones Solución: nonce
• Cada pebción debe enviar un parámetro obligatorio llamado “nonce”.
• Su valor debe ser un número no usado antes (milesegundos?) • El servidor conserva cada nonce enviado y verifica que no se
repita. • Si se envía el mismo “nonce” se rechaza la pebción.
Algunos trucos
Trabajar con decimales
Algunos trucos
Trabajar con decimales • Los números decimales pierden precisión cuando se
transforman de binario a decimal
• No importa cuando trabajamos con muchos decimales (de hecho, es necesario): distancias, pesos
• Pero SI importa cuando trabajamos con precios!
Algunos trucos
Trabajar con decimales: solución
1. Clases de dominio: usar BigDecimal!
Algunos trucos
Trabajar con decimales: solución
1. Clases de dominio: usar BigDecimal!2. Base de datos. Precisión de n+1 para
productos 3 decimales para € -‐> DECIMAL(16, 3)
Algunos trucos
Trabajar con decimales: solución
1. Clases de dominio: usar BigDecimal!2. Base de datos. Precisión de n+1 para
productos 3 decimales para € -‐> DECIMAL(16, 3)
3. Base de datos. Precisión de n para compras 2 decimales para € -‐> DECIMAL(15, 2)
Algunos trucos
Trabajar con decimales: solución
1. Clases de dominio: usar BigDecimal!2. Base de datos. Precisión de n+1 para
productos 3 decimales para € -‐> DECIMAL(16, 3)
3. Base de datos. Precisión de n para compras 2 decimales para € -‐> DECIMAL(15, 2)
4. Evitar recalcular Al hacer una compra, guardar una “foto” con todos los valores ya
calculados.
Concurrencia
Típico problema de concurrencia
Concurrencia
Típico problema de concurrencia 1. Un usuario se conecta desde diferentes
clientes a la vez (ipad y web),…
2. … ejecuta dos operaciones que escriben en el mismo registro de la base de datos…
Concurrencia
Típico problema de concurrencia
Concurrencia
A) Cuando NO ES un registro importante:
Fecha de úlbma conexión/sincronización/actualización Actualizar una anotación de un libro Datos personales del usuario,…
Concurrencia
A) Cuando NO ES un registro importante: 1. Desacbvar opbmisbc locking 2. Acbvar dynamicUpdate (opcional)
Concurrencia
A) Cuando NO ES un registro importante: 1. Desacbvar opbmisbc locking 2. Acbvar dynamicUpdate (opcional)
UPDATE … WHERE ID = 2678 AND VERSION = 10!
UPDATE table SET campo_que_ha_cambiado WHERE ID = 2678!
Concurrencia
DynamicUpdate PITFALL!! LLLLLLL
Si se modifica un campo dentro de beforeInsert / beforeUpdate
¡No se escribirá! Bug?
Solución: Usar beforeValidate
Se ejecuta después de save(), al insertar o modificar
Concurrencia
B) Cuando ES un registro importante
Modificar el saldo de un usuario Devolver una compra
Consumir un cupón prepago
Concurrencia
B) Cuando ES un registro importante 1. Iniciar transacción
Concurrencia
B) Cuando ES un registro importante 1. Iniciar transacción 2. Bloquear el registro
Concurrencia
B) Cuando ES un registro importante 1. Iniciar transacción 2. Bloquear el registro
SELECT * FROM USER … FOR UPDATE!
Algunos trucos
Operaciones en background Escritura de logs, actualizar estadísbcas, envío de mensajes, colas…
Algunos trucos
Operaciones en background Escritura de logs, actualizar estadísbcas, envío de mensajes, colas…
1. Objebvo Finalizar la pebción lo antes posible
2. Que necesitamos: Crear un hilo con la sesión de Hibernate
3. Plugins: 1. BackgroundThread plugin 2. Executor 3. Quartz
Algunos trucos
Actualizaciones masivas
Si hay muchos registros (cientes de miles o millones) • Extremadamento lento • Puedo agotar el heap • No es eficiente
Algunos trucos
Actualizaciones masivas
• A veces se puede susbtuir… J
Algunos trucos
Actualizaciones masivas • Pero otras no se puede!
Cada registro requiere lógica de negocio L
Algunos trucos
Actualizaciones masivas
Solución • Hilo en background • Seleccionar solo lo necesario (projecbons) • Paginar (hacer de 1000 en 1000 elementos) • Limpiar la sesión de nivel de 1 de Hibernate • Introducir pausas si es necesario
Si falla todo lo demás • Crear un PLSQL y llamarlo on demand o por la noche
Algunos trucos
Actualizaciones masivas
Algunos trucos
Actualizaciones masivas
Projection
Algunos trucos
Actualizaciones masivas
Paginación
Algunos trucos
Actualizaciones masivas
Vaciar la sesión de Hibernate Pausa
Algunos trucos
Actualizaciones masivas
¿Que sucede si cada operación individual es muuuuuy lenta?
Cada operación una llamada a un servicio externo…
Algunos trucos
Actualizaciones masivas
¿Que sucede si cada operación individual es muuuuuy lenta?
SOLUCIÓN: Un pool de hilos
Algunos trucos
Actualizaciones masivas Configuración pool de hilos: plugin Executor
Algunos trucos
Actualizaciones masivas Configuración pool de hilos: plugin Executor
Número de hilos máximos a la vez
Algunos trucos
Actualizaciones masivas
Se obtienen primero SOLO los ids
Algunos trucos
Actualizaciones masivas
Operación lenta: Cargo € en tarjeta
Se encola la operación
Algunos trucos
Actualizaciones masivas
Operación lenta: Cargo € en tarjeta
Esperamos a que acaben todos los hilos
Se encola la operación
Algunos trucos
Actualizaciones masivas
Vaciamos la sesión de Hibernate y volvemos a empezar
Escalabilidad
Escalabilidad
Arquitectura
hIps://services.mundoreader.com
hIps://panel.mundoreader.com
Tiendas (web)
Tomcat Grails
MySql
Escalabilidad
Arquitectura
hIps://services.mundoreader.com
hIps://panel.mundoreader.com
Tiendas (web)
Tomcat Grails
Stateless!! JJJJ
MySql
Escalabilidad
Arquitectura
hIps://services.mundoreader.com
hIps://panel.mundoreader.com
Tiendas (web)
Tomcat Grails
Stateless!! JJJJ
Escalabilidad
Arquitectura
hIps://services.mundoreader.com
hIps://panel.mundoreader.com
Tiendas (web)
Tomcat Grails
Stateless!! JJJJ
Escalabilidad
Evitar trabajo innecesario -‐> Delegar en la bbdd. Evitar N+1 consultas. Peligro! Gorm!!
• Usar JOINs. Crear índices si es necesario.
• Inyectar propiedades (valores agrupados, etc)
• Desnormalizar
Escalabilidad
Inyectar propiedades
• En vez de:
Escalabilidad
Inyectar propiedades
• En vez de:
• Usar
Escalabilidad
Analizar el rendimiento de la bbdd
• OpenNMS • Script de análisis • MySql Slow Queries
show variables like 'slow_query_log';!set global slow_query_log='ON';!!/var/log/mysql/mysql-slow.log!!
Escalabilidad
Arquitectura
hIps://services.mundoreader.com
hIps://panel.mundoreader.com Tomcat Grails
Stateless!! JJJJ
Escalabilidad
Dividir el esquema en dos • 2 MySql fisicamente disbntas • Relaciones ligeras entre tablas de disbnta bbdd • 2 datasources por Tomcat • No JOINs! -‐> Desnormalizar
hIps://panel.mundoreader.com Tomcat Grails
MySql 1
MySql 2
Escalabilidad
Escalabilidad Dos datasources • Configuración
• Clases de dominio
Escalabilidad
Rendimiento DESNORMALIZAR • Duplicar datos de otras tablas • Evita joins -‐> más rápido • Perdida de consistencia • Uso de otros bpos de base de datos
• Libros -‐> ElasbcSearch • Auditoría -‐> MongoDb con Gorm
Escalabilidad
Rendimiento ElasbcSearch • Motor de búsqueda en texto plano • Basado en Lucene • Api JSON 100% • Alta escalabilidad: cluster, nodos, shards • Facetas
Escalabilidad
Rendimiento ElasbcSearch Problema: Indexar desde diferentes puntos de la aplicación • Al modificar un libro • Al modificar una categoría (actualizar todos sus libros)
• Al modificar un catálogo “ “ “ “ • Bucles, hilos de fondo, barras de progreso… LLL
Escalabilidad
Rendimiento ElasbcSearch Problema: Indexar desde diferentes puntos de la aplicación Solución: Demonio de indexación • Único punto de acceso al ES para escribir • Conbnuamente procesa libros encolados para
indexar • Para indexar un libro, solo hay que encolarlo
Escalabilidad
Rendimiento ElasbcSearch Problema: Indexar desde diferentes puntos de la aplicación Solución: Demonio de indexación • Al arrancar, si no hay nodo, lo crea y reindexa • Permite encolar reindexaciones completas • Reconstruir con alias (truco) • Índice por versión de app.
Escalabilidad
Rendimiento Cachear todo!!
• SpringCache • JCS • Implementaciones propias
MySql • Cluster Master/Slave • Servidores read only • Amazon RDS
Escalabilidad
Rendimiento Pruebas de carga: conoce tu límite • Antes y después de aplicar mejoras. Mide lo que has
ganado. Profile tus JVMs • YourKit Java Profiler: memory leaks, threads • Java Melody (no funciona con múlbples datasources) • Plugin BigDesk para ElasbcSearch
Monitorización
Crea tu propia página de información
Monitorización
Monitorizar: página con información
Monitorización
Crea tu propia página de información
Monitorización
Crea tu propia página de información <app>/scripts/_Events.groovy!
Monitorización
Crea tu propia página de información 1. Mostrar estado de
servicios 2. Chequeo de
servicios 3. Mostrar hosts/ips
Monitorización
Crea tu propia página de información 1. Mostrar estado de
servicios 2. Chequeo de
servicios 3. Mostrar hosts/ips
Monitorización
Crea tu propia página de información 1. Mostrar estado de
servicios 2. Chequeo de
servicios 3. Mostrar hosts/ips
Pase a producción
Build • Scripts de update SVN, war y upload a repo Test • SoapUI y manuales Config • Configuración externa en SVN y por versión Deploy • Script de descarga de war desde repo (por versión) Rollback • Dump de base datos. Deploy versión anterior. Config
se conserva entre versiones.
Pase a producción
Config Un directorio por versión:
¡Gracias!
¿Preguntas? (Tienen premio!)