Ni GA ni YM. Comment nous avons créé notre propre flux de clics

Nous collectons plus de deux milliards d'événements analytiques par jour. Grâce à cela, nous pouvons découvrir un tas de choses nécessaires: s'ils cliquent plus sur les coeurs que sur les étoiles, à quelles heures ils écrivent des descriptions plus détaillées, dans quelles régions ils manquent souvent les boutons verts.


Le système de collecte et d'analyse d'événements peut être appelé génériquement clickstream. Je vais vous parler du côté technique du flux de clics dans Avito: l'organisation des événements, leur envoi et leur livraison, l'analyse, les rapports. Pourquoi voulez-vous le vôtre, s'il y a Google Analytics et Yandex.Metrica, que les développeurs de clickstream gâchent la vie et pourquoi les go-coders ne peuvent pas oublier php.



À propos de moi


Dmitry Khasanov, dix ans dans le développement web, trois ans chez Avito. Je travaille dans une équipe de plateforme, développe des outils d'infrastructure communs. J'adore les hackathons .


Défi


Les affaires nécessitent une compréhension approfondie des processus en cours sur le site. Par exemple, lors de l'enregistrement d'un utilisateur, je veux savoir de quelle région, de quel appareil et par quel navigateur l'utilisateur s'est connecté. Comment les champs du formulaire sont remplis, qu'ils aient été soumis ou que l'utilisateur ait abandonné. Et si vous abandonnez, à quelle étape. Et combien de temps cela a pris.


Je voudrais savoir s'ils cliqueront plus souvent sur le bouton s'il est repeint en vert. Le bouton vert sera-t-il pressé plus souvent à Mourmansk ou à Vladivostok, de jour comme de nuit, par les utilisateurs d'applications mobiles ou du site; les utilisateurs issus de la recherche principale ou de la recherche; qui ont déjà acheté sur Avito ou qui sont venus pour la première fois.


Tous ces signes: système d'exploitation, ID utilisateur, heure de la demande, appareil, navigateur, valeurs dans les champs - doivent être mis à disposition pour analyse. Collectez, structurez, donnez un accès rapide aux données.


De plus, il est souvent nécessaire de diviser le flux d'événements. Les projets doivent prendre des mesures lorsque certains événements se produisent. Par exemple, de cette manière, une rétroaction est obtenue pour recycler le modèle pour la reconnaissance de formes et l'auto-modération, et des statistiques en temps réel sont compilées.


Avec l'aide de clickstream en tant que produit, il devrait être facile pour les programmeurs d'envoyer des événements à partir d'un projet, et pour les analystes de gérer les événements collectés et de créer une variété de rapports montrant les tendances et les hypothèses à l'appui.


Rapports basés sur le flux d'événements.
Exemple 1


Exemple 2


Outils finis


Nous connaissons Yandex Metric et Google Analytics, nous l'utilisons pour certaines tâches. Avec leur aide, il est bon et rapide de collecter des données analytiques auprès des frontaux. Mais pour exporter des données de backends vers des systèmes analytiques externes, vous devez effectuer des intégrations délicates.


Avec des outils externes, vous devez résoudre indépendamment le problème de la division du flux d'événements.


Les informations analytiques sont très précieuses. Nous le collectons depuis des années, il nous permet de connaître en détail le comportement de nos utilisateurs. Je ne veux pas partager ces connaissances avec le monde extérieur.


La législation oblige à stocker des données sur le territoire de la Russie.


Ces raisons ont été suffisantes pour développer notre propre solution comme principal outil de collecte et de traitement des données analytiques.


Solution


Les événements sont distribués via un transport haute performance (Event Streaming Processing, ESP) dans le stockage (Data Warehouse, DWH). Sur la base des données du référentiel, des rapports analytiques sont créés.


Événement


Entité centrale. En soi, cela signifie un fait. Quelque chose de concret s'est produit dans l'unité de temps désignée.


