Ni GA ni YM. Cómo hicimos nuestro propio clickstream

Recopilamos más de dos mil millones de eventos analíticos por día. Gracias a esto, podemos descubrir un montón de cosas necesarias: si hacen clic en los corazones más que en las estrellas, a qué horas escriben descripciones más detalladas, en qué regiones a menudo pierden los botones verdes.


El sistema de recopilación y análisis de eventos se puede llamar genéricamente clickstream. Le contaré sobre el aspecto técnico de la secuencia de clics en Avito: la organización de eventos, su envío y entrega, análisis, informes. ¿Por qué quieres el tuyo, si hay Google Analytics y Yandex.Metrica, a quienes los desarrolladores de clickstream arruinan la vida y por qué los codificadores no pueden olvidar el php?



Acerca de mi


Dmitry Khasanov, diez años en desarrollo web, tres años en Avito. Trabajo en un equipo de plataforma, desarrollo herramientas de infraestructura comunes. Amo los hackathons .


Desafío


El negocio requiere una comprensión profunda de los procesos que tienen lugar en el sitio. Por ejemplo, al registrar un usuario, quiero saber desde qué región, desde qué dispositivo y a través de qué navegador ha iniciado sesión el usuario. Cómo se rellenan los campos del formulario, si se envió o si el usuario se dio por vencido. Y si te rendiste, a qué paso. Y cuanto tiempo tomó.


Me gustaría saber si harán clic en el botón con más frecuencia si se vuelve a pintar en verde. ¿Los usuarios de aplicaciones móviles o del sitio presionarán el botón verde con más frecuencia en Murmansk o Vladivostok, de día o de noche? usuarios que vinieron del principal o de la búsqueda; quien compró antes en Avito o quien vino por primera vez.


Todos estos signos: sistema operativo, ID de usuario, hora de solicitud, dispositivo, navegador, valores en los campos, deben estar disponibles para su análisis. Recopilar, estructurar, dar acceso rápido a los datos.


Además, a menudo se requiere dividir el flujo de eventos. Los proyectos deben tomar medidas cuando ocurren ciertos eventos. Por ejemplo, de esta manera, se obtiene retroalimentación para reentrenar el modelo para el reconocimiento de patrones y la moderación automática, y se compilan estadísticas en tiempo real.


Usando clickstream como producto, debería ser fácil para los programadores enviar eventos desde un proyecto, y para los analistas gestionar eventos recopilados y crear una variedad de informes que muestran tendencias e hipótesis de apoyo.


Informes basados ​​en el flujo de eventos.
Ejemplo 1


Ejemplo 2


Herramientas acabadas


Conocemos Yandex Metric y Google Analytics, lo usamos para algunas tareas. Con su ayuda, es bueno y rápido recopilar datos analíticos de los front-end. Pero para exportar datos de backends a sistemas analíticos externos, debe realizar integraciones complejas.


Con herramientas externas, debe resolver de forma independiente el problema de dividir el flujo de eventos.


La información analítica es muy valiosa. Lo hemos estado recolectando durante años, nos permite saber con gran detalle cómo se comportan nuestros usuarios. No quiero compartir ese conocimiento con el mundo exterior.


La legislación obliga a almacenar datos en el territorio de Rusia.


Estas razones fueron suficientes para desarrollar nuestra propia solución como la herramienta principal para recopilar y procesar datos analíticos.


Solución


Los eventos se envían a través del transporte de alto rendimiento (Event Streaming Processing, ESP) en el almacenamiento (Data Warehouse, DWH). Sobre la base de los datos en el repositorio, se crean informes analíticos.


El evento


Entidad central. En sí mismo, significa hecho. Algo concreto sucedió en la unidad de tiempo designada.


Es necesario distinguir un evento de otro. Este es el identificador único del evento.


También interesado en el momento de ocurrencia de eventos. Lo transmitimos en cada evento con una precisión de microsegundos. En los eventos que llegan desde las interfaces, también fijamos el tiempo en el dispositivo del cliente para restaurar con mayor precisión la secuencia de acciones.


El campo


Un evento consta de campos. El campo es la unidad semántica más pequeña del sistema analítico. En el párrafo anterior hay ejemplos de campos: identificador de evento, hora de envío.


Atributos de campo: tipo (cadena, número, matriz), obligatorio.


El medio ambiente


El mismo evento puede ocurrir en diferentes partes del sistema: por ejemplo, la autorización es posible en el sitio o en una aplicación móvil. En este caso, enviamos el mismo evento, pero siempre agregamos el identificador único del origen del evento dentro.


Las fuentes son notablemente diferentes entre sí. Pueden ser demonios internos y coronas, un servicio frontend o backend, una aplicación móvil. Parte de los campos deben enviarse con cada evento de una fuente en particular.


Existe el concepto de "medio ambiente". Esta es una agrupación lógica de eventos por origen con la capacidad de establecer campos comunes para todos los eventos de origen.


Ejemplos de entornos: "back-end del servicio A", "interfaz del servicio A", "ios-aplicación del servicio A".


