Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Mikhail Salosin (ci-après - MS): - Salut tout le monde! Mon nom est Michael. Je travaille en tant que développeur back-end chez MC2 Software, et je parlerai de l'utilisation de Go dans le back-end de l'application mobile Look+.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Est-ce que quelqu'un ici aime le hockey?

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Alors cette application est pour vous. C'est pour Android et iOS, il est utilisé pour regarder les diffusions de divers événements sportifs en ligne et sur disque. L'application dispose également de diverses statistiques, de diffusions de texte, de tableaux de conférences, de tournois et d'autres informations utiles aux fans.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Dans l'application, il existe également des moments vidéo, c'est-à-dire que vous pouvez regarder les moments forts des matchs (buts, combats, fusillades, etc.). Si vous n'avez pas envie de regarder toute l'émission, vous pouvez regarder uniquement les plus intéressantes.

Qu'est-ce qui a été utilisé dans le développement ?

La partie principale a été écrite en Go. L'API avec laquelle les clients mobiles communiquaient a été écrite en Go. De plus, un service a été écrit en Go pour envoyer des notifications push aux téléphones mobiles. Nous avons également dû écrire notre propre ORM, dont nous parlerons peut-être un jour. Bon, quelques petits services sont écrits en Go : redimensionnement et upload d'images pour le côté éditeurs...

Nous avons utilisé Postgres (PostgreSQL) comme base de données. L'interface pour les éditeurs a été écrite en Ruby on Rails à l'aide de la gemme ActiveAdmin. Ruby est également utilisé pour importer des statistiques à partir d'un fournisseur de statistiques.

Pour les tests du système API, nous avons utilisé le test unitaire Python. Memcached est utilisé pour la limitation des paiements API, Chef pour le contrôle de la configuration, Zabbix pour la collecte et la surveillance des statistiques internes du système. Graylog2 est pour la collecte des journaux, Slate est la documentation de l'API pour les clients.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Sélection du protocole

Le premier problème auquel nous avons été confrontés : nous avons dû choisir le protocole d'interaction du backend avec les clients mobiles, en fonction des points suivants…

  • L'exigence la plus importante est que les données sur les clients doivent être mises à jour en temps réel. Autrement dit, tous ceux qui regardent actuellement l'émission devraient recevoir des mises à jour presque instantanément.
  • Pour simplifier, nous avons supposé que les données synchronisées avec les clients ne sont pas supprimées, mais masquées à l'aide d'indicateurs spéciaux.
  • Toutes les requêtes rares (comme les statistiques, les files d'attente, les statistiques d'équipe) sont obtenues par des requêtes GET régulières.
  • De plus, le système devait supporter calmement 100 XNUMX utilisateurs en même temps.

Sur cette base, nous avions deux options de protocole :

  1. Websockets. Mais nous n'avions pas besoin de canaux du client au serveur. Nous n'avions besoin que d'envoyer des mises à jour du serveur au client, donc le socket Web est une option redondante.
  2. Les événements envoyés par le serveur (SSE) sont parfaits ! C'est assez simple et satisfait, en principe, tout ce dont nous avons besoin.

Événements envoyés par le serveur

Quelques mots sur le fonctionnement de ce truc...

Cela fonctionne au-dessus d'une connexion http. Le client envoie une requête, le serveur répond avec Content-Type : text/event-stream et ne ferme pas la connexion avec le client, mais continue à écrire des données sur la connexion :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Les données peuvent être envoyées dans un format convenu avec les clients. Dans notre cas, nous l'avons envoyé sous cette forme : le nom de la structure modifiée (personne, joueur) a été envoyé au champ d'événement, et JSON avec de nouveaux champs modifiés pour le joueur a été envoyé au champ de données.