Il est nécessaire de distinguer un événement d'un autre. Il s'agit de l'identifiant unique de l'événement.


Intéressé également par le moment de l'événement. Nous le transmettons dans chaque événement avec une précision de microsecondes. Dans les événements arrivant des frontends, nous fixons également l'heure sur le périphérique client afin de restaurer plus précisément la séquence d'actions.


Le terrain


Un événement se compose de champs. Le champ est la plus petite unité sémantique du système analytique. Dans le paragraphe précédent, il existe des exemples de champs: identifiant d'événement, heure d'envoi.


Attributs de champ: type (chaîne, nombre, tableau), obligatoire.


L'environnement


Le même événement peut se produire dans différentes parties du système: par exemple, l'autorisation est possible sur le site ou dans une application mobile. Dans ce cas, nous envoyons le même événement, mais ajoutons toujours l'identifiant unique de la source de l'événement à l'intérieur.


Les sources sont sensiblement différentes les unes des autres. Il peut s'agir de démons et de couronnes internes, d'un service frontend ou backend, d'une application mobile. Une partie des champs doit être envoyée avec chaque événement d'une source particulière.


Il y a le concept «d'environnement». Il s'agit d'un regroupement logique d'événements par source avec la possibilité de définir des champs communs pour tous les événements source.


Exemples d'environnements: «backend of service A», «frontend of service A», «ios-application of service A».


Annuaire des événements


Tous les événements existants sont décrits dans un répertoire que les développeurs et les analystes peuvent modifier. Les événements sont logiquement regroupés par environnement, chaque événement a un propriétaire, un journal des modifications dans le répertoire est conservé.


À l'heure actuelle, l'annuaire décrit plusieurs centaines de champs, plusieurs dizaines d'environnements et plus d'un millier d'événements.


Langpack


Nous avons refusé la torture et nous ne forçons plus les développeurs à écrire manuellement le code de répartition des événements. Au lieu de cela, en fonction du répertoire, nous générons un ensemble de fichiers pour chacune des langues de serveur prises en charge par l'entreprise: php, go ou python. Un tel code généré est appelé «langpack».


Les fichiers du langpack sont aussi simples que possible, ils ne connaissent pas la logique métier des projets. Il s'agit d'un ensemble de getters et de setters de champ pour chacun des événements et d'un code pour envoyer l'événement.


Un langpack est créé pour chaque environnement. Il se décompose en un référentiel de packages (satis pour php, pypi pour python). Il est mis à jour automatiquement lorsque des modifications sont apportées au répertoire.


Vous ne pouvez pas arrêter d'écrire en PHP. Le code du service générant les langpacks est écrit en Go. La société a suffisamment de projets PHP, j'ai donc dû me souvenir de mon langage de programmation à trois lettres préféré et générer du code PHP sur Go. Si vous vous laissez un peu emporter, vous pouvez également générer des tests pour tester le code généré avec ces tests.


Versioning


La référence peut être modifiée. Le code de la bataille ne peut pas être rompu. Nous générons le code de combat basé sur le répertoire. Dangereusement.


Après chaque modification de l'événement, une nouvelle version est créée dans le répertoire. Toutes les versions d'événements créées ont toujours vécu dans le répertoire. Nous résolvons donc le problème de l'immuabilité d'événements spécifiques. Les projets indiquent toujours avec quelle version de l'événement nous travaillons.


Si le code langpack change (par exemple, il n'y avait que des setters, mais maintenant nous avons également décidé d'ajouter des getters), créez une nouvelle version du langpack. Elle vivra également pour toujours. Les projets demandent toujours une version spécifique du langpack pour leur environnement. Nous résolvons donc le problème d'invariance de l'interface langpack.


Nous utilisons semver. La version de chaque langpack se compose de trois nombres. Le premier est toujours zéro, le second est la version du code langpack, le troisième est l'incrément. Le troisième chiffre change le plus souvent, après chaque changement d'événement.


