Service Dart: Framework d'application serveur

Table des matiĂšres




EntraĂźnement



La derniĂšre fois que nous avons terminĂ© sur cela, nous avons placĂ© un mannequin de page Web statique, dĂ©veloppĂ© Ă  l'aide de Flutter pour le Web. La page affiche la progression du dĂ©veloppement de notre service, mais les donnĂ©es sur les dates de dĂ©but de dĂ©veloppement et de sortie ont dĂ» ĂȘtre codĂ©es en dur dans l'application. Ainsi, nous avons perdu la possibilitĂ© de modifier les informations sur la page. Il est temps de dĂ©velopper une application de serveur de donnĂ©es. Un diagramme de toutes les applications de service se trouve dans l'article "Service in Dart Language: Introduction, Backend Infrastructure" .



Dans cet article, nous allons Ă©crire une application utilisant le framework Aqueduct, Ă©valuez ses performances et sa consommation de ressources dans diffĂ©rents modes, Ă©crivez une boĂźte Ă  outils pour la compilation dans une application native pour Windows et Linux, gĂ©rez les migrations du schĂ©ma de base de donnĂ©es pour les classes d'application de domaine et publiez mĂȘme notre image docker d'outil dans le registre DockerHub public.







Utilité




Installation de l'aqueduc



Commençons par installer dart-sdk, un kit de développement Dart. Vous pouvez l'installer à l'aide du gestionnaire de packages de votre systÚme d'exploitation comme suggéré ici . Cependant, dans le cas de Windows, aucun gestionnaire de packages n'est installé par défaut sur votre systÚme. Alors juste:



  • TĂ©lĂ©chargez l'archive et dĂ©compressez-la sur le lecteur C:
  • , , , . . « »



  • Path . dart , , C:\dart-sdk\bin
  • , dart pub ( dart)



    dart --version






    pub -v




  • , ,
  • aqueduct CLI (command line interface)



    pub global activate aqueduct






    aqueduct




Il est théoriquement possible d'installer également un serveur de base de données PostgreSQL localement . Cependant, Docker nous permet d'éviter ce besoin et rend l'environnement de développement similaire à l'environnement d'exécution sur le serveur.



Génération d'applications



Alors, ouvrons notre dossier serveur dans VsCode



code c:/docs/dart_server


Pour ceux qui n'ont pas vu les premier et deuxiĂšme articles, le code source peut ĂȘtre clonĂ© Ă  partir du rĂ©fĂ©rentiel guthub :



git clone https://github.com/AndX2/dart_server.git
Créons un modÚle d'application:



aqueduct create data_app






Faisons connaissance avec le contenu du modĂšle de projet:



  • README.md - une note dĂ©crivant comment travailler avec un projet d'aqueduc, exĂ©cuter des tests, gĂ©nĂ©rer de la documentation API, etc. Fichier auxiliaire.
  • pubspec.yaml — pub. , , , .





  • config.yaml config.src.yaml — . .
  • analysis_options.yaml — ( ). .
  • .travis.yml — (continuous Integration). .
  • pubspec.lock .packages — pub. — , , — ().
  • .dart_tool/package_config.json — , aqueduct CLI. .
  • bin/main.dart — (, ). ( ).





  • lib/channel.dart — ApplicationChannel — . Aqueduct CPU RAM. ( Dart isolate) () .



  • lib/data_app.dart — . (library) dart_app





  • test/ — . -, . Postman.


Configuration



La premiÚre tùche à résoudre est la configuration de l'application au démarrage. Aqueduct dispose d'un mécanisme intégré pour extraire les paramÚtres des fichiers de configuration, mais ce mécanisme n'est pas trÚs pratique lorsqu'il est exécuté dans un conteneur Docker. Nous agirons différemment:



  • Passons la liste des variables au systĂšme d'exploitation du conteneur.
  • Lors du lancement de l'application Ă  l'intĂ©rieur du conteneur, nous lirons les variables d'environnement du systĂšme d'exploitation et les utiliserons pour la configuration initiale.
  • CrĂ©ons une route pour afficher toutes les variables d'environnement de l'application en cours d'exĂ©cution sur le rĂ©seau (cela sera utile lors de la visualisation de l'Ă©tat de l'application depuis le panneau d'administration).


Dans le dossier / lib , créez plusieurs dossiers et le premier référentiel pour accéder aux variables d'environnement:







EnvironmentRepository dans le constructeur lit les variables d'environnement du systÚme d'exploitation sous la forme d'un dictionnaire Map <String, String> et les enregistre dans la variable privée _env . Ajoutons une méthode pour obtenir tous les paramÚtres sous la forme d'un dictionnaire: lib / service / EnvironmentService - le composant logique pour accéder aux données EnvironmentRepository:















Injection de dépendance



Ici, vous devez vous arrĂȘter et gĂ©rer les dĂ©pendances des composants:



  • le contrĂŽleur rĂ©seau aura besoin d'une instance du service variable,
  • le service doit ĂȘtre unique pour l'ensemble de l'application,
  • pour crĂ©er un service, vous devez d'abord crĂ©er une instance du rĂ©fĂ©rentiel de variables.


Nous allons résoudre ces problÚmes en utilisant la bibliothÚque GetIt . Connectez le package requis à pubspec.yaml :







Créez une instance du conteneur d'injecteur lib / di / di_container.dart et écrivez une méthode avec l'enregistrement du référentiel et du service: Appelez la méthode d'initialisation du conteneur DI dans la méthode de préparation de l'application:















Couche réseau



lib / controller / ActuatorController - composant réseau http. Il contient des méthodes pour accéder aux données de service de l'application: Déclarons les gestionnaires de route pour les contrÎleurs dans lib / controller / Routes :















Premier départ



Pour courir, vous avez besoin de:



  • empaqueter l'application dans une image Docker,
  • ajouter un conteneur au script docker-compose,
  • configurer NGINX pour les requĂȘtes proxy.


Créez un Dockerfile dans le dossier de l'application . Voici le script pour créer et exécuter l'image pour Docker: Ajoutez le conteneur d'application au script docker-compose.yaml : Créez le fichier data_app.env avec des variables de configuration pour l'application: Ajoutez un nouvel emplacement à la configuration de débogage NGINX conf.dev.d / default.conf : Exécutez le débogage script avec l'indicateur de pré-construction:



































docker-compose -f docker-compose.yaml -f docker-compose.dev.yaml up --build






Le script s'est exécuté avec succÚs, mais il y a plusieurs points alarmants:



  • l'image officielle de la flĂ©chette de google est de 290 Mo archivĂ©e . Une fois dĂ©ballĂ©, il prendra beaucoup plus d'espace - 754 Mo. Afficher une liste d'images et leurs tailles:



    docker images
  • Le temps de compilation et de compilation JIT Ă©tait de plus de 100 secondes. Trop pour exĂ©cuter une application en solde
  • Consommation de mĂ©moire dans le tableau de bord docker 300 Mo immĂ©diatement aprĂšs le lancement



  • Lors d'un test de charge (rĂ©seau GET / api / actionneur / requĂȘtes uniquement), la consommation de mĂ©moire est comprise entre 350 et 390 Mo pour une application s'exĂ©cutant dans un isolat