Maintenant, comment fonctionne l'interaction elle-même.

  • Tout d'abord, le client détermine à quand remonte la dernière synchronisation horaire avec le service : il consulte sa base de données locale et détermine la date du dernier changement enregistré par celui-ci.
  • Il envoie une requête avec cette date.
  • En réponse, nous lui envoyons toutes les mises à jour intervenues depuis cette date.
  • Après cela, il se connecte au canal en direct et ne se ferme pas tant qu'il n'a pas besoin de ces mises à jour :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

On lui envoie une liste de changements : si quelqu'un marque un but - on change le score du match, on s'est blessé - c'est aussi envoyé en temps réel. Ainsi, dans le flux d'événements de match, les clients reçoivent instantanément des données à jour. Périodiquement, pour que le client comprenne que le serveur n'est pas mort, que rien ne lui est arrivé, nous envoyons un horodatage une fois toutes les 15 secondes - afin qu'il sache que tout est en ordre et qu'il n'est pas nécessaire de se reconnecter.

Comment une connexion en direct est-elle gérée ?

  • Tout d'abord, nous créons un canal qui recevra les mises à jour avec un tampon.
  • Après cela, nous nous abonnons à cette chaîne pour recevoir des mises à jour.
  • Nous définissons l'en-tête correct afin que le client sache que tout va bien.
  • Nous envoyons le premier ping. Nous écrivons simplement l'horodatage actuel de la connexion.
  • Après cela, nous lisons le canal en boucle jusqu'à ce que le canal de mise à jour soit fermé. Le canal reçoit périodiquement soit l'horodatage actuel, soit les modifications que nous écrivons déjà pour ouvrir les connexions.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Le premier problème auquel nous avons été confrontés était le suivant : pour chaque connexion ouverte avec le client, nous avons créé une minuterie qui cochait toutes les 15 secondes - il s'avère que si nous avions 6 6 connexions ouvertes avec une machine (avec un serveur API), XNUMX XNUMX minuteries ont été créés. Cela a conduit au fait que la machine ne tenait pas la charge requise. Le problème n'était pas si évident pour nous, mais nous avons obtenu un peu d'aide et nous l'avons résolu.

En conséquence, nous avons maintenant un ping provenant du même canal d'où provient la mise à jour.

En conséquence, il n'y a qu'une seule minuterie qui tourne toutes les 15 secondes.

Il existe plusieurs fonctions auxiliaires ici - envoyer l'en-tête, le ping et la structure elle-même. C'est-à-dire que le nom de la table (personne, match, saison) et les informations sur cet enregistrement lui-même sont transmis ici :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Mécanisme d'envoi des mises à jour

Maintenant, un peu d'où viennent les changements. Nous avons plusieurs personnes, des monteurs, qui regardent l'émission en temps réel. Ils créent tous les événements : quelqu'un a été expulsé, quelqu'un s'est blessé, une sorte de remplaçant...

Avec l'aide du CMS, les données entrent dans la base de données. Après cela, la base de données utilise le mécanisme Listen/Notify pour en informer les serveurs API. Les serveurs API envoient déjà ces informations aux clients. Ainsi, en fait, nous n'avons que quelques serveurs connectés à la base de données et il n'y a pas de charge particulière sur la base de données, car le client n'interagit en aucune façon directement avec la base de données :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

PostgreSQL : Écouter/Notifier

Le mécanisme d'écoute/notification de Postgres vous permet d'avertir les abonnés aux événements qu'un événement a changé - un enregistrement a été créé dans la base de données. Pour ce faire, nous avons écrit un déclencheur et une fonction simples :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Lors de l'insertion ou de la modification d'un enregistrement, nous appelons la fonction de notification sur le canal data_updates, en transmettant le nom de la table et l'ID de l'enregistrement qui y a été modifié ou inséré.

Pour toutes les tables qui doivent être synchronisées avec le client, nous définissons un déclencheur qui, après modification / mise à jour d'un enregistrement, appelle la fonction indiquée sur la diapositive ci-dessous.
Comment l'API souscrit-elle à ces changements ?