Le contrôle de version à deux niveaux vous permet de modifier le répertoire sans casser le code au combat. Il repose sur deux principes: vous ne pouvez rien supprimer; Vous ne pouvez pas modifier les entités créées, créez simplement des copies modifiées côte à côte.


Le transport


Contrairement aux gars de Badoo sur LSD , nous n'avons jamais appris à écrire des fichiers magnifiquement . Et nous pensons que NSQ n'est pas seulement un serveur de file d'attente , mais aussi un transport d'événements.


Ils ont caché NSQ derrière une petite couche de code go, ont disposé des collecteurs pour chaque nœud du cluster Kubernetes à l'aide de jeux de démons et ont écrit des consuméristes qui peuvent ajouter des événements à différentes sources.


À l'heure actuelle, le transport fournit environ deux milliards d'événements par jour. Sous une telle charge, trente collectionneurs travaillent avec une certaine marge. Chacun consomme un peu plus de cœur de processeur et un peu plus d'un gigaoctet de mémoire.


Routage d'événement


Les expéditeurs d'événements peuvent être des projets vivant à l'intérieur ou à l'extérieur de notre cluster. À l'intérieur des clusters, il s'agit des backends de service, des couronnes, des démons, des projets d'infrastructure et d'un intranet. A l'extérieur, les événements proviennent des frontends des projets publics, des applications mobiles et des projets partenaires.


Pour recevoir des événements en dehors du cluster, nous utilisons un proxy. Un point d'entrée commun avec un petit filtrage du flux des événements, avec la possibilité de leur enrichissement. Envoi ultérieur au transport selon le schéma général.


Schéma de routage général: chaque événement peut avoir un ensemble de destinataires. Les destinataires possibles incluent un référentiel analytique partagé (DWH), des rebbits ou des projets monga intéressés par certains événements. Ce dernier cas, par exemple, est utilisé pour recycler les modèles d'auto-modération des annonces. Les mannequins écoutent certains événements et obtiennent les commentaires nécessaires.


Du côté des projets, il n'y a aucune connaissance sur le routage. Ils envoient des événements à l'aide de langpacks dans lesquels les adresses des collectionneurs communs sont cousues.


Stockage


Le principal référentiel d'événements est le HP Vertica, quelques dizaines de téraoctets. Une base de colonne avec des fonctionnalités adaptées à nos analystes. Interface - Tableau pour les rapports.


Il est plus efficace d'enregistrer des événements dans notre stockage en lots importants. Devant le stockage se trouve un tampon en forme de Mongo. Collections de suppression automatique créées automatiquement pour chaque heure. Les collections sont stockées pendant plusieurs jours afin de pouvoir redémarrer la relecture dans Vertica en cas de problème.


Lecture du tampon Mongo sur les scripts pour animaux de compagnie. Les scripts sont guidés par une référence, nous essayons de ne pas garder la logique métier ici. À ce stade, l'enrichissement de l'événement est possible.


Évolution


Main qui danse dans le noir


La nécessité de consigner les événements est apparue bien avant la prise de conscience de la nécessité de gérer un répertoire. Les développeurs de chacun des projets ont trouvé un moyen d'envoyer des événements à la recherche de moyens de transport. Cela a généré beaucoup de code dans différents langages, se trouvant dans différents projets, mais résolvant un problème.


Souvent à l'intérieur du code de répartition des événements, des bits de logique métier ont survécu. Le code avec cette connaissance ne peut pas être porté à d'autres projets. Lors de la refactorisation, la logique métier doit être renvoyée au projet, ne laissant dans le code d'événement que la conformité au format de données spécifié.


À ce stade, il n'y avait pas de répertoire d'événements. Pour comprendre quels événements sont déjà enregistrés, quels champs avaient les événements, cela n'a été possible qu'en consultant le code. Pour apprendre que le développeur a accidentellement cessé d'écrire des données dans le champ requis, cela a été possible lors de la création du rapport, si vous y faites particulièrement attention.


