Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Alexeï Naydenov, PDG ITooLabs, parle du développement d'une plateforme de télécommunications pour les opérateurs télécoms dans le langage de programmation Go (Golang). Alexey partage également son expérience dans le déploiement et l'exploitation de la plateforme chez l'un des plus grands opérateurs de télécommunications asiatiques, qui a utilisé la plateforme pour fournir des services de messagerie vocale (VoiceMail) et de PBX virtuel (Cloud PBX).

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Alexeï Naydenov (ci-après – AN) : - Salut tout le monde! Je m'appelle Alexeï Naydenov. Je suis le directeur d'ITooLabs. Tout d’abord, je voudrais répondre à ce que je fais ici et comment je me suis retrouvé ici.

Si vous regardez la Marketplace Bitrix24 (section « Téléphonie »), alors 14 applications et 36 qui s'y trouvent (40%) sont nous :

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Plus précisément, ce sont nos partenaires opérateurs, mais derrière tout cela se trouve notre plateforme (Platform as a Service) - ce que nous leur vendons pour un petit centime. En fait, j'aimerais parler du développement de cette plateforme et de la façon dont nous en sommes arrivés à Go.

Les chiffres de notre plateforme maintenant :

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

44 opérateurs partenaires, dont Megafon. D'une manière générale, nous aimons beaucoup vivre des aventures différentes et nous avons effectivement accès à 100 millions d'abonnés de 44 opérateurs ici en Russie. Par conséquent, si quelqu’un a des idées commerciales, nous serons toujours heureux de les écouter.

  • 5000 entreprises utilisatrices.
  • 20 000 abonnés au total. Tout cela est b2b - nous travaillons uniquement avec des entreprises.
  • 300 appels par minute pendant la journée.
  • 100 millions de minutes d’appel l’année dernière (nous avons fêté). Cela ne prend pas en compte les négociations internes disponibles sur notre plateforme.

Comment ça a commencé?

Comment les bons gars peuvent-ils commencer à créer leur plateforme ? Il faut aussi prendre en compte que nous avons un historique de développement « d’entreprise hardcore », et même à la période de l’année la plus précise pour une entreprise ! C’était cette époque heureuse où vous vous présentiez vers le client et lui disiez : « Nous avons besoin de quelques serveurs supplémentaires. » Et le client : « Pas de question ! Nous en avons dix dans le rack.

Nous avons donc fait Oracle, Java, WebSphere, Db2 et tout ça. C’est pourquoi nous avons bien sûr choisi les meilleures solutions des fournisseurs, les avons intégrées et avons essayé de décoller avec elles. Nous avons marché seuls. Ce serait une telle startup interne.

Tout cela a effectivement commencé en 2009. Depuis 2006, nous sommes étroitement impliqués dans les solutions des opérateurs, d'une manière ou d'une autre. Nous avons réalisé plusieurs PBX virtuels personnalisés (comme celui que nous avons actuellement en commande) : nous l'avons examiné, avons décidé qu'il était bon et avons décidé de démarrer un démarrage interne.

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Nous avons pris VMware. Comme nous étions seuls, nous avons dû immédiatement abandonner le cool fournisseur Storage. On sait tout d’eux : les promesses doivent être divisées par 3, et le coût doit être multiplié par 10. C’est pour ça qu’ils ont fait DirDB et ainsi de suite.

Puis ça a commencé à grandir. Un service de facturation a été ajouté à cela car la plateforme ne pouvait plus faire face. Ensuite, le serveur de facturation de MySQL a été transféré vers Mongo. En conséquence, nous avons obtenu une solution fonctionnelle qui traite tous les appels qui y parviennent :

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Mais quelque part là-bas, à l'intérieur, tourne le même produit du fournisseur - le principal, nucléaire, que nous avons pris autrefois. Vers la fin de l'année 2011, nous avons réalisé que le principal goulot d'étranglement pour nous serait bien sûr ce produit particulier - nous y serions confrontés. Nous avons vu un mur devant nous, dans lequel nous avons couru au grand galop, alors que de plus en plus de clients arrivaient.
En conséquence, nous devions faire quelque chose. Bien sûr, nous avons mené de nombreuses recherches sur divers produits, qu'ils soient open source ou fournis par des fournisseurs. Je ne m’étendrai pas là-dessus maintenant – ce n’est pas de cela dont nous parlons. La toute dernière option de secours à laquelle nous avons pensé était de créer notre propre plateforme.