Un mécanisme de sortance est créé - il envoie des messages au client. Il collecte tous les canaux clients et distribue les mises à jour qu'il a reçues via ces canaux :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Voici la bibliothèque standard pq, qui se connecte à la base de données et dit qu'elle veut écouter le canal (data_updates), vérifie que la connexion est ouverte et que tout va bien. J'omets la vérification des erreurs pour économiser de l'espace (ne pas vérifier est risqué).

Ensuite, nous définissons de manière asynchrone un Ticker qui enverra un ping toutes les 15 secondes et commencera à écouter la chaîne à laquelle nous nous sommes abonnés. Si nous recevons un ping, nous publions ce ping. Si nous avons reçu un enregistrement, alors nous publions cet enregistrement à tous les abonnés de cette Fanout'a.

Comment fonctionne la distribution ?

En russe, cela se traduit par "séparateur". Nous avons un objet qui enregistre les abonnés qui souhaitent recevoir une sorte de mises à jour. Et dès qu'une mise à jour de cet objet arrive, il distribue cette mise à jour à tous ses abonnés. Assez simple:

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Comment il est implémenté dans Go :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Il y a une structure, elle est synchronisée à l'aide de Mutex'ov. Il a un champ qui enregistre l'état de la connexion de Fanout à la base de données, c'est-à-dire qu'il écoute actuellement et recevra des mises à jour, ainsi qu'une liste de tous les canaux disponibles - carte, dont la clé est le canal et la structure comme valeurs (en fait, il ne s'en sert d'aucune façon).

Deux méthodes - Connecté et Déconnecté - vous permettent de dire à Fanout que nous avons une connexion à la base, qu'elle est apparue et que la connexion à la base est interrompue. Dans le second cas, vous devez déconnecter tous les clients et leur dire qu'ils ne peuvent plus rien écouter et qu'ils se reconnectent car la connexion avec eux a été fermée.

Il existe également une méthode Subscribe qui ajoute la chaîne aux "auditeurs" :

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Il existe la méthode Unsubscribe, qui supprime le canal des écouteurs si le client est déconnecté, ainsi que la méthode Publish, qui permet d'envoyer un message à tous les abonnés.

question: Qu'est-ce qui est transmis sur ce canal ?

MME: - Le modèle qui a changé ou le ping est transmis (essentiellement juste un nombre, entier).

MME: - Vous pouvez envoyer n'importe quoi, n'importe quelle structure, la publier - elle se transforme simplement en JSON et c'est tout.

MME: - Nous recevons une notification de Postgres - elle contient le nom de la table et l'identifiant. Par le nom de la table, nous obtenons et l'identifiant nous obtenons l'enregistrement dont nous avons besoin, et nous envoyons cette structure pour publication.

Infrastructure

À quoi cela ressemble-t-il en termes d'infrastructures? Nous avons 7 serveurs de fer : l'un d'eux est entièrement dédié à la base, les six autres font tourner des machines virtuelles. Il existe 6 copies de l'API : chaque machine virtuelle avec l'API s'exécute sur un serveur de fer séparé - c'est pour la fiabilité.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Nous avons deux interfaces sur lesquelles Keepalived est installé pour améliorer l'accessibilité afin qu'une interface puisse remplacer l'autre en cas de problème. Aussi - deux copies du CMS.

Il existe également un importateur de statistiques. Il existe une base de données esclave à partir de laquelle des sauvegardes sont effectuées périodiquement. Il y a Pigeon Pusher - l'application qui envoie des poussées aux clients, ainsi que des éléments d'infrastructure : Zabbix, Graylog2 et Chef.

En fait, cette infrastructure est redondante, car 100 XNUMX peuvent être servis avec moins de serveurs. Mais il y avait du fer - nous l'avons utilisé (on nous a dit que c'était possible - pourquoi pas).

Avantages de Go