Il n'y a pas eu beaucoup d'événements. Des collections de tampons dans les mongos ont été ajoutées au besoin. À mesure que le nombre d'événements augmentait, il était nécessaire de rediriger manuellement les événements vers d'autres collections pour créer les collections nécessaires. La décision de placer l'événement dans une collection tampon particulière a été prise au moment de l'envoi, côté projet. Le transport était fluide, le client était td-agent.


Conscient asynchrone


Il a été décidé de créer un répertoire de tous les événements existants. Nous avons analysé le code des backends, extrait certaines informations de là. Nous avons obligé les développeurs à noter cela dans le répertoire à chaque changement dans le code de l'événement.


Les événements arrivant des interfaces et des applications mobiles ont été décrits manuellement, captant parfois les informations nécessaires dans le flux d'événements au niveau du transport.


Les développeurs savent oublier. Cela a conduit à une désynchronisation du répertoire et du code, mais le répertoire a montré l'image générale.


Le nombre de collections de tampons a considérablement augmenté, le travail manuel pour les maintenir a considérablement augmenté. Une personne irremplaçable est apparue avec un tas de connaissances secrètes sur le stockage tampon.


Nouveau transport


Ils ont créé un transport partagé, ESP, au courant de tous les points de livraison de l'événement. Ils en ont fait un point d'accueil unique. Cela a permis de contrôler tous les flux d'événements. Les projets ont directement cessé d'accéder au stockage tampon.


Clickstreamism éclairé


Sur la base du répertoire, des langpacks ont été générés. Ils ne permettent pas la création d'événements non valides.


Introduction de vérifications automatiques de l'exactitude des événements provenant des frontaux et des applications mobiles. Dans ce cas, nous n'arrêtons pas d'écrire des événements pour ne pas perdre de données, mais nous enregistrons les erreurs et signalons aux développeurs.


Les événements rares sur les backends qui sont difficiles à refactoriser et qui ne sont toujours pas envoyés via les langpacks sont validés par une bibliothèque distincte selon les règles du répertoire. En cas d'erreur, lève une exception qui bloque le déploiement.


Vous avez un système tendant à correspondre au répertoire. Bonus: transparence, gérabilité, rapidité de création et changement d'événements.


Postface


Les principales difficultés et enseignements ont été d'ordre organisationnel. Il est difficile de lier des initiatives impliquant plusieurs équipes. Il n'est pas facile de changer le code d'un grand projet ancien. Aptitude à communiquer avec les autres équipes, à diviser les tâches en une intégration relativement indépendante et réfléchie avec la possibilité de déployer de l'aide de manière indépendante. Les développeurs Clickstream n'aiment plus les équipes de produits lorsque la phase d'intégration d'une nouvelle solution commence. Si les interfaces changent, le travail est ajouté à tout le monde.


La création d'un répertoire était une très bonne idée. Il est devenu la seule source de vérité, vous pouvez toujours faire appel à lui en cas de divergences dans le code. Une grande partie de l'automatisation est liée au répertoire: vérifications, routage d'événements, génération de code.


L'infrastructure n'a pas besoin de connaître la logique métier. Signes de l'émergence d'une logique métier: les événements évoluent en cours de route du projet au référentiel; changer de transport sans changer de projet devient impossible. Du côté des infrastructures, il faut connaître la composition des événements, les types de champs, leur caractère obligatoire. Côté produit, la signification logique de ces champs.


Il y a toujours de la place pour grandir. Techniquement, il s'agit d'une augmentation du nombre d'événements, d'une diminution du temps entre la création de l'événement et le début de l'enregistrement des données, la suppression du travail manuel à toutes les étapes.


Il y a quelques idées audacieuses. Obtenir un graphique détaillé des transitions des utilisateurs, configurer les événements à la volée sans déployer le service. Mais plus à ce sujet dans les articles suivants.


PS J'ai parlé sur ce sujet lors de la réunion Backend United # 1. Vinaigrette. Peut voir
présentation ou vidéo de la réunion.

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


All Articles