Vraisemblablement, les ressources de notre VPS à petit budget ne sont pas suffisantes pour exécuter une application aussi gourmande en ressources. Allons vérifier:



  • CrĂ©ez un dossier sur le serveur pour la nouvelle version de l'application et copiez le contenu du projet



    ssh root@dartservice.ru "mkdir -p /opt/srv_2" && scp -r ./* root@91.230.60.120:/opt/srv_2/
  • Vous devez maintenant transfĂ©rer dans ce dossier le projet de page Web de / opt / srv_1 / public / et tout le contenu du dossier / opt / srv_1 / sertbot / (il contient des certificats SSL pour NGINX et Let's encrypt bot logs), et copier la clĂ© de / opt / srv_1 / dhparam /
  • Lancez le moniteur de ressources du serveur dans une console distincte



    htop


  • ExĂ©cutons le script docker-compose dans le dossier / opt / srv_2 /



    docker-compose up --build -d
  • Voici Ă  quoi ressemble l'assembly d'application avant le lancement:





  • Et donc - au travail:







    Sur les 1 Go de RAM disponibles, notre application consomme 1,5 Go "occupant" le fichier d'échange manquant. Oui, l'application a démarré, mais nous ne parlons d'aucune capacité de charge.
  • ArrĂȘtons le script:



    docker-compose down


AOT



Nous avons trois tùches à résoudre:



  • rĂ©duire la consommation de RAM par application de flĂ©chettes,
  • rĂ©duire le temps de dĂ©marrage,
  • rĂ©duire la taille du conteneur Docker de l'application.


La solution sera d'abandonner la fléchette lors de l'exécution. Depuis la version 2.6, les applications Dart prennent en charge la compilation en code exécutable natif . Aqueduct prend en charge la compilation à partir de la version 4.0.0-b1.



Commençons par supprimer la CLI d'aqueduc localement:



pub global deactivate aqueduct


Installez la nouvelle version:



pub global activate aqueduct 4.0.0-b1


Augmentons les dépendances dans pubspec.yaml: Construisons l'







application native:



aqueduct build


Le résultat sera un seul fichier data_app.aot d'une taille d'environ 6 Mo. Vous pouvez lancer immédiatement cette application avec des paramÚtres, par exemple:



data_app.aot --port 8080 --isolates 2


La consommation de mémoire immédiatement aprÚs le lancement est inférieure à 10 Mo.



Voyons sous charge. ParamĂštres de test: requĂȘtes GET / actionneur rĂ©seau, 100 threads avec vitesse maximale disponible, 10 minutes. RĂ©sultat:







Total: vitesse moyenne - 13 000 requĂȘtes par seconde pour un corps de rĂ©ponse JSON de 1,4 kV, temps de rĂ©ponse moyen - 7 ms, consommation de mĂ©moire (pour deux instances) 42 Mo. Il n'y a pas d'erreurs.



Lorsque nous répétons le test avec six instances de l'application, la vitesse moyenne monte bien sûr à 19k / s, mais l'utilisation du processeur atteint 45% lorsque la consommation de mémoire est de 64 Mo.

C'est un excellent résultat.



Emballage de conteneurs



Ici, nous allons faire face à une difficulté de plus: nous ne pouvons compiler une application de fléchettes dans un natif pour le systÚme d'exploitation actuel. Dans mon cas, il s'agit de Windows10 x64. Dans un conteneur docker, je préférerais bien sûr l'une des distributions Linux - par exemple, Ubuntu 20.10.



La solution ici sera un docker-stand intermĂ©diaire, utilisĂ© uniquement pour crĂ©er des applications natives pour Ubuntu. Écrivons il / dart2native / Dockerfile : Maintenant , nous allons construire dans une image docker nommĂ© aqueduct_builder: 4.0.0-b1 , les anciennes versions d' Ă©craser, le cas Ă©chĂ©ant:











docker build --pull --rm -f "dart2native\Dockerfile" -t aqueduct_builder:4.0.0-b1 "dart2native"


Allons vérifier:



docker images






Écrivons un script de construction pour l'application native docker-compose.dev.build.yaml : ExĂ©cutez le script de construction:











docker-compose -f docker-compose.dev.build.yaml up






Le fichier data_app.aot compilĂ© pour Ubuntu prend dĂ©jĂ  9 Mo. Au dĂ©marrage, utilise 19 Mo de RAM (pour deux instances). Effectuons des tests de charge locaux dans les mĂȘmes conditions, mais dans un conteneur avec proxy NGINX (GET, 100 threads): en







moyenne 5,3k requĂȘtes par seconde. Dans le mĂȘme temps, la consommation de RAM ne dĂ©passait pas 55 Mo. La taille de l'image a diminuĂ© par rapport Ă  la flĂ©chette et Ă  l'aqueduc installĂ©s de 840 Mo Ă  74 Mo sur le disque. Écrivons



un nouveau script docker-compose.aot.yaml pour lancer l'application. Pour ce faire, nous remplacerons le bloc de description data_app en installant l'image de base «vide» Ubuntu: 20.10. Montons le fichier d'assemblage et modifions la commande de lancement:







RĂ©solvons un autre problĂšme de service: en fait, l'image de construction du docker avec flĂ©chettes et aqueduc installĂ©s est un outil assez rĂ©utilisable. Il est logique de le tĂ©lĂ©charger sur un registre public et de le connecter en tant qu'image tĂ©lĂ©chargeable prĂȘte Ă  l'emploi. Cela nĂ©cessite:



  • s'inscrire auprĂšs d'un registre public tel que DockerHub ,
  • connectez-vous localement avec le mĂȘme identifiant



    docker login 
  • renommer l'image tĂ©lĂ©chargĂ©e en utilisant le schĂ©ma login / title: tag



    docker image tag a365ac7f5bbb andx2/aqueduct:4.0.0-b1
  • dĂ©charger l'image dans le registre



    docker push andx2/aqueduct:4.0.0-b1


    https://hub.docker.com/repository/docker/andx2/aqueduct/general



Nous pouvons maintenant modifier notre script de construction pour utiliser l'image publique







Connexion à la base de données



Aqueduct a déjà un ORM intégré pour travailler avec une base de données PostgreSQL. Pour l'utiliser, vous avez besoin de:



  • CrĂ©ez des objets de domaine qui dĂ©crivent les enregistrements de la base de donnĂ©es.
  • . : , , . Aqueduct , , ManagedObject ( ), , . .
  • . , , .
  • , aqueduct, , seed() — - .
  • aqueduct CLI.


Commençons par connecter un nouveau conteneur docker Ă  la base de donnĂ©es PostgreSQL dans le script docker-compose.aot.yaml. Image prĂȘte basĂ©e sur Linux Alpine (version "compacte" de Linux pour les applications embarquĂ©es): Ici, vous devez faire attention au fichier de variable d'environnement data_db.env . Le fait est que l'image est prĂ©configurĂ©e pour utiliser ces variables comme nom d'utilisateur, hĂŽte, port et mot de passe d'accĂšs. Ajoutons ces variables au fichier: Les valeurs sont donnĂ©es sous condition. Nous allons Ă©galement monter le dossier hĂŽte ./data_db/ dans un conteneur pour stocker les donnĂ©es de la base de donnĂ©es. Ensuite, dans l'application data_app, ajoutez la classe / service / DbHelper pour vous connecter Ă  la base de donnĂ©es Ă  l'aide de variables d'environnement:





























Créons un objet de domaine géré par ORM pour obtenir les paramÚtres de l'application cliente: Ajoutez un référentiel et un service pour ajouter des paramÚtres et obtenir la version actuelle: ContrÎleur réseau: enregistrer de nouveaux composants dans le conteneur DI: ajouter un nouveau contrÎleur et un nouveau point de terminaison au routeur: nous générons maintenant un fichier de migration de base de données. Exécutons:















































aqueduct db generate
Le rĂ©sultat sera la crĂ©ation de fichiers de migration dans le dossier du projet: Vous devez maintenant rĂ©soudre le problĂšme de service: les migrations doivent ĂȘtre appliquĂ©es Ă  partir d'un systĂšme avec aqueduct (et dart) installĂ© vers une base de donnĂ©es s'exĂ©cutant dans un conteneur, et cela doit ĂȘtre fait Ă  la fois pendant le dĂ©veloppement local et sur le serveur. Dans ce cas, nous utiliserons l'image prĂ©cĂ©demment construite et publiĂ©e pour l'assemblage AOT. Écrivons le script de migration de base de donnĂ©es docker-compose correspondant: Un dĂ©tail intĂ©ressant est la chaĂźne de connexion Ă  la base de donnĂ©es. Lors de l'exĂ©cution du script, vous pouvez transmettre un fichier avec des variables d'environnement comme argument, puis utiliser ces variables pour la substitution dans le script:























docker-compose -f docker-compose.migrations.yaml --env-file=./data_app.env --compatibility up --abort-on-container-exit


Faisons Ă©galement attention aux drapeaux de lancement:



  • --compatibility - compatibilitĂ© avec les versions docker-compose des scripts 2.x. Cela permettra d'utiliser les options de dĂ©ploiement pour restreindre l'utilisation des ressources par le conteneur, qui sont ignorĂ©es dans les versions 3.x. Nous avons limitĂ© la consommation de RAM Ă  200 Mo et l'utilisation du processeur Ă  50%
  • --abort-on-container-exit - Cet indicateur dĂ©finit le mode d'exĂ©cution du script de sorte que lorsque l'un des conteneurs de script s'arrĂȘte, tous les autres se terminent. Par consĂ©quent, lorsque la commande de migration du schĂ©ma de base de donnĂ©es est exĂ©cutĂ©e et que le conteneur avec l'aqueduc s'arrĂȘte, docker-compose mettra Ă©galement fin au conteneur de base de donnĂ©es.


Publication



Pour préparer la publication de l'application, vous devez:



  • data_app.env data_db.env. , POSTGRES_PASSWORD=postgres_password
  • docker-compose.aot.yaml docker-compose.yaml.
  • /api/actuator. .


Copiez le dossier d'application ./data_app/ sur le serveur . Un point important ici sera le commutateur -p (copier tout en préservant les attributs de fichier) dans la commande de copie. Permettez-moi de vous rappeler que lors de la construction de l'application native, nous définissons les droits d'exécution sur le fichier data_app.aot :



scp -rp ./data_app root@dartservice.ru:/opt/srv_1


Copions Ă©galement:



  • Configuration de NGINX modifiĂ©e ./conf.d/default.conf
  • Scripts de dĂ©marrage et de migration docker-compose.yaml , docker-compose.migrations.yaml
  • Fichiers avec les variables d'environnement data_app.env et data_db.env


Ajoutez le dossier / opt / srv_1 / data_db sur le serveur . Il s'agit du volume du systÚme de fichiers hÎte à monter dans le conteneur de base de données. Toutes les données PostgreSQL seront enregistrées ici.



mkdir /opt/srv_2/data_db


Exécutons le script de migration du schéma de base de données:



docker-compose -f docker-compose.migrations.yaml --env-file=./data_app.env up --abort-on-container-exit


Lançons le script d'application:



docker-compose up -d


-> Code source github



Au lieu d'une conclusion



Le framework pour les applications backend est prĂȘt. Dans le prochain article, sur cette base, nous Ă©crirons une nouvelle application pour autoriser les utilisateurs du service. Pour ce faire, nous utiliserons la spĂ©cification oAuth2 et nous intĂ©grerons Ă  VK et Github.



All Articles