Finalement, nous sommes arrivés à cette option. Pourquoi? Parce que tous les produits fournisseurs et open source ont été conçus pour résoudre des problèmes vieux de 10 ans. Eh bien, si 10 ans, et encore plus ! Le choix est devenu évident pour nous : soit nous disons adieu à notre grande idée de service idéal (pour les partenaires, les opérateurs et nous-mêmes), soit nous faisons quelque chose de notre côté.

Nous avons décidé de faire quelque chose de notre côté !

Exigences de la plate-forme

Si vous faites quelque chose depuis longtemps (en utilisant le produit de quelqu'un d'autre), alors la pensée se forme lentement dans votre tête : comment ferais-je cela moi-même ? Puisque nous sommes tous programmeurs dans l'entreprise (à l'exception des commerciaux, il n'y a pas de non-programmeurs), nos exigences se sont développées il y a longtemps, et elles étaient claires :

  1. Vitesse de développement élevée. Le produit du fournisseur qui nous tourmentait n'était pas satisfaisant, tout d'abord parce que tout s'est déroulé longtemps et lentement. Nous le voulions rapidement, nous avions beaucoup d'idées ! Nous avons encore beaucoup d'idées, mais la liste d'idées était telle qu'elle semblait avoir dix ans d'avance. Maintenant seulement depuis un an.
  2. Utilisation maximale du fer multicœur. C'était également important pour nous, car nous voyions qu'il y aurait de plus en plus de cœurs.
  3. Grande fiabilité. Quelque chose avec lequel nous avons aussi pleuré.
  4. Haute résistance aux pannes.
  5. Nous voulions aboutir à un processus de sorties quotidiennes. Pour cela, nous avions besoin d’un choix de langue.

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

En conséquence, à partir des exigences relatives au produit que nous nous sommes fixées, les exigences relatives à la langue augmentent de manière clairement logique.

  1. Si nous voulons prendre en charge les systèmes multicœurs, nous avons besoin de la prise en charge de l'exécution parallèle.
  2. Si nous avons besoin de rapidité de développement, nous avons besoin d’un langage qui prend en charge un développement compétitif, une programmation compétitive. Si quelqu'un n'a pas rencontré la différence, c'est très simple :
    • La programmation parallèle concerne la façon dont deux threads différents sont exécutés sur des cœurs différents ;
    • L'exécution simultanée, ou plus précisément la prise en charge de la concurrence, concerne la manière dont un langage (ou un environnement d'exécution, peu importe) permet de masquer toute la complexité liée à l'exécution parallèle.
  3. Haute stabilité. De toute évidence, nous avions besoin d’un cluster, meilleur que celui que nous avions sur le produit du fournisseur.

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Nous n’avions pas vraiment beaucoup d’options, si vous vous en souvenez. Tout d’abord, Erlang – nous l’aimons et le connaissons, c’était mon préféré, personnellement. Deuxièmement, Java n'est même pas Java, mais spécifiquement Scala. Troisièmement, une langue qu'à cette époque nous ne connaissions pas du tout - Go. Il venait tout juste d’apparaître à l’époque, ou plutôt, il existait déjà depuis environ deux ans, mais n’était pas encore sorti.

Allez, c'est gagné !

Histoire du Go

Nous avons créé une plateforme dessus. Je vais essayer d'expliquer pourquoi.