Directorio de eventos


Todos los eventos existentes se describen en un directorio que los desarrolladores y analistas pueden editar. Los eventos se agrupan lógicamente por entorno, cada evento tiene un propietario, se mantiene un registro de cambios en el directorio.


Por el momento, el directorio describe varios cientos de campos, varias decenas de entornos y más de mil eventos.


Langpack


Rechazamos la tortura y ya no obligamos a los desarrolladores a escribir manualmente el código de envío de eventos. En cambio, según el directorio, generamos un conjunto de archivos para cada uno de los idiomas de servidor admitidos por la compañía: php, go o python. Tal código generado se llama "langpack".


Los archivos en el langpack son lo más simples posible, no conocen la lógica empresarial de los proyectos. Este es un conjunto de captadores y establecedores de campo para cada uno de los eventos y un código para enviar el evento.


Se crea un langpack para cada entorno. Se descompone en un repositorio de paquetes (satis para php, pypi para python). Se actualiza automáticamente cuando se realizan cambios en el directorio.


No puedes dejar de escribir en PHP. El código para el servicio que genera los langpacks está escrito en Go. La compañía tiene suficientes proyectos PHP, así que tuve que recordar mi lenguaje de programación favorito de tres letras y generar código PHP en Go. Si se deja llevar un poco, también puede generar pruebas para probar el código generado con estas pruebas.


Versionado


La referencia puede ser editada. El código en la batalla no puede ser roto. Generamos el código de combate basado en el directorio. Peligrosamente


Después de cada cambio del evento, se crea una nueva versión en el directorio. Todas las versiones de eventos creadas viven para siempre en el directorio. Entonces resolvemos el problema de la inmutabilidad de eventos específicos. Los proyectos siempre indican con qué versión del evento estamos trabajando.


Si el código de langpack cambia (por ejemplo, solo había setters, pero ahora también decidimos agregar getters), cree una nueva versión de langpack. Ella también vivirá para siempre. Los proyectos siempre solicitan una versión específica del langpack para su entorno. Entonces resolvemos el problema de la invariancia de la interfaz langpack.


Usamos semver. La versión de cada langpack consta de tres números. El primero es siempre cero, el segundo es la versión del código langpack, el tercero es el incremento. El tercer dígito cambia con mayor frecuencia, después de cada cambio de eventos.


El control de versiones de dos niveles te permite editar el directorio sin romper el código en la batalla. Se basa en dos principios: no puede eliminar nada; No puede editar entidades creadas, solo cree copias modificadas una al lado de la otra.


Transporte


A diferencia de los chicos de Badoo en LSD , nunca aprendimos a escribir archivos maravillosamente . Y creemos que NSQ no es solo un servidor de colas , sino también un transporte para eventos.


Ocultaron NSQ detrás de una pequeña capa de código go, distribuyeron los recopiladores para cada nodo en el clúster de Kubernetes utilizando conjuntos de demonios y escribieron consumidores que pueden agregar eventos a diferentes fuentes.


Por el momento, el transporte ofrece alrededor de dos mil millones de eventos por día. Bajo tal carga, treinta coleccionistas trabajan con cierto margen. Cada uno consume un poco más de núcleo de procesador y un poco más de un gigabyte de memoria.


Enrutamiento de eventos


Los remitentes de eventos pueden ser proyectos que viven dentro o fuera de nuestro clúster. Dentro de los clústeres, estos son backends de servicio, coronas, daemons, proyectos de infraestructura y una intranet. Afuera, los eventos provienen de los frentes de proyectos públicos, de aplicaciones móviles y proyectos de socios.


Para recibir eventos fuera del clúster, utilizamos un proxy. Un punto de entrada común con un pequeño filtrado del flujo de eventos, con la posibilidad de su enriquecimiento. Envío posterior al transporte según el esquema general.


Esquema de enrutamiento general: cada evento puede tener un conjunto de destinatarios. Los posibles destinatarios incluyen un repositorio analítico compartido (DWH), rebbits o proyectos monga interesados ​​en ciertos eventos. El último caso, por ejemplo, se usa para reentrenar los modelos de moderación automática de anuncios. Las modelos escuchan ciertos eventos y obtienen los comentarios necesarios.


Del lado de los proyectos no hay conocimiento sobre enrutamiento. Envían eventos utilizando langpacks en los que se cosen las direcciones de los coleccionistas comunes.


Almacenamiento


El repositorio principal de eventos es el HP Vertica, unas pocas docenas de terabytes. Una base de columna con características adecuadas para nuestros analistas. Interfaz: Tableau para informes.


Es más eficiente registrar eventos en nuestro almacenamiento en grandes lotes. Delante del almacenamiento hay un búfer en forma de Mongo. Colecciones de eliminación automática creadas automáticamente para cada hora. Las colecciones se almacenan durante varios días para poder reiniciar la corrección de pruebas en Vertica si algo sale mal.


Lectura del buffer Mongo en scripts de mascotas. Los guiones están guiados por una referencia, tratamos de no mantener la lógica de negocios aquí. En esta etapa, el enriquecimiento de eventos es posible.