Après avoir travaillé sur cette application, ces avantages évidents de Go sont apparus.

  • Sympa la bibliothèque http. Avec lui, vous pouvez déjà créer beaucoup de choses "prêtes à l'emploi".
  • De plus, des canaux qui nous ont permis de mettre en place très facilement un mécanisme d'envoi de notifications aux clients.
  • La merveilleuse fonctionnalité de détecteur de course nous a permis d'éliminer plusieurs bugs critiques (infrastructure de mise en scène). Tout ce qui fonctionne sur la mise en scène est en cours d'exécution, compilé avec la clé Race; et nous pouvons, en conséquence, voir sur l'infrastructure de mise en scène quels problèmes potentiels nous avons.
  • Minimalisme et simplicité du langage.

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

Nous recherchons des développeurs ! Si quelqu'un veut - s'il vous plaît.

des questions

Question du public (ci-après - Q) : - Je pense que vous avez manqué un point important concernant le Fan-out. Dois-je bien comprendre que lorsque vous envoyez une réponse à un client, vous bloquez si le client ne veut pas lire ?

MME: Non, nous ne bloquons pas. Tout d'abord, nous avons tout derrière nginx, c'est-à-dire qu'il n'y a aucun problème avec les clients lents. Deuxièmement, le client a un canal avec un tampon - en fait, nous pouvons y mettre jusqu'à une centaine de mises à jour ... Si nous ne pouvons pas écrire sur le canal, il le supprime. Si nous voyons que le canal est bloqué, nous fermons simplement le canal, et c'est tout - le client se reconnectera en cas de problème. Par conséquent, en principe, le blocage ne se produit pas ici.

В: - Ne pourrait-il pas être envoyé immédiatement en Ecoute/Notifier un enregistrement, et non une table d'identifiants ?

MME: - Listen/Notify a une limite de 8k octets sur le préchargement qu'il envoie. En principe, il serait possible d'envoyer si nous avions affaire à une petite quantité de données, mais il me semble que cette façon [comme nous le faisons] est tout simplement plus fiable. Les restrictions sont dans Postgres lui-même.

В: – Les clients reçoivent-ils des mises à jour sur les matchs qui ne les intéressent pas ?

MME: – En général, oui. En règle générale, il y a 2-3 matchs en parallèle, puis assez rarement. Si un client regarde quelque chose, il regarde généralement le match en cours. Ensuite, sur le client, il y a une base de données locale dans laquelle toutes ces mises à jour sont ajoutées, et même sans connexion Internet, le client peut regarder tous les matchs passés pour lesquels il a des mises à jour. En fait, nous synchronisons notre base de données sur le serveur avec la base de données locale du client afin qu'elle puisse fonctionner hors ligne.

В: – Pourquoi avez-vous fait votre ORM ?

Alexey (l'un des développeurs de "Look +") : - A cette époque (c'était il y a un an) il y avait moins d'ORM que maintenant, alors qu'il y en a pas mal. De la plupart des ORM, ce que je n'aime pas le plus, c'est que la plupart d'entre eux fonctionnent sur des interfaces vides. C'est-à-dire les méthodes qui, dans ces ORM, sont prêtes à tout : une structure, un pointeur de structure, un nombre, quelque chose de complètement hors de propos...

Notre ORM génère des structures basées sur le modèle de données. Moi-même. Et donc toutes les méthodes sont concrètes, n'utilisent pas de réflexion, etc. Elles acceptent les structures et s'attendent à utiliser toutes les structures qui se présentent.

В: – Combien de personnes ont participé ?

MME: - Au stade initial, deux personnes ont participé. Quelque part en juin, nous avons commencé, en août, la partie principale était prête (la première version). Il y a eu une sortie en septembre.

В: - Lorsque vous décrivez SSE, vous n'utilisez pas de délai d'attente. Pourquoi donc?

