Développer un utilitaire sur GraalVM

Énoncé du problème


Périodiquement, j'ai pour tâche de partager des fichiers sur un réseau local, par exemple, avec un collègue de projet.


Il peut y avoir de nombreuses solutions pour cela - Samba / FTP / scp. Vous pouvez simplement télécharger le fichier dans un lieu public comme Google Drive, le joindre à une tâche dans Jira ou même l'envoyer par e-mail.


Mais tout cela, à un degré ou à un autre, est inflexible, quelque part il nécessite un ajustement préliminaire et a ses propres limites (par exemple, la taille maximale de l'investissement).


Et vous voulez quelque chose de plus léger et flexible.


J'ai toujours été agréablement surpris par l'opportunité sous Linux, en utilisant les moyens disponibles, de construire rapidement une solution pratique.


Disons, j'ai souvent résolu la tâche susmentionnée en utilisant le système python avec la ligne unique suivante


$ python3 -mhttp.server Serving HTTP on 0.0.0.0 port 8000 ... 

Cette commande démarre le serveur Web dans le dossier actuel et vous permet d'obtenir une liste de fichiers et de les télécharger via l'interface Web. Plus de ces choses peuvent être vidées ici .


Il y a plusieurs inconvénients. Maintenant, afin de transférer le lien de téléchargement à un collègue, vous devez connaître votre adresse IP sur le réseau.


Il est pratique d'utiliser la commande


 $ ifconfig -a 

Et puis dans la liste résultante des interfaces réseau, sélectionnez celle qui convient et composez manuellement un lien de la forme http: // IP: 8000 , que vous pouvez envoyer.


Deuxième inconvénient: ce serveur est mono-thread. Cela signifie que si l'un de vos collègues télécharge le fichier, le second ne pourra même pas télécharger la liste des fichiers.


Troisièmement, c'est inflexible. Si vous devez transférer un seul fichier, il ouvrira le dossier entier, c'est-à-dire vous devrez effectuer de tels gestes (puis nettoyer la corbeille):


 $ mkdir tmp1 $ cp file.zip tmp1 $ cd tmp1 $ python3 -mhttp.server 

Le quatrième inconvénient - il n'y a pas de moyen facile de télécharger l'intégralité du contenu d'un dossier.


Pour transférer le contenu d'un dossier, ils utilisent généralement une technique appelée tar pipe .


Ils font quelque chose comme ça:


 $ ssh user@host 'cd /path/to/source && tar cf - .' | cd /path/to/destination && tar xvf - 

Si du coup ce n'est pas clair, je vais vous expliquer comment ça marche. La première partie de la commande tar cf - . crée une archive du contenu du dossier actuel et écrit sur la sortie standard. En outre, cette sortie est transmise via un tuyau via un canal ssh sécurisé à l'entrée d'une tar xvf - similaire tar xvf - qui effectue la procédure inverse, c'est-à-dire lit l'entrée standard et décompresse le dossier actuel. En fait, l'archive de fichiers est transférée, mais sans créer de fichier intermédiaire!


L'inconvénient de cette approche est également évident. Nous avons besoin d'un accès ssh d'une machine à l'autre, et cela n'est presque jamais fait dans le cas général.


Est-il possible de réaliser tout ce qui précède, mais sans ces problèmes décrits?


Il est donc temps de formaliser ce que nous allons construire:


  1. Programme facile à installer (binaire statique)
  2. Ce qui vous permettra de transférer à la fois un fichier et un dossier avec tout le contenu
  3. Avec compression en option
  4. Ce qui permet à l'hôte de télécharger le (s) fichier (s) en utilisant uniquement les outils * nix standard (wget / curl / tar)
  5. Le programme émettra immédiatement après le lancement les commandes exactes à télécharger

Solution


Lors de la conférence JEEConf , à laquelle j'ai assisté il n'y a pas si longtemps, le sujet Graal a été soulevé à plusieurs reprises. Le sujet est loin d'être nouveau, mais pour moi c'était un déclencheur pour enfin sentir cette bête de ma propre main.


Pour ceux qui ne sont pas encore dans le sujet (y a-t-il vraiment une telle chose encore? OO), permettez-moi de vous rappeler que GraalVM est une telle machine virtuelle Java d'Oracle avec des fonctionnalités supplémentaires, dont les plus notables sont:


  1. Polyglot JVM - la possibilité d'exécuter de manière transparente Java, Javascript, Python, Ruby, R, etc. code
  2. Prise en charge de la compilation AOT - compilation de Java directement dans le binaire natif
  3. Une caractéristique moins visible, mais très cool - le compilateur C2 a été réécrit de C ++ à Java afin de faciliter son développement ultérieur. Cela a déjà produit des résultats notables. Ce compilateur fait beaucoup plus d'optimisations au stade de la conversion du bytecode Java en code natif. Par exemple, il est capable d'éliminer plus efficacement les allocations. Twitter a pu réduire la consommation de CPU de 11% simplement en activant ce paramètre, ce qui, à leur échelle, a permis une économie notable de ressources (et d'argent).

Vous pouvez rafraîchir l'idée de Graal, par exemple, dans cet article habr .


Nous allons écrire en Java, donc pour nous la fonctionnalité la plus pertinente sera la compilation AOT.


En fait, le résultat du développement est présenté dans ce référentiel Github .


Exemple d'utilisation pour transférer un seul fichier:


 $ serv '/path/to/report.pdf' To download the file please use one of the commands below: curl http://192.168.0.179:17777/dl > 'report.pdf' wget -O- http://192.168.0.179:17777/dl > 'report.pdf' curl http://192.168.0.179:17777/dl?z --compressed > 'report.pdf' wget -O- http://192.168.0.179:17777/dl?z | gunzip > 'report.pdf' 

Un exemple d'utilisation lors du transfert du contenu d'un dossier (tous les fichiers, y compris les fichiers joints!):


 $ serv '/path/to/folder' To download the files please use one of the commands below. NB! All files will be placed into current folder! curl http://192.168.0.179:17777/dl | tar -xvf - wget -O- http://192.168.0.179:17777/dl | tar -xvf - curl http://192.168.0.179:17777/dl?z | tar -xzvf - wget -O- http://192.168.0.179:17777/dl?z | tar -xzvf - 

Oui, si simple!


Veuillez noter - le programme lui-même détermine l'adresse IP correcte à laquelle les fichiers seront disponibles pour téléchargement.


Observations / Réflexions


Il est clair que l'un des objectifs de la création du programme était sa compacité. Et voici le résultat obtenu:


 $ du -hs `which serv` 2.4M /usr/local/bin/serv 

Incroyablement, l'ensemble de la JVM, avec le code de l'application, tient dans quelques misérables mégaoctets! Bien sûr, tout est quelque peu faux, mais nous en reparlerons plus tard.


En fait, le compilateur Graal produit un binaire légèrement supérieur à 7 mégaoctets. J'ai décidé de le compresser davantage avec l'UPX .


Cela s'est avéré être une bonne idée, car le temps de lancement a augmenté alors qu'il était très insignifiant:


Option non compressée:


 $ time ./build/com.cmlteam.serv.serv -v 0.1 real 0m0.001s user 0m0.001s sys 0m0.000s 

Compressé:


 $ time ./build/serv -v 0.1 real 0m0.021s user 0m0.021s sys 0m0.000s 

A titre de comparaison, l'heure de lancement de la "manière traditionnelle":


 $ time java -cp "/home/xonix/proj/serv/target/classes:/home/xonix/.m2/repository/commons-cli/commons-cli/1.4/commons-cli-1.4.jar:/home/xonix/.m2/repository/org/apache/commons/commons-compress/1.18/commons-compress-1.18.jar" com.cmlteam.serv.Serv -v 0.1 real 0m0.040s user 0m0.030s sys 0m0.019s 

Comme vous pouvez le voir, deux fois plus lent que la version UPX.


En général, un temps de démarrage court est l'un des points forts de GraalVM. Ceci, ainsi que la faible consommation de mémoire, conduisent à un enthousiasme significatif autour de l'utilisation de cette technologie pour les microservices et sans serveur.


J'ai essayé de rendre la logique du programme aussi minimale que possible et d'utiliser un minimum de bibliothèques. En principe, cette approche est généralement justifiée, et dans ce cas, je craignais que l'ajout de dépendances Maven tierces «alourdit» considérablement le fichier de programme résultant.


Par exemple, c'est pourquoi je n'ai pas utilisé de dépendance tierce pour le serveur Web Java (et il y en a beaucoup pour tous les goûts et toutes les couleurs), mais j'ai utilisé l'implémentation JDK du serveur Web à partir du package com.sun.net.httpserver.* . En fait, l'utilisation du package com.sun.* Est considérée comme une com.sun.* , mais j'ai considéré cela comme autorisé dans ce cas, car je compile en code natif, et donc, il n'est pas question de compatibilité entre la JVM.


Cependant, mes craintes étaient complètement vaines. Dans le programme, j'ai utilisé deux dépendances pour plus de commodité


  1. commons-cli - pour analyser les arguments de la ligne de commande
  2. commons-compress - pour générer une archive tar de dossier et une compression gzip facultative

Dans le même temps, la taille du fichier a très légèrement augmenté. Je vais oser suggérer que le compilateur Graal est très intelligent de ne pas mettre tous les jar-nicks de plug-in dans le fichier exécutable, mais uniquement le code d'eux qui est réellement utilisé par le code de l'application.


La compilation en code Graal natif est effectuée par l'utilitaire d' image native . Il convient de mentionner que ce processus est gourmand en ressources. Disons que sur ma configuration pas si lente avec un processeur Intel 7700K à bord, ce processus prend 19 secondes. Par conséquent, je recommande que lors du développement, exécutez le programme comme d'habitude (via java) et récupérez le binaire au stade final.


Conclusions


L'expérience, il me semble, a été très réussie. Lors du développement à l'aide de la boîte à outils Graal, je n'ai rencontré aucun problème insurmontable ni même significatif. Tout fonctionnait de manière prévisible et stable. Bien que, presque certainement, tout ne sera pas si fluide si vous essayez de construire quelque chose de plus complexe de cette façon, par exemple, une application sur Spring Boot . Néanmoins, un certain nombre de plates-formes ont déjà été présentées dans lesquelles le support natif de Graal est déclaré. Parmi eux, Micronaut , Microprofile , Quarkus .


Quant à la poursuite du développement du projet, une liste d'améliorations prévues pour la version 0.2 est déjà prête. De plus, pour le moment, l'assemblage du binaire final n'est implémenté que pour Linux x64. J'espère que cette omission sera corrigée à l'avenir, d'autant plus que le compilateur d' native-image de Graal prend en charge MacOS et Windows. Malheureusement, il ne prend pas encore en charge la compilation croisée, ce qui pourrait faciliter les choses.


J'espère que l'utilitaire présenté sera utile à au moins quelqu'un de la communauté habr réputée. Je serai doublement heureux s'il y a ceux qui veulent contribuer au projet .

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


All Articles