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.