MME: - Pour être honnête, SSE est toujours un protocole html5 : la norme SSE est conçue pour communiquer avec les navigateurs, pour autant que je sache. Il a des fonctionnalités supplémentaires pour que les navigateurs puissent se reconnecter (et ainsi de suite), mais nous n'en avons pas besoin, car nous avions des clients qui pouvaient implémenter n'importe quelle logique pour se connecter et recevoir des informations. Nous n'avons pas plutôt SSE, mais quelque chose de similaire à SSE. Ce n'est pas le protocole lui-même.
Ce n'était pas nécessaire. Autant que je sache, les clients ont implémenté le mécanisme de connexion presque à partir de zéro. Ils s'en fichaient fondamentalement.

В: – Quels utilitaires supplémentaires avez-vous utilisés ?

MME: – Nous avons utilisé govet et golint le plus activement pour garder le style cohérent, ainsi que gofmt. Rien d'autre n'a été utilisé.

В: - Qu'as-tu utilisé pour le réparer ?

MME: - Le débogage s'est généralement fait à l'aide de tests. Nous n'avons utilisé aucun débogueur, GOP.

В: - Pouvez-vous retourner la diapositive où la fonction Publier est implémentée ? Les noms de variables à une seule lettre vous dérangent-ils ?

MME: - Non. Ils ont une portée assez "étroite". Ils ne sont utilisés nulle part sauf ici (sauf pour les composants internes de cette classe), et il est très compact - il ne prend que 7 lignes.

В: D'une certaine manière, ce n'est toujours pas intuitif...

MME: - Non, non, c'est un vrai code ! Ce n'est pas une question de style. C'est juste une si petite classe utilitaire - seulement 3 champs à l'intérieur de la classe ...

Mikhaïl Salosin. Golang réunion. Utilisation de Go dans le backend de l'application Look+

MME: – Dans l'ensemble, toutes les données synchronisées avec les clients (matchs saisonniers, joueurs) ne changent pas. En gros, si nous faisons un autre type de sport dans lequel nous devons changer le match, nous prendrons simplement tout en compte dans la nouvelle version du client, et les anciennes versions du client seront bannies.

В: – Existe-t-il des packages tiers pour gérer les dépendances ?

MME: Nous avons utilisé go dep.

В: - Dans le sujet du rapport, il y avait quelque chose à propos de la vidéo, mais il n'y avait pas de vidéo dans le rapport.

MME: – Non, je n'ai rien dans le sujet concernant la vidéo. Cela s'appelle "Look+" - c'est ainsi que l'application s'appelle.

В: – Vous avez dit que vous streamiez pour des clients ?..

MME: - Nous n'avons pas traité de vidéo en streaming. Cela a été entièrement réalisé par Megafon. Oui, je n'ai pas dit que l'application est mégaphone.

MME: – Go – pour envoyer toutes les données – par score, par événements de match, statistiques… Go est tout le backend de l'application. Le client doit savoir de quelque part quel lien utiliser pour le joueur afin que l'utilisateur puisse regarder le match. Nous avons des liens vers des vidéos et des flux qui sont préparés.

Quelques publicités 🙂

Merci de rester avec nous. Vous aimez nos articles ? Vous voulez voir du contenu plus intéressant ? Soutenez-nous en passant une commande ou en recommandant à vos amis, cloud VPS pour les développeurs à partir de 4.99 $, un analogue unique des serveurs d'entrée de gamme, que nous avons inventé pour vous : Toute la vérité sur le VPS (KVM) E5-2697 v3 (6 Cores) 10Go DDR4 480Go SSD 1Gbps à partir de 19$ ou comment partager un serveur ? (disponible avec RAID1 et RAID10, jusqu'à 24 cœurs et jusqu'à 40 Go de DDR4).

Dell R730xd 2 fois moins cher dans le centre de données Equinix Tier IV à Amsterdam ? Ici seulement 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV à partir de 199$ aux Pays-Bas! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - à partir de 99$ ! En savoir plus Comment construire une infrastructure corp. classe avec l'utilisation de serveurs Dell R730xd E5-2650 v4 qui valent 9000 XNUMX euros pour un sou ?

Source: habr.com

Ajouter un commentaire