Evolución


Mano bailando en la oscuridad


La necesidad de registrar eventos surgió mucho antes que la conciencia de la necesidad de mantener un directorio. Los desarrolladores de cada uno de los proyectos idearon una forma de enviar eventos en busca de transporte. Esto generó una gran cantidad de código en diferentes idiomas, en proyectos diferentes, pero resolviendo un problema.


A menudo, dentro del código de envío de eventos, los bits de la lógica empresarial perduraron. El código con este conocimiento no se puede portar a otros proyectos. Al refactorizar, la lógica empresarial debe devolverse al proyecto, dejando en el código del evento solo el cumplimiento del formato de datos especificado.


En esta etapa, no había un directorio de eventos. Para comprender qué eventos ya se están registrando, qué campos tenían los eventos, solo fue posible mirando el código. Para saber que el desarrollador accidentalmente dejó de escribir datos en el campo requerido, fue posible al crear el informe, si presta atención específicamente a esto.


No hubo muchos eventos. Se agregaron colecciones de amortiguadores en mongos según sea necesario. A medida que crecía el número de eventos, era necesario redirigir manualmente los eventos a otras colecciones, para crear las colecciones necesarias. La decisión de colocar el evento en una colección de búfer particular se tomó al momento del envío, en el lado del proyecto. El transporte era fluido, el cliente era td-agent.


Consciente asincrónico


Se decidió crear un directorio de todos los eventos existentes. Analizamos el código de los backends, sacamos parte de la información de allí. Obligamos a los desarrolladores a tener esto en cuenta en el directorio con cada cambio en el código del evento.


Los eventos que llegan desde las interfaces y las aplicaciones móviles se describieron manualmente, a veces capturando la información necesaria del flujo de eventos a nivel de transporte.


Los desarrolladores saben cómo olvidar. Esto condujo a una desincronización del directorio y el código, pero el directorio mostró la imagen general.


El número de colecciones de búfer ha crecido significativamente, el trabajo manual para mantenerlas ha aumentado significativamente. Una persona irremplazable apareció con un montón de conocimiento secreto sobre el almacenamiento del búfer.


Nuevo transporte


Crearon un transporte compartido, ESP, consciente de todos los puntos de entrega de eventos. Lo convirtieron en un único punto de recepción. Esto hizo posible controlar todos los flujos de eventos. Los proyectos dejaron de acceder directamente al almacenamiento intermedio.


Clickstreamism iluminado


Según el directorio, se generaron langpacks. No permiten la creación de eventos inválidos.


Se introdujeron comprobaciones automáticas para la exactitud de los eventos que llegan desde aplicaciones frontales y móviles. En este caso, no dejamos de escribir eventos para no perder datos, sino que registramos los errores y los enviamos a los desarrolladores.


Los eventos raros en backends que son difíciles de refactorizar y que aún no se envían a través de langpacks son validados por una biblioteca separada de acuerdo con las reglas del directorio. En caso de errores, inicie una excepción que bloquee la implementación.


Tengo un sistema que tiende a coincidir con el directorio. Bonificaciones: transparencia, manejabilidad, velocidad de creación y cambio de eventos.


Epílogo


Las principales dificultades y lecciones fueron de organización. Es difícil vincular iniciativas que involucren a varios equipos. No es fácil cambiar el código de un gran proyecto antiguo. Las habilidades de comunicarse con otros equipos, dividiendo las tareas en una integración relativamente independiente y pensada con la posibilidad de una ayuda de despliegue independiente. Los desarrolladores de Clickstream ya no aman a los equipos de productos cuando comienza la fase de integración de una nueva solución. Si las interfaces cambian, el trabajo se agrega a todos.


Crear un directorio fue una muy buena idea. Se convirtió en la única fuente de verdad, siempre se puede recurrir a él en caso de discrepancias en el código. Una gran cantidad de automatización está vinculada al directorio: verificaciones, enrutamiento de eventos, generación de código.


La infraestructura no necesita saber acerca de la lógica empresarial. Señales del surgimiento de la lógica empresarial: los eventos cambian en el camino desde el proyecto hasta el repositorio; Cambiar el transporte sin cambiar los proyectos se vuelve imposible. En el lado de la infraestructura, debe haber conocimiento sobre la composición de los eventos, los tipos de campos y su obligatoriedad. Del lado del producto, el significado lógico de estos campos.


Siempre hay espacio para crecer. Técnicamente, esto es un aumento en el número de eventos, una disminución en el tiempo desde la creación del evento hasta el inicio del registro de datos, la eliminación del trabajo manual en todas las etapas.


Hay un par de ideas audaces. Obtener un gráfico detallado de las transiciones de los usuarios, configurar eventos sobre la marcha sin implementar el servicio. Pero más sobre eso en los siguientes artículos.


PD: Hablé sobre este tema en la reunión Backend United # 1. Vinagreta Puede ver
presentación o video de la reunión.

Source: https://habr.com/ru/post/es419651/


All Articles