Une brève histoire du Go. Cela a commencé en 2007, ouvert en 2009, la première version est sortie en 2012 (c'est-à-dire que nous avons commencé à travailler avant même la première version). L'initiateur était Google, qui voulait remplacer, comme je le soupçonne, Java.

Les auteurs sont très connus :

  • Ken Thomson, qui était à l'origine d'Unix, a inventé UTF-8, a travaillé sur le système Plan 9 ;
  • Rob Pike, qui a inventé l'UTF-8 avec Ken, a également travaillé sur Plan 9, Inferno, Limbo aux Bell Labs ;
  • Robert Giesmer, que l'on connaît et aime pour l'invention du compilateur Java HotSpot et pour son travail sur le générateur en V8 (l'interpréteur Javascript de Google) ;
  • Et plus de 700 contributeurs, dont certains de nos correctifs.

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Allez-y : premier aperçu

On voit que le langage est plus ou moins simple et compréhensible. Nous avons des types évidents : dans certains cas, ils doivent être déclarés, dans d'autres, ils ne sont pas nécessaires (cela signifie que les types sont déduits d'une manière ou d'une autre).

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

On voit qu’il est de bon ton de décrire des structures. On voit que nous avons la notion de pointeur (là où se trouve l'astérisque). On peut voir qu'il existe un support spécial pour déclarer l'initialisation des tableaux et des tableaux associatifs.

C'est presque clair : vous pouvez vivre. Essayons d'écrire Bonjour tout le monde :

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Que voit-on ? Il s'agit d'une syntaxe de type C, le point-virgule est facultatif. Il peut s'agir d'un séparateur pour deux lignes, mais seulement s'il s'agit de deux constructions situées sur la même ligne.

Nous voyons que les parenthèses dans les structures de contrôle (à la 14e ligne) sont facultatives, mais les accolades sont toujours obligatoires. On voit que le typage est statique. Tim est retiré la plupart du temps. Cet exemple est un peu plus compliqué que l'habituel Hello, world - juste pour montrer qu'il existe une bibliothèque.

Que voyons-nous d’autre d’important ? Le code est organisé en packages. Et pour utiliser un package dans votre propre code, vous devez l'importer à l'aide de la directive import - c'est également important. Nous le lançons - ça marche. Super!

Essayons ensuite quelque chose de plus compliqué : Bonjour tout le monde, mais seulement maintenant, c'est un serveur http. Que voyons-nous d’intéressant ici ?

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Premièrement, la fonction agit comme un paramètre. Cela signifie que notre fonction est celle de « citoyen de première classe » et que vous pouvez faire beaucoup de choses intéressantes avec elle dans un style fonctionnel. Ensuite, nous voyons quelque chose d'inattendu : la directive d'importation est directement liée au référentiel GitHub. C’est vrai, c’est comme ça – et c’est d’ailleurs comme ça qu’il faut faire.

Dans Go, l'identifiant universel d'un package est l'url de son référentiel. Il existe un utilitaire Goget spécial qui récupérera toutes les dépendances, les téléchargera, les installera, les compilera et les préparera à l'utilisation si nécessaire. En même temps, Goget connaît la méta-html. Ainsi, vous pouvez conserver un répertoire http qui contiendra des liens vers votre référentiel spécifique (comme nous le faisons par exemple).

Que voyons-nous d’autre ? Http et Json dans la bibliothèque standard. Il existe évidemment une introspection - réflexion, qui devrait être utilisée dans encoding/json, car nous lui substituons simplement un objet arbitraire.

Nous l'exécutons et voyons que nous avons un code utile en 20 lignes, qui compile, exécute et rapporte la charge moyenne actuelle de la machine (sur la machine sur laquelle elle est lancée).
Qu’est-ce qui est important et que nous pouvons voir immédiatement ici ? Il est compilé en un binaire statique (buinary). Ce binaire n'a aucune dépendance, pas de bibliothèque ! Vous pouvez le copier sur n'importe quel système, l'exécuter immédiatement et cela fonctionnera.

Passant.

Aller : méthodes et interfaces

Go a des méthodes. Vous pouvez déclarer une méthode pour n’importe quel type personnalisé. De plus, il ne s’agit pas nécessairement d’une structure, mais peut-être d’un alias quelconque. Vous pouvez déclarer un alias pour N32 et écrire des méthodes pour qu'il fasse quelque chose d'utile.

Et là, nous tombons dans la stupeur pour la première fois... Il s'avère que Go n'a pas de cours en tant que tel. Ceux qui connaissent Go pourraient dire qu’il existe une inclusion de types, mais c’est tout autre chose. Plus tôt un développeur cessera de considérer cela comme un héritage, mieux ce sera. Il n'y a pas de classes dans Go, et il n'y a pas non plus d'héritage.

Question! Que nous a apporté la société d’auteurs dirigée par Google pour refléter la complexité du monde ? Ils nous ont donné des interfaces !

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

Une interface est un type spécial qui vous permet d'écrire simplement des méthodes, des signatures de méthodes. De plus, tout type pour lequel ces méthodes existent (sont exécutées) correspondra à cette interface. Cela signifie que vous pouvez simplement décrire la fonction correspondante pour un type, pour un autre (qui correspond à ce type d'interface). Ensuite, déclarez une variable du type de cette interface et affectez-lui l'un de ces objets.

Pour les fans inconditionnels, je peux dire que cette variable aura en fait deux pointeurs : l'un vers les données, l'autre vers une table spéciale de descripteurs, typique de ce type particulier, pour l'interface de ce type. Autrement dit, le compilateur crée de telles tables de descripteurs au moment de la liaison.

Et dans Go, bien sûr, il y a des pointeurs vers void. Le mot interface {} (avec deux accolades) est une variable qui permet en principe de pointer vers n'importe quel objet.
Jusqu'à présent, tout va bien, tout est familier. Rien de surprenant.

Allez : goroutines

Venons-en maintenant à ce qui nous intéresse : les processus légers - les goroutines (goroutines) dans la terminologie Go.

Alexeï Naydenov. ITooLabs. Cas de développement sur plateforme téléphonique Go (Golang). Partie 1

  1. Premièrement, ils sont vraiment légers (moins de 2 Ko).
  2. Deuxièmement, les coûts de création d'une telle goroutine sont négligeables : vous pouvez en créer des milliers par seconde - rien ne se passera.
  3. Ils sont servis par leur propre planificateur, qui transfère simplement le contrôle d'une goroutine à une autre.
  4. Dans ce cas, le contrôle est transféré dans les cas suivants :
    • si l'expression go est rencontrée (si la goroutine démarre la goroutine suivante) ;
    • si un appel d'entrée/sortie bloquant est activé ;
    • si le garbage collection démarre ;
    • si une opération avec des canaux est lancée.

Autrement dit, chaque fois qu'un programme Go s'exécute sur un ordinateur, il détermine le nombre de cœurs dans le système et lance autant de threads que nécessaire (combien de cœurs y a-t-il dans le système ou combien vous lui avez indiqué). En conséquence, le planificateur exécutera ces threads d'exécution légers sur tous ces threads du système d'exploitation dans chaque cœur.

Il convient de noter que c’est la manière la plus efficace d’utiliser le fer. En plus de ce qui est montré, nous faisons bien plus. Nous fabriquons, par exemple, des systèmes DPI qui permettent à une unité de desservir 40 gigabits (en fonction de ce qui se passe dans ces lignes).

Là, même avant Go, nous utilisions exactement le même schéma précisément pour cette raison : car il nous permet de préserver la localité du cache du processeur et de réduire considérablement le nombre de changements de contexte du système d'exploitation (ce qui prend également beaucoup de temps). Je le répète : c’est la manière la plus efficace d’utiliser le fer.

Cet exemple simple de 21 lignes est un exemple qui fait simplement écho-server. Attention, la fonction servir est extrêmement simple, elle est linéaire. Il n'y a pas de rappels, pas besoin de s'embêter et de réfléchir... Il vous suffit de lire et d'écrire !

En même temps, si vous lisez et écrivez, cela devrait en fait bloquer - cette goroutine est simplement mise dans une file d'attente et prise par le planificateur lorsque l'exécution devient à nouveau possible. Autrement dit, ce code simple peut agir comme un serveur d'écho pour autant de connexions que le permet le système d'exploitation de cette machine.

A suivre très prochainement...

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